Sophon Testnet
    /

    Contract Diff Checker

    Contract Name:
    SophonBadgeNFT

    Contract Source Code:

    <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.28;
    
    import "@openzeppelin/contracts/utils/Strings.sol";
    import "@ethereum-attestation-service/resolver/SchemaResolver.sol";
    import "@erc721a/IERC721A.sol";
    import "./SophonBadgeNFTState.sol";
    import "../../proxies/UpgradeableAccessControl.sol";
    import "../../common/Rescuable.sol";
    
    contract SophonBadgeNFT is UpgradeableAccessControl, SophonBadgeNFTState, Rescuable, SchemaResolver {
        using Strings for uint256;
    
        event BadgeClaimed(address indexed user, uint256 badgeType, uint256 level, uint256 tokenId);
        event BadgeAdded(uint256 badgeType, uint256 level);
        event BaseURIUpdated(string newBaseURI);
    
        error BadgeNotActive();
        error BadgeAlreadyClaimed();
        error TokenDoesNotExist();
        error BadgeNotTransferable();
    
        constructor(address easContractAddress)
            UpgradeableAccessControl(msg.sender)
            ERC721A("", "")
            SchemaResolver(IEAS(easContractAddress))
        {}
    
        function addBadge(uint256 badgeType, uint256 level, bool active) external onlyRole(DEFAULT_ADMIN_ROLE) {
            badgeInfos[badgeType][level] = BadgeInfo({badgeType: badgeType, level: level, active: active});
    
            emit BadgeAdded(badgeType, level);
        }
    
        function setBaseURI(string calldata baseURI) external onlyRole(DEFAULT_ADMIN_ROLE) {
            baseTokenURI = baseURI;
            emit BaseURIUpdated(baseURI);
        }
    
        function onAttest(Attestation calldata attestation, uint256 /* value */ ) internal override returns (bool) {
            _checkRole(ATTESTOR_ROLE, attestation.attester);
    
            (uint256 badgeType, uint256 level) = abi.decode(attestation.data, (uint256, uint256));
    
            if (!badgeInfos[badgeType][level].active) revert BadgeNotActive();
    
            address recipient = attestation.recipient;
            if (claimed[recipient][badgeType][level]) revert BadgeAlreadyClaimed();
    
            claimed[recipient][badgeType][level] = true;
    
            // Get the next token ID using the function, not as a variable
            uint256 tokenId = _nextTokenId();
    
            _safeMint(recipient, 1); // This automatically increments _currentIndex
    
            tokenToBadgeInfo[tokenId] = BadgeInfo({badgeType: badgeType, level: level, active: true});
    
            userBadgeTokenIds[recipient][badgeType][level] = tokenId;
    
            emit BadgeClaimed(recipient, badgeType, level, tokenId);
    
            return true;
        }
    
        function onRevoke(Attestation calldata, /* attestation */ uint256 /* value */ )
            internal
            pure
            override
            returns (bool)
        {
            return false;
        }
    
        function name() public pure override(ERC721A, IERC721A) returns (string memory) {
            return "Sophon+ Badges";
        }
    
        function symbol() public pure override(ERC721A, IERC721A) returns (string memory) {
            return unicode"SOPH+ 🏆";
        }
    
        function tokenURI(uint256 tokenId) public view override(ERC721A, IERC721A) returns (string memory) {
            if (!_exists(tokenId)) revert TokenDoesNotExist();
    
            BadgeInfo memory info = tokenToBadgeInfo[tokenId];
    
            return string(
                abi.encodePacked(
                    baseTokenURI, "badge-type-", info.badgeType.toString(), "-level-", info.level.toString(), ".json"
                )
            );
        }
    
        function eas() external view returns (IEAS) {
            return _eas;
        }
    
        function isPayable() public pure override returns (bool) {
            return false;
        }
    
        function canClaim(address user, uint256 badgeType, uint256 level) external view returns (bool) {
            return badgeInfos[badgeType][level].active && !claimed[user][badgeType][level];
        }
    
        /**
         * @dev Override approve to disable approvals
         */
        function approve(address, uint256) public payable override(ERC721A, IERC721A) {
            revert BadgeNotTransferable();
        }
    
        /**
         * @dev Override setApprovalForAll to disable approvals
         */
        function setApprovalForAll(address, bool) public pure override(ERC721A, IERC721A) {
            revert BadgeNotTransferable();
        }
    
        function _beforeTokenTransfers(address from, address to, uint256 startTokenId, uint256 quantity)
            internal
            override
        {
            if (from != address(0)) {
                revert BadgeNotTransferable();
            }
    
            super._beforeTokenTransfers(from, to, startTokenId, quantity);
        }
    
        function _requireRescuerRole() internal view override onlyRole(DEFAULT_ADMIN_ROLE) {
            // Empty function body
        }
    
        // Override supportsInterface with all parent contracts
        function supportsInterface(bytes4 interfaceId)
            public
            view
            virtual
            override(ERC721A, IERC721A, UpgradeableAccessControl)
            returns (bool)
        {
            return ERC721A.supportsInterface(interfaceId) || AccessControlDefaultAdminRules.supportsInterface(interfaceId);
        }
    }

    <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (utils/Strings.sol)
    
    pragma solidity ^0.8.20;
    
    import {Math} from "./math/Math.sol";
    import {SignedMath} from "./math/SignedMath.sol";
    
    /**
     * @dev String operations.
     */
    library Strings {
        bytes16 private constant HEX_DIGITS = "0123456789abcdef";
        uint8 private constant ADDRESS_LENGTH = 20;
    
        /**
         * @dev The `value` string doesn't fit in the specified `length`.
         */
        error StringsInsufficientHexLength(uint256 value, uint256 length);
    
        /**
         * @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), HEX_DIGITS))
                    }
                    value /= 10;
                    if (value == 0) break;
                }
                return buffer;
            }
        }
    
        /**
         * @dev Converts a `int256` to its ASCII `string` decimal representation.
         */
        function toStringSigned(int256 value) internal pure returns (string memory) {
            return string.concat(value < 0 ? "-" : "", toString(SignedMath.abs(value)));
        }
    
        /**
         * @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) {
            uint256 localValue = value;
            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] = HEX_DIGITS[localValue & 0xf];
                localValue >>= 4;
            }
            if (localValue != 0) {
                revert StringsInsufficientHexLength(value, length);
            }
            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);
        }
    
        /**
         * @dev Returns true if the two strings are equal.
         */
        function equal(string memory a, string memory b) internal pure returns (bool) {
            return bytes(a).length == bytes(b).length && keccak256(bytes(a)) == keccak256(bytes(b));
        }
    }

    <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

    // SPDX-License-Identifier: MIT
    
    pragma solidity ^0.8.4;
    
    import { AccessDenied, InvalidEAS, InvalidLength } from "./../Common.sol";
    import { IEAS, Attestation } from "./../IEAS.sol";
    import { Semver } from "./../Semver.sol";
    import { ISchemaResolver } from "./ISchemaResolver.sol";
    
    /// @title SchemaResolver
    /// @notice The base schema resolver contract.
    abstract contract SchemaResolver is ISchemaResolver, Semver {
        error InsufficientValue();
        error NotPayable();
    
        // The global EAS contract.
        IEAS internal immutable _eas;
    
        /// @dev Creates a new resolver.
        /// @param eas The address of the global EAS contract.
        constructor(IEAS eas) Semver(1, 4, 0) {
            if (address(eas) == address(0)) {
                revert InvalidEAS();
            }
    
            _eas = eas;
        }
    
        /// @dev Ensures that only the EAS contract can make this call.
        modifier onlyEAS() {
            _onlyEAS();
    
            _;
        }
    
        /// @inheritdoc ISchemaResolver
        function isPayable() public pure virtual returns (bool) {
            return false;
        }
    
        /// @dev ETH callback.
        receive() external payable virtual {
            if (!isPayable()) {
                revert NotPayable();
            }
        }
    
        /// @inheritdoc ISchemaResolver
        function attest(Attestation calldata attestation) external payable onlyEAS returns (bool) {
            return onAttest(attestation, msg.value);
        }
    
        /// @inheritdoc ISchemaResolver
        function multiAttest(
            Attestation[] calldata attestations,
            uint256[] calldata values
        ) external payable onlyEAS returns (bool) {
            uint256 length = attestations.length;
            if (length != values.length) {
                revert InvalidLength();
            }
    
            // We are keeping track of the remaining ETH amount that can be sent to resolvers and will keep deducting
            // from it to verify that there isn't any attempt to send too much ETH to resolvers. Please note that unless
            // some ETH was stuck in the contract by accident (which shouldn't happen in normal conditions), it won't be
            // possible to send too much ETH anyway.
            uint256 remainingValue = msg.value;
    
            for (uint256 i = 0; i < length; ++i) {
                // Ensure that the attester/revoker doesn't try to spend more than available.
                uint256 value = values[i];
                if (value > remainingValue) {
                    revert InsufficientValue();
                }
    
                // Forward the attestation to the underlying resolver and return false in case it isn't approved.
                if (!onAttest(attestations[i], value)) {
                    return false;
                }
    
                unchecked {
                    // Subtract the ETH amount, that was provided to this attestation, from the global remaining ETH amount.
                    remainingValue -= value;
                }
            }
    
            return true;
        }
    
        /// @inheritdoc ISchemaResolver
        function revoke(Attestation calldata attestation) external payable onlyEAS returns (bool) {
            return onRevoke(attestation, msg.value);
        }
    
        /// @inheritdoc ISchemaResolver
        function multiRevoke(
            Attestation[] calldata attestations,
            uint256[] calldata values
        ) external payable onlyEAS returns (bool) {
            uint256 length = attestations.length;
            if (length != values.length) {
                revert InvalidLength();
            }
    
            // We are keeping track of the remaining ETH amount that can be sent to resolvers and will keep deducting
            // from it to verify that there isn't any attempt to send too much ETH to resolvers. Please note that unless
            // some ETH was stuck in the contract by accident (which shouldn't happen in normal conditions), it won't be
            // possible to send too much ETH anyway.
            uint256 remainingValue = msg.value;
    
            for (uint256 i = 0; i < length; ++i) {
                // Ensure that the attester/revoker doesn't try to spend more than available.
                uint256 value = values[i];
                if (value > remainingValue) {
                    revert InsufficientValue();
                }
    
                // Forward the revocation to the underlying resolver and return false in case it isn't approved.
                if (!onRevoke(attestations[i], value)) {
                    return false;
                }
    
                unchecked {
                    // Subtract the ETH amount, that was provided to this attestation, from the global remaining ETH amount.
                    remainingValue -= value;
                }
            }
    
            return true;
        }
    
        /// @notice A resolver callback that should be implemented by child contracts.
        /// @param attestation The new attestation.
        /// @param value An explicit ETH amount that was sent to the resolver. Please note that this value is verified in
        ///     both attest() and multiAttest() callbacks EAS-only callbacks and that in case of multi attestations, it'll
        ///     usually hold that msg.value != value, since msg.value aggregated the sent ETH amounts for all the
        ///     attestations in the batch.
        /// @return Whether the attestation is valid.
        function onAttest(Attestation calldata attestation, uint256 value) internal virtual returns (bool);
    
        /// @notice Processes an attestation revocation and verifies if it can be revoked.
        /// @param attestation The existing attestation to be revoked.
        /// @param value An explicit ETH amount that was sent to the resolver. Please note that this value is verified in
        ///     both revoke() and multiRevoke() callbacks EAS-only callbacks and that in case of multi attestations, it'll
        ///     usually hold that msg.value != value, since msg.value aggregated the sent ETH amounts for all the
        ///     attestations in the batch.
        /// @return Whether the attestation can be revoked.
        function onRevoke(Attestation calldata attestation, uint256 value) internal virtual returns (bool);
    
        /// @dev Ensures that only the EAS contract can make this call.
        function _onlyEAS() private view {
            if (msg.sender != address(_eas)) {
                revert AccessDenied();
            }
        }
    }

    <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

    // SPDX-License-Identifier: MIT
    // ERC721A Contracts v4.3.0
    // 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();
    
        /**
         * `_sequentialUpTo()` must be greater than `_startTokenId()`.
         */
        error SequentialUpToTooSmall();
    
        /**
         * The `tokenId` of a sequential mint exceeds `_sequentialUpTo()`.
         */
        error SequentialMintExceedsLimit();
    
        /**
         * Spot minting requires a `tokenId` greater than `_sequentialUpTo()`.
         */
        error SpotMintTokenIdTooSmall();
    
        /**
         * Cannot mint over a token that already exists.
         */
        error TokenAlreadyExists();
    
        /**
         * The feature is not compatible with spot mints.
         */
        error NotCompatibleWithSpotMints();
    
        // =============================================================
        //                            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);
    }

    <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

    // SPDX-License-Identifier: GPL-3.0-only
    pragma solidity 0.8.28;
    
    import "@erc721a/extensions/ERC721AQueryable.sol";
    
    /**
     * @title SophonBadgeNFTState
     * @dev This abstract contract manages the state variables and mappings for the Sophon Badge NFT contract.
     * It extends ERC721A and ERC721AQueryable to support efficient minting and querying of NFTs.
     */
    abstract contract SophonBadgeNFTState is ERC721AQueryable {
        /// @notice The role identifier for the attestation issuer role
        bytes32 public constant ATTESTOR_ROLE = keccak256("ATTESTOR_ROLE");
    
        /// @notice Struct to store badge information
        struct BadgeInfo {
            uint256 badgeType;
            uint256 level;
            bool active;
        }
    
        /// @notice The base URI for the token metadata
        string public baseTokenURI;
    
        /// @notice Mapping of badge type to level to badge info
        mapping(uint256 => mapping(uint256 => BadgeInfo)) public badgeInfos;
    
        /// @notice Mapping of token ID to badge info
        mapping(uint256 => BadgeInfo) public tokenToBadgeInfo;
    
        /// @notice Mapping of user address to badge type to level to boolean to track claimed badges
        mapping(address => mapping(uint256 => mapping(uint256 => bool))) public claimed;
    
        /// @notice Mapping of user address to badge type to level to token ID
        mapping(address => mapping(uint256 => mapping(uint256 => uint256))) public userBadgeTokenIds;
    }

    <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

    // SPDX-License-Identifier: GPL-3.0-only
    pragma solidity 0.8.28;
    
    import "@openzeppelin/contracts/access/extensions/AccessControlDefaultAdminRules.sol";
    import "../common/Initializable.sol";
    
    /**
     * @title UpgradeableAccessControl
     * @notice This contract extends AccessControlDefaultAdminRules to provide role-based access control with an upgradeable implementation.
     * @dev Allows the default admin to replace the implementation address with a new one and optionally initialize it. The admin role changes are subject to a delay defined in the constructor.
     */
    contract UpgradeableAccessControl is AccessControlDefaultAdminRules, Initializable {
        /// @notice The slot containing the address of the current implementation contract.
        bytes32 public constant IMPLEMENTATION_SLOT = keccak256("IMPLEMENTATION_SLOT");
    
        /**
         * @notice Constructs the UpgradeableAccessControl contract.
         * @dev Initializes the AccessControlDefaultAdminRules with a delay of 3 days and sets the initial default admin.
         */
        constructor(address initialOwner) AccessControlDefaultAdminRules(3 days, initialOwner) {}
    
        /**
         * @notice Replaces the current implementation with a new one and optionally initializes it.
         * @dev Can only be called by an account with the DEFAULT_ADMIN_ROLE. If `initData_` is provided, a delegatecall is made to the new implementation with that data.
         * @param impl_ The address of the new implementation contract.
         * @param initData_ Optional initialization data to delegatecall to the new implementation.
         */
        function replaceImplementation(address impl_, bytes memory initData_) public onlyRole(DEFAULT_ADMIN_ROLE) {
            _replaceImplementation(impl_, initData_);
        }
    
        /**
         * @notice Internal function that replaces the current implementation with a new one and optionally initializes it.
         * @dev Can only be called by an account with the DEFAULT_ADMIN_ROLE. If `initData_` is provided, a delegatecall is made to the new implementation with that data.
         * @param impl_ The address of the new implementation contract.
         * @param initData_ Optional initialization data to delegatecall to the new implementation.
         */
        function _replaceImplementation(address impl_, bytes memory initData_) internal {
            require(impl_ != address(0), "impl_ is zero address");
            bytes32 slot = IMPLEMENTATION_SLOT;
            assembly {
                sstore(slot, impl_)
            }
            if (initData_.length != 0) {
                (bool success,) = impl_.delegatecall(initData_);
                require(success, "init failed");
            }
        }
    
        /**
         * @notice Checks if the contract implements an interface.
         * @dev Overrides supportsInterface from AccessControlDefaultAdminRules.
         * @param interfaceId The interface identifier, as specified in ERC-165.
         * @return True if the contract implements `interfaceId`, false otherwise.
         */
        function supportsInterface(bytes4 interfaceId)
            public
            view
            virtual
            override(AccessControlDefaultAdminRules)
            returns (bool)
        {
            return super.supportsInterface(interfaceId);
        }
    
        /**
         * @notice Returns the current implementation address
         * @return The current implementation address
         */
        function implementation() public view returns (address) {
            address implementation_;
            bytes32 slot = IMPLEMENTATION_SLOT;
            assembly {
                implementation_ := sload(slot)
            }
            return implementation_;
        }
    }

    <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

    // SPDX-License-Identifier: MIT
    
    pragma solidity 0.8.28;
    
    import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
    
    abstract contract Rescuable {
        using SafeERC20 for IERC20;
    
        /**
         * @notice Override this function in inheriting contracts to set appropriate permissions
         */
        function _requireRescuerRole() internal view virtual;
    
        /**
         * @notice Allows the rescue of ERC20 tokens held by the contract
         * @param token The ERC20 token to be rescued
         */
        function rescue(IERC20 token) external {
            _requireRescuerRole();
            uint256 balance = token.balanceOf(address(this));
            token.safeTransfer(msg.sender, balance);
        }
    
        /**
         * @notice Allows the rescue of Ether held by the contract
         */
        function rescueEth() external {
            _requireRescuerRole();
            uint256 balance = address(this).balance;
            (bool success,) = msg.sender.call{value: balance}("");
            require(success, "Transfer failed");
        }
    }

    <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol)
    
    pragma solidity ^0.8.20;
    
    /**
     * @dev Standard math utilities missing in the Solidity language.
     */
    library Math {
        /**
         * @dev Muldiv operation overflow.
         */
        error MathOverflowedMulDiv();
    
        enum Rounding {
            Floor, // Toward negative infinity
            Ceil, // Toward positive infinity
            Trunc, // Toward zero
            Expand // Away from zero
        }
    
        /**
         * @dev Returns the addition of two unsigned integers, with an overflow flag.
         */
        function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
            unchecked {
                uint256 c = a + b;
                if (c < a) return (false, 0);
                return (true, c);
            }
        }
    
        /**
         * @dev Returns the subtraction of two unsigned integers, with an overflow flag.
         */
        function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
            unchecked {
                if (b > a) return (false, 0);
                return (true, a - b);
            }
        }
    
        /**
         * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
         */
        function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
            unchecked {
                // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
                // benefit is lost if 'b' is also tested.
                // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
                if (a == 0) return (true, 0);
                uint256 c = a * b;
                if (c / a != b) return (false, 0);
                return (true, c);
            }
        }
    
        /**
         * @dev Returns the division of two unsigned integers, with a division by zero flag.
         */
        function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
            unchecked {
                if (b == 0) return (false, 0);
                return (true, a / b);
            }
        }
    
        /**
         * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
         */
        function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
            unchecked {
                if (b == 0) return (false, 0);
                return (true, a % b);
            }
        }
    
        /**
         * @dev Returns the largest of two numbers.
         */
        function max(uint256 a, uint256 b) internal pure returns (uint256) {
            return a > b ? a : b;
        }
    
        /**
         * @dev Returns the smallest of two numbers.
         */
        function min(uint256 a, uint256 b) internal pure returns (uint256) {
            return a < b ? a : b;
        }
    
        /**
         * @dev Returns the average of two numbers. The result is rounded towards
         * zero.
         */
        function average(uint256 a, uint256 b) internal pure returns (uint256) {
            // (a + b) / 2 can overflow.
            return (a & b) + (a ^ b) / 2;
        }
    
        /**
         * @dev Returns the ceiling of the division of two numbers.
         *
         * This differs from standard division with `/` in that it rounds towards infinity instead
         * of rounding towards zero.
         */
        function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
            if (b == 0) {
                // Guarantee the same behavior as in a regular Solidity division.
                return a / b;
            }
    
            // (a + b - 1) / b can overflow on addition, so we distribute.
            return a == 0 ? 0 : (a - 1) / b + 1;
        }
    
        /**
         * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
         * denominator == 0.
         * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by
         * Uniswap Labs also under MIT license.
         */
        function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
            unchecked {
                // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
                // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
                // variables such that product = prod1 * 2^256 + prod0.
                uint256 prod0 = x * y; // Least significant 256 bits of the product
                uint256 prod1; // Most significant 256 bits of the product
                assembly {
                    let mm := mulmod(x, y, not(0))
                    prod1 := sub(sub(mm, prod0), lt(mm, prod0))
                }
    
                // Handle non-overflow cases, 256 by 256 division.
                if (prod1 == 0) {
                    // Solidity will revert if denominator == 0, unlike the div opcode on its own.
                    // The surrounding unchecked block does not change this fact.
                    // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
                    return prod0 / denominator;
                }
    
                // Make sure the result is less than 2^256. Also prevents denominator == 0.
                if (denominator <= prod1) {
                    revert MathOverflowedMulDiv();
                }
    
                ///////////////////////////////////////////////
                // 512 by 256 division.
                ///////////////////////////////////////////////
    
                // Make division exact by subtracting the remainder from [prod1 prod0].
                uint256 remainder;
                assembly {
                    // Compute remainder using mulmod.
                    remainder := mulmod(x, y, denominator)
    
                    // Subtract 256 bit number from 512 bit number.
                    prod1 := sub(prod1, gt(remainder, prod0))
                    prod0 := sub(prod0, remainder)
                }
    
                // Factor powers of two out of denominator and compute largest power of two divisor of denominator.
                // Always >= 1. See https://cs.stackexchange.com/q/138556/92363.
    
                uint256 twos = denominator & (0 - denominator);
                assembly {
                    // Divide denominator by twos.
                    denominator := div(denominator, twos)
    
                    // Divide [prod1 prod0] by twos.
                    prod0 := div(prod0, twos)
    
                    // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                    twos := add(div(sub(0, twos), twos), 1)
                }
    
                // Shift in bits from prod1 into prod0.
                prod0 |= prod1 * twos;
    
                // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
                // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
                // four bits. That is, denominator * inv = 1 mod 2^4.
                uint256 inverse = (3 * denominator) ^ 2;
    
                // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also
                // works in modular arithmetic, doubling the correct bits in each step.
                inverse *= 2 - denominator * inverse; // inverse mod 2^8
                inverse *= 2 - denominator * inverse; // inverse mod 2^16
                inverse *= 2 - denominator * inverse; // inverse mod 2^32
                inverse *= 2 - denominator * inverse; // inverse mod 2^64
                inverse *= 2 - denominator * inverse; // inverse mod 2^128
                inverse *= 2 - denominator * inverse; // inverse mod 2^256
    
                // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
                // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
                // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
                // is no longer required.
                result = prod0 * inverse;
                return result;
            }
        }
    
        /**
         * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
         */
        function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
            uint256 result = mulDiv(x, y, denominator);
            if (unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0) {
                result += 1;
            }
            return result;
        }
    
        /**
         * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
         * towards zero.
         *
         * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
         */
        function sqrt(uint256 a) internal pure returns (uint256) {
            if (a == 0) {
                return 0;
            }
    
            // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
            //
            // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
            // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
            //
            // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
            // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
            // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
            //
            // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
            uint256 result = 1 << (log2(a) >> 1);
    
            // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
            // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
            // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
            // into the expected uint128 result.
            unchecked {
                result = (result + a / result) >> 1;
                result = (result + a / result) >> 1;
                result = (result + a / result) >> 1;
                result = (result + a / result) >> 1;
                result = (result + a / result) >> 1;
                result = (result + a / result) >> 1;
                result = (result + a / result) >> 1;
                return min(result, a / result);
            }
        }
    
        /**
         * @notice Calculates sqrt(a), following the selected rounding direction.
         */
        function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
            unchecked {
                uint256 result = sqrt(a);
                return result + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0);
            }
        }
    
        /**
         * @dev Return the log in base 2 of a positive value rounded towards zero.
         * Returns 0 if given 0.
         */
        function log2(uint256 value) internal pure returns (uint256) {
            uint256 result = 0;
            unchecked {
                if (value >> 128 > 0) {
                    value >>= 128;
                    result += 128;
                }
                if (value >> 64 > 0) {
                    value >>= 64;
                    result += 64;
                }
                if (value >> 32 > 0) {
                    value >>= 32;
                    result += 32;
                }
                if (value >> 16 > 0) {
                    value >>= 16;
                    result += 16;
                }
                if (value >> 8 > 0) {
                    value >>= 8;
                    result += 8;
                }
                if (value >> 4 > 0) {
                    value >>= 4;
                    result += 4;
                }
                if (value >> 2 > 0) {
                    value >>= 2;
                    result += 2;
                }
                if (value >> 1 > 0) {
                    result += 1;
                }
            }
            return result;
        }
    
        /**
         * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
         * Returns 0 if given 0.
         */
        function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
            unchecked {
                uint256 result = log2(value);
                return result + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0);
            }
        }
    
        /**
         * @dev Return the log in base 10 of a positive value rounded towards zero.
         * Returns 0 if given 0.
         */
        function log10(uint256 value) internal pure returns (uint256) {
            uint256 result = 0;
            unchecked {
                if (value >= 10 ** 64) {
                    value /= 10 ** 64;
                    result += 64;
                }
                if (value >= 10 ** 32) {
                    value /= 10 ** 32;
                    result += 32;
                }
                if (value >= 10 ** 16) {
                    value /= 10 ** 16;
                    result += 16;
                }
                if (value >= 10 ** 8) {
                    value /= 10 ** 8;
                    result += 8;
                }
                if (value >= 10 ** 4) {
                    value /= 10 ** 4;
                    result += 4;
                }
                if (value >= 10 ** 2) {
                    value /= 10 ** 2;
                    result += 2;
                }
                if (value >= 10 ** 1) {
                    result += 1;
                }
            }
            return result;
        }
    
        /**
         * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
         * Returns 0 if given 0.
         */
        function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
            unchecked {
                uint256 result = log10(value);
                return result + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0);
            }
        }
    
        /**
         * @dev Return the log in base 256 of a positive value rounded towards zero.
         * Returns 0 if given 0.
         *
         * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
         */
        function log256(uint256 value) internal pure returns (uint256) {
            uint256 result = 0;
            unchecked {
                if (value >> 128 > 0) {
                    value >>= 128;
                    result += 16;
                }
                if (value >> 64 > 0) {
                    value >>= 64;
                    result += 8;
                }
                if (value >> 32 > 0) {
                    value >>= 32;
                    result += 4;
                }
                if (value >> 16 > 0) {
                    value >>= 16;
                    result += 2;
                }
                if (value >> 8 > 0) {
                    result += 1;
                }
            }
            return result;
        }
    
        /**
         * @dev Return the log in base 256, following the selected rounding direction, of a positive value.
         * Returns 0 if given 0.
         */
        function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
            unchecked {
                uint256 result = log256(value);
                return result + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0);
            }
        }
    
        /**
         * @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
         */
        function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
            return uint8(rounding) % 2 == 1;
        }
    }

    <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (utils/math/SignedMath.sol)
    
    pragma solidity ^0.8.20;
    
    /**
     * @dev Standard signed math utilities missing in the Solidity language.
     */
    library SignedMath {
        /**
         * @dev Returns the largest of two signed numbers.
         */
        function max(int256 a, int256 b) internal pure returns (int256) {
            return a > b ? a : b;
        }
    
        /**
         * @dev Returns the smallest of two signed numbers.
         */
        function min(int256 a, int256 b) internal pure returns (int256) {
            return a < b ? a : b;
        }
    
        /**
         * @dev Returns the average of two signed numbers without overflow.
         * The result is rounded towards zero.
         */
        function average(int256 a, int256 b) internal pure returns (int256) {
            // Formula from the book "Hacker's Delight"
            int256 x = (a & b) + ((a ^ b) >> 1);
            return x + (int256(uint256(x) >> 255) & (a ^ b));
        }
    
        /**
         * @dev Returns the absolute unsigned value of a signed value.
         */
        function abs(int256 n) internal pure returns (uint256) {
            unchecked {
                // must be unchecked in order to support `n = type(int256).min`
                return uint256(n >= 0 ? n : -n);
            }
        }
    }

    <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

    // SPDX-License-Identifier: MIT
    
    pragma solidity ^0.8.0;
    
    // A representation of an empty/uninitialized UID.
    bytes32 constant EMPTY_UID = 0;
    
    // A zero expiration represents an non-expiring attestation.
    uint64 constant NO_EXPIRATION_TIME = 0;
    
    error AccessDenied();
    error DeadlineExpired();
    error InvalidEAS();
    error InvalidLength();
    error InvalidSignature();
    error NotFound();
    
    /// @notice A struct representing ECDSA signature data.
    struct Signature {
        uint8 v; // The recovery ID.
        bytes32 r; // The x-coordinate of the nonce R.
        bytes32 s; // The signature data.
    }
    
    /// @notice A struct representing a single attestation.
    struct Attestation {
        bytes32 uid; // A unique identifier of the attestation.
        bytes32 schema; // The unique identifier of the schema.
        uint64 time; // The time when the attestation was created (Unix timestamp).
        uint64 expirationTime; // The time when the attestation expires (Unix timestamp).
        uint64 revocationTime; // The time when the attestation was revoked (Unix timestamp).
        bytes32 refUID; // The UID of the related attestation.
        address recipient; // The recipient of the attestation.
        address attester; // The attester/sender of the attestation.
        bool revocable; // Whether the attestation is revocable.
        bytes data; // Custom attestation data.
    }

    <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

    // SPDX-License-Identifier: MIT
    
    pragma solidity ^0.8.0;
    
    import { ISchemaRegistry } from "./ISchemaRegistry.sol";
    import { ISemver } from "./ISemver.sol";
    import { Attestation, Signature } from "./Common.sol";
    
    /// @notice A struct representing the arguments of the attestation request.
    struct AttestationRequestData {
        address recipient; // The recipient of the attestation.
        uint64 expirationTime; // The time when the attestation expires (Unix timestamp).
        bool revocable; // Whether the attestation is revocable.
        bytes32 refUID; // The UID of the related attestation.
        bytes data; // Custom attestation data.
        uint256 value; // An explicit ETH amount to send to the resolver. This is important to prevent accidental user errors.
    }
    
    /// @notice A struct representing the full arguments of the attestation request.
    struct AttestationRequest {
        bytes32 schema; // The unique identifier of the schema.
        AttestationRequestData data; // The arguments of the attestation request.
    }
    
    /// @notice A struct representing the full arguments of the full delegated attestation request.
    struct DelegatedAttestationRequest {
        bytes32 schema; // The unique identifier of the schema.
        AttestationRequestData data; // The arguments of the attestation request.
        Signature signature; // The ECDSA signature data.
        address attester; // The attesting account.
        uint64 deadline; // The deadline of the signature/request.
    }
    
    /// @notice A struct representing the full arguments of the multi attestation request.
    struct MultiAttestationRequest {
        bytes32 schema; // The unique identifier of the schema.
        AttestationRequestData[] data; // The arguments of the attestation request.
    }
    
    /// @notice A struct representing the full arguments of the delegated multi attestation request.
    struct MultiDelegatedAttestationRequest {
        bytes32 schema; // The unique identifier of the schema.
        AttestationRequestData[] data; // The arguments of the attestation requests.
        Signature[] signatures; // The ECDSA signatures data. Please note that the signatures are assumed to be signed with increasing nonces.
        address attester; // The attesting account.
        uint64 deadline; // The deadline of the signature/request.
    }
    
    /// @notice A struct representing the arguments of the revocation request.
    struct RevocationRequestData {
        bytes32 uid; // The UID of the attestation to revoke.
        uint256 value; // An explicit ETH amount to send to the resolver. This is important to prevent accidental user errors.
    }
    
    /// @notice A struct representing the full arguments of the revocation request.
    struct RevocationRequest {
        bytes32 schema; // The unique identifier of the schema.
        RevocationRequestData data; // The arguments of the revocation request.
    }
    
    /// @notice A struct representing the arguments of the full delegated revocation request.
    struct DelegatedRevocationRequest {
        bytes32 schema; // The unique identifier of the schema.
        RevocationRequestData data; // The arguments of the revocation request.
        Signature signature; // The ECDSA signature data.
        address revoker; // The revoking account.
        uint64 deadline; // The deadline of the signature/request.
    }
    
    /// @notice A struct representing the full arguments of the multi revocation request.
    struct MultiRevocationRequest {
        bytes32 schema; // The unique identifier of the schema.
        RevocationRequestData[] data; // The arguments of the revocation request.
    }
    
    /// @notice A struct representing the full arguments of the delegated multi revocation request.
    struct MultiDelegatedRevocationRequest {
        bytes32 schema; // The unique identifier of the schema.
        RevocationRequestData[] data; // The arguments of the revocation requests.
        Signature[] signatures; // The ECDSA signatures data. Please note that the signatures are assumed to be signed with increasing nonces.
        address revoker; // The revoking account.
        uint64 deadline; // The deadline of the signature/request.
    }
    
    /// @title IEAS
    /// @notice EAS - Ethereum Attestation Service interface.
    interface IEAS is ISemver {
        /// @notice Emitted when an attestation has been made.
        /// @param recipient The recipient of the attestation.
        /// @param attester The attesting account.
        /// @param uid The UID of the new attestation.
        /// @param schemaUID The UID of the schema.
        event Attested(address indexed recipient, address indexed attester, bytes32 uid, bytes32 indexed schemaUID);
    
        /// @notice Emitted when an attestation has been revoked.
        /// @param recipient The recipient of the attestation.
        /// @param attester The attesting account.
        /// @param schemaUID The UID of the schema.
        /// @param uid The UID the revoked attestation.
        event Revoked(address indexed recipient, address indexed attester, bytes32 uid, bytes32 indexed schemaUID);
    
        /// @notice Emitted when a data has been timestamped.
        /// @param data The data.
        /// @param timestamp The timestamp.
        event Timestamped(bytes32 indexed data, uint64 indexed timestamp);
    
        /// @notice Emitted when a data has been revoked.
        /// @param revoker The address of the revoker.
        /// @param data The data.
        /// @param timestamp The timestamp.
        event RevokedOffchain(address indexed revoker, bytes32 indexed data, uint64 indexed timestamp);
    
        /// @notice Returns the address of the global schema registry.
        /// @return The address of the global schema registry.
        function getSchemaRegistry() external view returns (ISchemaRegistry);
    
        /// @notice Attests to a specific schema.
        /// @param request The arguments of the attestation request.
        /// @return The UID of the new attestation.
        ///
        /// Example:
        ///     attest({
        ///         schema: "0facc36681cbe2456019c1b0d1e7bedd6d1d40f6f324bf3dd3a4cef2999200a0",
        ///         data: {
        ///             recipient: "0xdEADBeAFdeAdbEafdeadbeafDeAdbEAFdeadbeaf",
        ///             expirationTime: 0,
        ///             revocable: true,
        ///             refUID: "0x0000000000000000000000000000000000000000000000000000000000000000",
        ///             data: "0xF00D",
        ///             value: 0
        ///         }
        ///     })
        function attest(AttestationRequest calldata request) external payable returns (bytes32);
    
        /// @notice Attests to a specific schema via the provided ECDSA signature.
        /// @param delegatedRequest The arguments of the delegated attestation request.
        /// @return The UID of the new attestation.
        ///
        /// Example:
        ///     attestByDelegation({
        ///         schema: '0x8e72f5bc0a8d4be6aa98360baa889040c50a0e51f32dbf0baa5199bd93472ebc',
        ///         data: {
        ///             recipient: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
        ///             expirationTime: 1673891048,
        ///             revocable: true,
        ///             refUID: '0x0000000000000000000000000000000000000000000000000000000000000000',
        ///             data: '0x1234',
        ///             value: 0
        ///         },
        ///         signature: {
        ///             v: 28,
        ///             r: '0x148c...b25b',
        ///             s: '0x5a72...be22'
        ///         },
        ///         attester: '0xc5E8740aD971409492b1A63Db8d83025e0Fc427e',
        ///         deadline: 1673891048
        ///     })
        function attestByDelegation(
            DelegatedAttestationRequest calldata delegatedRequest
        ) external payable returns (bytes32);
    
        /// @notice Attests to multiple schemas.
        /// @param multiRequests The arguments of the multi attestation requests. The requests should be grouped by distinct
        ///     schema ids to benefit from the best batching optimization.
        /// @return The UIDs of the new attestations.
        ///
        /// Example:
        ///     multiAttest([{
        ///         schema: '0x33e9094830a5cba5554d1954310e4fbed2ef5f859ec1404619adea4207f391fd',
        ///         data: [{
        ///             recipient: '0xdEADBeAFdeAdbEafdeadbeafDeAdbEAFdeadbeaf',
        ///             expirationTime: 1673891048,
        ///             revocable: true,
        ///             refUID: '0x0000000000000000000000000000000000000000000000000000000000000000',
        ///             data: '0x1234',
        ///             value: 1000
        ///         },
        ///         {
        ///             recipient: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
        ///             expirationTime: 0,
        ///             revocable: false,
        ///             refUID: '0x480df4a039efc31b11bfdf491b383ca138b6bde160988222a2a3509c02cee174',
        ///             data: '0x00',
        ///             value: 0
        ///         }],
        ///     },
        ///     {
        ///         schema: '0x5ac273ce41e3c8bfa383efe7c03e54c5f0bff29c9f11ef6ffa930fc84ca32425',
        ///         data: [{
        ///             recipient: '0xdEADBeAFdeAdbEafdeadbeafDeAdbEAFdeadbeaf',
        ///             expirationTime: 0,
        ///             revocable: true,
        ///             refUID: '0x75bf2ed8dca25a8190c50c52db136664de25b2449535839008ccfdab469b214f',
        ///             data: '0x12345678',
        ///             value: 0
        ///         },
        ///     }])
        function multiAttest(MultiAttestationRequest[] calldata multiRequests) external payable returns (bytes32[] memory);
    
        /// @notice Attests to multiple schemas using via provided ECDSA signatures.
        /// @param multiDelegatedRequests The arguments of the delegated multi attestation requests. The requests should be
        ///     grouped by distinct schema ids to benefit from the best batching optimization.
        /// @return The UIDs of the new attestations.
        ///
        /// Example:
        ///     multiAttestByDelegation([{
        ///         schema: '0x8e72f5bc0a8d4be6aa98360baa889040c50a0e51f32dbf0baa5199bd93472ebc',
        ///         data: [{
        ///             recipient: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
        ///             expirationTime: 1673891048,
        ///             revocable: true,
        ///             refUID: '0x0000000000000000000000000000000000000000000000000000000000000000',
        ///             data: '0x1234',
        ///             value: 0
        ///         },
        ///         {
        ///             recipient: '0xdEADBeAFdeAdbEafdeadbeafDeAdbEAFdeadbeaf',
        ///             expirationTime: 0,
        ///             revocable: false,
        ///             refUID: '0x0000000000000000000000000000000000000000000000000000000000000000',
        ///             data: '0x00',
        ///             value: 0
        ///         }],
        ///         signatures: [{
        ///             v: 28,
        ///             r: '0x148c...b25b',
        ///             s: '0x5a72...be22'
        ///         },
        ///         {
        ///             v: 28,
        ///             r: '0x487s...67bb',
        ///             s: '0x12ad...2366'
        ///         }],
        ///         attester: '0x1D86495b2A7B524D747d2839b3C645Bed32e8CF4',
        ///         deadline: 1673891048
        ///     }])
        function multiAttestByDelegation(
            MultiDelegatedAttestationRequest[] calldata multiDelegatedRequests
        ) external payable returns (bytes32[] memory);
    
        /// @notice Revokes an existing attestation to a specific schema.
        /// @param request The arguments of the revocation request.
        ///
        /// Example:
        ///     revoke({
        ///         schema: '0x8e72f5bc0a8d4be6aa98360baa889040c50a0e51f32dbf0baa5199bd93472ebc',
        ///         data: {
        ///             uid: '0x101032e487642ee04ee17049f99a70590c735b8614079fc9275f9dd57c00966d',
        ///             value: 0
        ///         }
        ///     })
        function revoke(RevocationRequest calldata request) external payable;
    
        /// @notice Revokes an existing attestation to a specific schema via the provided ECDSA signature.
        /// @param delegatedRequest The arguments of the delegated revocation request.
        ///
        /// Example:
        ///     revokeByDelegation({
        ///         schema: '0x8e72f5bc0a8d4be6aa98360baa889040c50a0e51f32dbf0baa5199bd93472ebc',
        ///         data: {
        ///             uid: '0xcbbc12102578c642a0f7b34fe7111e41afa25683b6cd7b5a14caf90fa14d24ba',
        ///             value: 0
        ///         },
        ///         signature: {
        ///             v: 27,
        ///             r: '0xb593...7142',
        ///             s: '0x0f5b...2cce'
        ///         },
        ///         revoker: '0x244934dd3e31bE2c81f84ECf0b3E6329F5381992',
        ///         deadline: 1673891048
        ///     })
        function revokeByDelegation(DelegatedRevocationRequest calldata delegatedRequest) external payable;
    
        /// @notice Revokes existing attestations to multiple schemas.
        /// @param multiRequests The arguments of the multi revocation requests. The requests should be grouped by distinct
        ///     schema ids to benefit from the best batching optimization.
        ///
        /// Example:
        ///     multiRevoke([{
        ///         schema: '0x8e72f5bc0a8d4be6aa98360baa889040c50a0e51f32dbf0baa5199bd93472ebc',
        ///         data: [{
        ///             uid: '0x211296a1ca0d7f9f2cfebf0daaa575bea9b20e968d81aef4e743d699c6ac4b25',
        ///             value: 1000
        ///         },
        ///         {
        ///             uid: '0xe160ac1bd3606a287b4d53d5d1d6da5895f65b4b4bab6d93aaf5046e48167ade',
        ///             value: 0
        ///         }],
        ///     },
        ///     {
        ///         schema: '0x5ac273ce41e3c8bfa383efe7c03e54c5f0bff29c9f11ef6ffa930fc84ca32425',
        ///         data: [{
        ///             uid: '0x053d42abce1fd7c8fcddfae21845ad34dae287b2c326220b03ba241bc5a8f019',
        ///             value: 0
        ///         },
        ///     }])
        function multiRevoke(MultiRevocationRequest[] calldata multiRequests) external payable;
    
        /// @notice Revokes existing attestations to multiple schemas via provided ECDSA signatures.
        /// @param multiDelegatedRequests The arguments of the delegated multi revocation attestation requests. The requests
        ///     should be grouped by distinct schema ids to benefit from the best batching optimization.
        ///
        /// Example:
        ///     multiRevokeByDelegation([{
        ///         schema: '0x8e72f5bc0a8d4be6aa98360baa889040c50a0e51f32dbf0baa5199bd93472ebc',
        ///         data: [{
        ///             uid: '0x211296a1ca0d7f9f2cfebf0daaa575bea9b20e968d81aef4e743d699c6ac4b25',
        ///             value: 1000
        ///         },
        ///         {
        ///             uid: '0xe160ac1bd3606a287b4d53d5d1d6da5895f65b4b4bab6d93aaf5046e48167ade',
        ///             value: 0
        ///         }],
        ///         signatures: [{
        ///             v: 28,
        ///             r: '0x148c...b25b',
        ///             s: '0x5a72...be22'
        ///         },
        ///         {
        ///             v: 28,
        ///             r: '0x487s...67bb',
        ///             s: '0x12ad...2366'
        ///         }],
        ///         revoker: '0x244934dd3e31bE2c81f84ECf0b3E6329F5381992',
        ///         deadline: 1673891048
        ///     }])
        function multiRevokeByDelegation(
            MultiDelegatedRevocationRequest[] calldata multiDelegatedRequests
        ) external payable;
    
        /// @notice Timestamps the specified bytes32 data.
        /// @param data The data to timestamp.
        /// @return The timestamp the data was timestamped with.
        function timestamp(bytes32 data) external returns (uint64);
    
        /// @notice Timestamps the specified multiple bytes32 data.
        /// @param data The data to timestamp.
        /// @return The timestamp the data was timestamped with.
        function multiTimestamp(bytes32[] calldata data) external returns (uint64);
    
        /// @notice Revokes the specified bytes32 data.
        /// @param data The data to timestamp.
        /// @return The timestamp the data was revoked with.
        function revokeOffchain(bytes32 data) external returns (uint64);
    
        /// @notice Revokes the specified multiple bytes32 data.
        /// @param data The data to timestamp.
        /// @return The timestamp the data was revoked with.
        function multiRevokeOffchain(bytes32[] calldata data) external returns (uint64);
    
        /// @notice Returns an existing attestation by UID.
        /// @param uid The UID of the attestation to retrieve.
        /// @return The attestation data members.
        function getAttestation(bytes32 uid) external view returns (Attestation memory);
    
        /// @notice Checks whether an attestation exists.
        /// @param uid The UID of the attestation to retrieve.
        /// @return Whether an attestation exists.
        function isAttestationValid(bytes32 uid) external view returns (bool);
    
        /// @notice Returns the timestamp that the specified data was timestamped with.
        /// @param data The data to query.
        /// @return The timestamp the data was timestamped with.
        function getTimestamp(bytes32 data) external view returns (uint64);
    
        /// @notice Returns the timestamp that the specified data was timestamped with.
        /// @param data The data to query.
        /// @return The timestamp the data was timestamped with.
        function getRevokeOffchain(address revoker, bytes32 data) external view returns (uint64);
    }

    <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

    // SPDX-License-Identifier: MIT
    
    pragma solidity ^0.8.4;
    
    import { Strings } from "@openzeppelin/contracts/utils/Strings.sol";
    
    import { ISemver } from "./ISemver.sol";
    
    /// @title Semver
    /// @notice A simple contract for managing contract versions.
    contract Semver is ISemver {
        // Contract's major version number.
        uint256 private immutable _major;
    
        // Contract's minor version number.
        uint256 private immutable _minor;
    
        // Contract's patch version number.
        uint256 private immutable _patch;
    
        /// @dev Create a new Semver instance.
        /// @param major Major version number.
        /// @param minor Minor version number.
        /// @param patch Patch version number.
        constructor(uint256 major, uint256 minor, uint256 patch) {
            _major = major;
            _minor = minor;
            _patch = patch;
        }
    
        /// @notice Returns the full semver contract version.
        /// @return Semver contract version as a string.
        function version() external view returns (string memory) {
            return
                string(
                    abi.encodePacked(Strings.toString(_major), ".", Strings.toString(_minor), ".", Strings.toString(_patch))
                );
        }
    }

    <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

    // SPDX-License-Identifier: MIT
    
    pragma solidity ^0.8.0;
    
    import { Attestation } from "./../Common.sol";
    import { ISemver } from "./../ISemver.sol";
    
    /// @title ISchemaResolver
    /// @notice The interface of an optional schema resolver.
    interface ISchemaResolver is ISemver {
        /// @notice Checks if the resolver can be sent ETH.
        /// @return Whether the resolver supports ETH transfers.
        function isPayable() external pure returns (bool);
    
        /// @notice Processes an attestation and verifies whether it's valid.
        /// @param attestation The new attestation.
        /// @return Whether the attestation is valid.
        function attest(Attestation calldata attestation) external payable returns (bool);
    
        /// @notice Processes multiple attestations and verifies whether they are valid.
        /// @param attestations The new attestations.
        /// @param values Explicit ETH amounts which were sent with each attestation.
        /// @return Whether all the attestations are valid.
        function multiAttest(
            Attestation[] calldata attestations,
            uint256[] calldata values
        ) external payable returns (bool);
    
        /// @notice Processes an attestation revocation and verifies if it can be revoked.
        /// @param attestation The existing attestation to be revoked.
        /// @return Whether the attestation can be revoked.
        function revoke(Attestation calldata attestation) external payable returns (bool);
    
        /// @notice Processes revocation of multiple attestation and verifies they can be revoked.
        /// @param attestations The existing attestations to be revoked.
        /// @param values Explicit ETH amounts which were sent with each revocation.
        /// @return Whether the attestations can be revoked.
        function multiRevoke(
            Attestation[] calldata attestations,
            uint256[] calldata values
        ) external payable returns (bool);
    }

    <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

    // SPDX-License-Identifier: MIT
    // ERC721A Contracts v4.3.0
    // Creator: Chiru Labs
    
    pragma solidity ^0.8.4;
    
    import './IERC721AQueryable.sol';
    import '../ERC721A.sol';
    
    /**
     * @title ERC721AQueryable.
     *
     * @dev ERC721A subclass with convenience query functions.
     */
    abstract contract ERC721AQueryable is ERC721A, IERC721AQueryable {
        /**
         * @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)
            public
            view
            virtual
            override
            returns (TokenOwnership memory ownership)
        {
            unchecked {
                if (tokenId >= _startTokenId()) {
                    if (tokenId > _sequentialUpTo()) return _ownershipAt(tokenId);
    
                    if (tokenId < _nextTokenId()) {
                        // If the `tokenId` is within bounds,
                        // scan backwards for the initialized ownership slot.
                        while (!_ownershipIsInitialized(tokenId)) --tokenId;
                        return _ownershipAt(tokenId);
                    }
                }
            }
        }
    
        /**
         * @dev Returns an array of `TokenOwnership` structs at `tokenIds` in order.
         * See {ERC721AQueryable-explicitOwnershipOf}
         */
        function explicitOwnershipsOf(uint256[] calldata tokenIds)
            external
            view
            virtual
            override
            returns (TokenOwnership[] memory)
        {
            TokenOwnership[] memory ownerships;
            uint256 i = tokenIds.length;
            assembly {
                // Grab the free memory pointer.
                ownerships := mload(0x40)
                // Store the length.
                mstore(ownerships, i)
                // Allocate one word for the length,
                // `tokenIds.length` words for the pointers.
                i := shl(5, i) // Multiply `i` by 32.
                mstore(0x40, add(add(ownerships, 0x20), i))
            }
            while (i != 0) {
                uint256 tokenId;
                assembly {
                    i := sub(i, 0x20)
                    tokenId := calldataload(add(tokenIds.offset, i))
                }
                TokenOwnership memory ownership = explicitOwnershipOf(tokenId);
                assembly {
                    // Store the pointer of `ownership` in the `ownerships` array.
                    mstore(add(add(ownerships, 0x20), i), ownership)
                }
            }
            return ownerships;
        }
    
        /**
         * @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 virtual override returns (uint256[] memory) {
            return _tokensOfOwnerIn(owner, start, stop);
        }
    
        /**
         * @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 virtual override returns (uint256[] memory) {
            // If spot mints are enabled, full-range scan is disabled.
            if (_sequentialUpTo() != type(uint256).max) _revert(NotCompatibleWithSpotMints.selector);
            uint256 start = _startTokenId();
            uint256 stop = _nextTokenId();
            uint256[] memory tokenIds;
            if (start != stop) tokenIds = _tokensOfOwnerIn(owner, start, stop);
            return tokenIds;
        }*/
    
        /**
         * @dev Helper function for returning an array of token IDs owned by `owner`.
         *
         * Note that this function is optimized for smaller bytecode size over runtime gas,
         * since it is meant to be called off-chain.
         */
        function _tokensOfOwnerIn(
            address owner,
            uint256 start,
            uint256 stop
        ) private view returns (uint256[] memory tokenIds) {
            unchecked {
                if (start >= stop) _revert(InvalidQueryRange.selector);
                // Set `start = max(start, _startTokenId())`.
                if (start < _startTokenId()) start = _startTokenId();
                uint256 nextTokenId = _nextTokenId();
                // If spot mints are enabled, scan all the way until the specified `stop`.
                uint256 stopLimit = _sequentialUpTo() != type(uint256).max ? stop : nextTokenId;
                // Set `stop = min(stop, stopLimit)`.
                if (stop >= stopLimit) stop = stopLimit;
                // Number of tokens to scan.
                uint256 tokenIdsMaxLength = balanceOf(owner);
                // Set `tokenIdsMaxLength` to zero if the range contains no tokens.
                if (start >= stop) tokenIdsMaxLength = 0;
                // If there are one or more tokens to scan.
                if (tokenIdsMaxLength != 0) {
                    // Set `tokenIdsMaxLength = min(balanceOf(owner), tokenIdsMaxLength)`.
                    if (stop - start <= tokenIdsMaxLength) tokenIdsMaxLength = stop - start;
                    uint256 m; // Start of available memory.
                    assembly {
                        // Grab the free memory pointer.
                        tokenIds := mload(0x40)
                        // Allocate one word for the length, and `tokenIdsMaxLength` words
                        // for the data. `shl(5, x)` is equivalent to `mul(32, x)`.
                        m := add(tokenIds, shl(5, add(tokenIdsMaxLength, 1)))
                        mstore(0x40, m)
                    }
                    // We need to call `explicitOwnershipOf(start)`,
                    // because the slot at `start` may not be initialized.
                    TokenOwnership memory ownership = explicitOwnershipOf(start);
                    address currOwnershipAddr;
                    // If the starting slot exists (i.e. not burned),
                    // initialize `currOwnershipAddr`.
                    // `ownership.address` will not be zero,
                    // as `start` is clamped to the valid token ID range.
                    if (!ownership.burned) currOwnershipAddr = ownership.addr;
                    uint256 tokenIdsIdx;
                    // Use a do-while, which is slightly more efficient for this case,
                    // as the array will at least contain one element.
                    do {
                        if (_sequentialUpTo() != type(uint256).max) {
                            // Skip the remaining unused sequential slots.
                            if (start == nextTokenId) start = _sequentialUpTo() + 1;
                            // Reset `currOwnershipAddr`, as each spot-minted token is a batch of one.
                            if (start > _sequentialUpTo()) currOwnershipAddr = address(0);
                        }
                        ownership = _ownershipAt(start); // This implicitly allocates memory.
                        assembly {
                            switch mload(add(ownership, 0x40))
                            // if `ownership.burned == false`.
                            case 0 {
                                // if `ownership.addr != address(0)`.
                                // The `addr` already has it's upper 96 bits clearned,
                                // since it is written to memory with regular Solidity.
                                if mload(ownership) {
                                    currOwnershipAddr := mload(ownership)
                                }
                                // if `currOwnershipAddr == owner`.
                                // The `shl(96, x)` is to make the comparison agnostic to any
                                // dirty upper 96 bits in `owner`.
                                if iszero(shl(96, xor(currOwnershipAddr, owner))) {
                                    tokenIdsIdx := add(tokenIdsIdx, 1)
                                    mstore(add(tokenIds, shl(5, tokenIdsIdx)), start)
                                }
                            }
                            // Otherwise, reset `currOwnershipAddr`.
                            // This handles the case of batch burned tokens
                            // (burned bit of first slot set, remaining slots left uninitialized).
                            default {
                                currOwnershipAddr := 0
                            }
                            start := add(start, 1)
                            // Free temporary memory implicitly allocated for ownership
                            // to avoid quadratic memory expansion costs.
                            mstore(0x40, m)
                        }
                    } while (!(start == stop || tokenIdsIdx == tokenIdsMaxLength));
                    // Store the length of the array.
                    assembly {
                        mstore(tokenIds, tokenIdsIdx)
                    }
                }
            }
        }
    }

    <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (access/extensions/AccessControlDefaultAdminRules.sol)
    
    pragma solidity ^0.8.20;
    
    import {IAccessControlDefaultAdminRules} from "./IAccessControlDefaultAdminRules.sol";
    import {AccessControl, IAccessControl} from "../AccessControl.sol";
    import {SafeCast} from "../../utils/math/SafeCast.sol";
    import {Math} from "../../utils/math/Math.sol";
    import {IERC5313} from "../../interfaces/IERC5313.sol";
    
    /**
     * @dev Extension of {AccessControl} that allows specifying special rules to manage
     * the `DEFAULT_ADMIN_ROLE` holder, which is a sensitive role with special permissions
     * over other roles that may potentially have privileged rights in the system.
     *
     * If a specific role doesn't have an admin role assigned, the holder of the
     * `DEFAULT_ADMIN_ROLE` will have the ability to grant it and revoke it.
     *
     * This contract implements the following risk mitigations on top of {AccessControl}:
     *
     * * Only one account holds the `DEFAULT_ADMIN_ROLE` since deployment until it's potentially renounced.
     * * Enforces a 2-step process to transfer the `DEFAULT_ADMIN_ROLE` to another account.
     * * Enforces a configurable delay between the two steps, with the ability to cancel before the transfer is accepted.
     * * The delay can be changed by scheduling, see {changeDefaultAdminDelay}.
     * * It is not possible to use another role to manage the `DEFAULT_ADMIN_ROLE`.
     *
     * Example usage:
     *
     * ```solidity
     * contract MyToken is AccessControlDefaultAdminRules {
     *   constructor() AccessControlDefaultAdminRules(
     *     3 days,
     *     msg.sender // Explicit initial `DEFAULT_ADMIN_ROLE` holder
     *    ) {}
     * }
     * ```
     */
    abstract contract AccessControlDefaultAdminRules is IAccessControlDefaultAdminRules, IERC5313, AccessControl {
        // pending admin pair read/written together frequently
        address private _pendingDefaultAdmin;
        uint48 private _pendingDefaultAdminSchedule; // 0 == unset
    
        uint48 private _currentDelay;
        address private _currentDefaultAdmin;
    
        // pending delay pair read/written together frequently
        uint48 private _pendingDelay;
        uint48 private _pendingDelaySchedule; // 0 == unset
    
        /**
         * @dev Sets the initial values for {defaultAdminDelay} and {defaultAdmin} address.
         */
        constructor(uint48 initialDelay, address initialDefaultAdmin) {
            if (initialDefaultAdmin == address(0)) {
                revert AccessControlInvalidDefaultAdmin(address(0));
            }
            _currentDelay = initialDelay;
            _grantRole(DEFAULT_ADMIN_ROLE, initialDefaultAdmin);
        }
    
        /**
         * @dev See {IERC165-supportsInterface}.
         */
        function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
            return interfaceId == type(IAccessControlDefaultAdminRules).interfaceId || super.supportsInterface(interfaceId);
        }
    
        /**
         * @dev See {IERC5313-owner}.
         */
        function owner() public view virtual returns (address) {
            return defaultAdmin();
        }
    
        ///
        /// Override AccessControl role management
        ///
    
        /**
         * @dev See {AccessControl-grantRole}. Reverts for `DEFAULT_ADMIN_ROLE`.
         */
        function grantRole(bytes32 role, address account) public virtual override(AccessControl, IAccessControl) {
            if (role == DEFAULT_ADMIN_ROLE) {
                revert AccessControlEnforcedDefaultAdminRules();
            }
            super.grantRole(role, account);
        }
    
        /**
         * @dev See {AccessControl-revokeRole}. Reverts for `DEFAULT_ADMIN_ROLE`.
         */
        function revokeRole(bytes32 role, address account) public virtual override(AccessControl, IAccessControl) {
            if (role == DEFAULT_ADMIN_ROLE) {
                revert AccessControlEnforcedDefaultAdminRules();
            }
            super.revokeRole(role, account);
        }
    
        /**
         * @dev See {AccessControl-renounceRole}.
         *
         * For the `DEFAULT_ADMIN_ROLE`, it only allows renouncing in two steps by first calling
         * {beginDefaultAdminTransfer} to the `address(0)`, so it's required that the {pendingDefaultAdmin} schedule
         * has also passed when calling this function.
         *
         * After its execution, it will not be possible to call `onlyRole(DEFAULT_ADMIN_ROLE)` functions.
         *
         * NOTE: Renouncing `DEFAULT_ADMIN_ROLE` will leave the contract without a {defaultAdmin},
         * thereby disabling any functionality that is only available for it, and the possibility of reassigning a
         * non-administrated role.
         */
        function renounceRole(bytes32 role, address account) public virtual override(AccessControl, IAccessControl) {
            if (role == DEFAULT_ADMIN_ROLE && account == defaultAdmin()) {
                (address newDefaultAdmin, uint48 schedule) = pendingDefaultAdmin();
                if (newDefaultAdmin != address(0) || !_isScheduleSet(schedule) || !_hasSchedulePassed(schedule)) {
                    revert AccessControlEnforcedDefaultAdminDelay(schedule);
                }
                delete _pendingDefaultAdminSchedule;
            }
            super.renounceRole(role, account);
        }
    
        /**
         * @dev See {AccessControl-_grantRole}.
         *
         * For `DEFAULT_ADMIN_ROLE`, it only allows granting if there isn't already a {defaultAdmin} or if the
         * role has been previously renounced.
         *
         * NOTE: Exposing this function through another mechanism may make the `DEFAULT_ADMIN_ROLE`
         * assignable again. Make sure to guarantee this is the expected behavior in your implementation.
         */
        function _grantRole(bytes32 role, address account) internal virtual override returns (bool) {
            if (role == DEFAULT_ADMIN_ROLE) {
                if (defaultAdmin() != address(0)) {
                    revert AccessControlEnforcedDefaultAdminRules();
                }
                _currentDefaultAdmin = account;
            }
            return super._grantRole(role, account);
        }
    
        /**
         * @dev See {AccessControl-_revokeRole}.
         */
        function _revokeRole(bytes32 role, address account) internal virtual override returns (bool) {
            if (role == DEFAULT_ADMIN_ROLE && account == defaultAdmin()) {
                delete _currentDefaultAdmin;
            }
            return super._revokeRole(role, account);
        }
    
        /**
         * @dev See {AccessControl-_setRoleAdmin}. Reverts for `DEFAULT_ADMIN_ROLE`.
         */
        function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual override {
            if (role == DEFAULT_ADMIN_ROLE) {
                revert AccessControlEnforcedDefaultAdminRules();
            }
            super._setRoleAdmin(role, adminRole);
        }
    
        ///
        /// AccessControlDefaultAdminRules accessors
        ///
    
        /**
         * @inheritdoc IAccessControlDefaultAdminRules
         */
        function defaultAdmin() public view virtual returns (address) {
            return _currentDefaultAdmin;
        }
    
        /**
         * @inheritdoc IAccessControlDefaultAdminRules
         */
        function pendingDefaultAdmin() public view virtual returns (address newAdmin, uint48 schedule) {
            return (_pendingDefaultAdmin, _pendingDefaultAdminSchedule);
        }
    
        /**
         * @inheritdoc IAccessControlDefaultAdminRules
         */
        function defaultAdminDelay() public view virtual returns (uint48) {
            uint48 schedule = _pendingDelaySchedule;
            return (_isScheduleSet(schedule) && _hasSchedulePassed(schedule)) ? _pendingDelay : _currentDelay;
        }
    
        /**
         * @inheritdoc IAccessControlDefaultAdminRules
         */
        function pendingDefaultAdminDelay() public view virtual returns (uint48 newDelay, uint48 schedule) {
            schedule = _pendingDelaySchedule;
            return (_isScheduleSet(schedule) && !_hasSchedulePassed(schedule)) ? (_pendingDelay, schedule) : (0, 0);
        }
    
        /**
         * @inheritdoc IAccessControlDefaultAdminRules
         */
        function defaultAdminDelayIncreaseWait() public view virtual returns (uint48) {
            return 5 days;
        }
    
        ///
        /// AccessControlDefaultAdminRules public and internal setters for defaultAdmin/pendingDefaultAdmin
        ///
    
        /**
         * @inheritdoc IAccessControlDefaultAdminRules
         */
        function beginDefaultAdminTransfer(address newAdmin) public virtual onlyRole(DEFAULT_ADMIN_ROLE) {
            _beginDefaultAdminTransfer(newAdmin);
        }
    
        /**
         * @dev See {beginDefaultAdminTransfer}.
         *
         * Internal function without access restriction.
         */
        function _beginDefaultAdminTransfer(address newAdmin) internal virtual {
            uint48 newSchedule = SafeCast.toUint48(block.timestamp) + defaultAdminDelay();
            _setPendingDefaultAdmin(newAdmin, newSchedule);
            emit DefaultAdminTransferScheduled(newAdmin, newSchedule);
        }
    
        /**
         * @inheritdoc IAccessControlDefaultAdminRules
         */
        function cancelDefaultAdminTransfer() public virtual onlyRole(DEFAULT_ADMIN_ROLE) {
            _cancelDefaultAdminTransfer();
        }
    
        /**
         * @dev See {cancelDefaultAdminTransfer}.
         *
         * Internal function without access restriction.
         */
        function _cancelDefaultAdminTransfer() internal virtual {
            _setPendingDefaultAdmin(address(0), 0);
        }
    
        /**
         * @inheritdoc IAccessControlDefaultAdminRules
         */
        function acceptDefaultAdminTransfer() public virtual {
            (address newDefaultAdmin, ) = pendingDefaultAdmin();
            if (_msgSender() != newDefaultAdmin) {
                // Enforce newDefaultAdmin explicit acceptance.
                revert AccessControlInvalidDefaultAdmin(_msgSender());
            }
            _acceptDefaultAdminTransfer();
        }
    
        /**
         * @dev See {acceptDefaultAdminTransfer}.
         *
         * Internal function without access restriction.
         */
        function _acceptDefaultAdminTransfer() internal virtual {
            (address newAdmin, uint48 schedule) = pendingDefaultAdmin();
            if (!_isScheduleSet(schedule) || !_hasSchedulePassed(schedule)) {
                revert AccessControlEnforcedDefaultAdminDelay(schedule);
            }
            _revokeRole(DEFAULT_ADMIN_ROLE, defaultAdmin());
            _grantRole(DEFAULT_ADMIN_ROLE, newAdmin);
            delete _pendingDefaultAdmin;
            delete _pendingDefaultAdminSchedule;
        }
    
        ///
        /// AccessControlDefaultAdminRules public and internal setters for defaultAdminDelay/pendingDefaultAdminDelay
        ///
    
        /**
         * @inheritdoc IAccessControlDefaultAdminRules
         */
        function changeDefaultAdminDelay(uint48 newDelay) public virtual onlyRole(DEFAULT_ADMIN_ROLE) {
            _changeDefaultAdminDelay(newDelay);
        }
    
        /**
         * @dev See {changeDefaultAdminDelay}.
         *
         * Internal function without access restriction.
         */
        function _changeDefaultAdminDelay(uint48 newDelay) internal virtual {
            uint48 newSchedule = SafeCast.toUint48(block.timestamp) + _delayChangeWait(newDelay);
            _setPendingDelay(newDelay, newSchedule);
            emit DefaultAdminDelayChangeScheduled(newDelay, newSchedule);
        }
    
        /**
         * @inheritdoc IAccessControlDefaultAdminRules
         */
        function rollbackDefaultAdminDelay() public virtual onlyRole(DEFAULT_ADMIN_ROLE) {
            _rollbackDefaultAdminDelay();
        }
    
        /**
         * @dev See {rollbackDefaultAdminDelay}.
         *
         * Internal function without access restriction.
         */
        function _rollbackDefaultAdminDelay() internal virtual {
            _setPendingDelay(0, 0);
        }
    
        /**
         * @dev Returns the amount of seconds to wait after the `newDelay` will
         * become the new {defaultAdminDelay}.
         *
         * The value returned guarantees that if the delay is reduced, it will go into effect
         * after a wait that honors the previously set delay.
         *
         * See {defaultAdminDelayIncreaseWait}.
         */
        function _delayChangeWait(uint48 newDelay) internal view virtual returns (uint48) {
            uint48 currentDelay = defaultAdminDelay();
    
            // When increasing the delay, we schedule the delay change to occur after a period of "new delay" has passed, up
            // to a maximum given by defaultAdminDelayIncreaseWait, by default 5 days. For example, if increasing from 1 day
            // to 3 days, the new delay will come into effect after 3 days. If increasing from 1 day to 10 days, the new
            // delay will come into effect after 5 days. The 5 day wait period is intended to be able to fix an error like
            // using milliseconds instead of seconds.
            //
            // When decreasing the delay, we wait the difference between "current delay" and "new delay". This guarantees
            // that an admin transfer cannot be made faster than "current delay" at the time the delay change is scheduled.
            // For example, if decreasing from 10 days to 3 days, the new delay will come into effect after 7 days.
            return
                newDelay > currentDelay
                    ? uint48(Math.min(newDelay, defaultAdminDelayIncreaseWait())) // no need to safecast, both inputs are uint48
                    : currentDelay - newDelay;
        }
    
        ///
        /// Private setters
        ///
    
        /**
         * @dev Setter of the tuple for pending admin and its schedule.
         *
         * May emit a DefaultAdminTransferCanceled event.
         */
        function _setPendingDefaultAdmin(address newAdmin, uint48 newSchedule) private {
            (, uint48 oldSchedule) = pendingDefaultAdmin();
    
            _pendingDefaultAdmin = newAdmin;
            _pendingDefaultAdminSchedule = newSchedule;
    
            // An `oldSchedule` from `pendingDefaultAdmin()` is only set if it hasn't been accepted.
            if (_isScheduleSet(oldSchedule)) {
                // Emit for implicit cancellations when another default admin was scheduled.
                emit DefaultAdminTransferCanceled();
            }
        }
    
        /**
         * @dev Setter of the tuple for pending delay and its schedule.
         *
         * May emit a DefaultAdminDelayChangeCanceled event.
         */
        function _setPendingDelay(uint48 newDelay, uint48 newSchedule) private {
            uint48 oldSchedule = _pendingDelaySchedule;
    
            if (_isScheduleSet(oldSchedule)) {
                if (_hasSchedulePassed(oldSchedule)) {
                    // Materialize a virtual delay
                    _currentDelay = _pendingDelay;
                } else {
                    // Emit for implicit cancellations when another delay was scheduled.
                    emit DefaultAdminDelayChangeCanceled();
                }
            }
    
            _pendingDelay = newDelay;
            _pendingDelaySchedule = newSchedule;
        }
    
        ///
        /// Private helpers
        ///
    
        /**
         * @dev Defines if an `schedule` is considered set. For consistency purposes.
         */
        function _isScheduleSet(uint48 schedule) private pure returns (bool) {
            return schedule != 0;
        }
    
        /**
         * @dev Defines if an `schedule` is considered passed. For consistency purposes.
         */
        function _hasSchedulePassed(uint48 schedule) private view returns (bool) {
            return schedule < block.timestamp;
        }
    }

    <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.28;
    
    contract Initializable {
        error AlreadyInitialized();
    
        uint256 private constant NOT_INITIALIZED = 1;
        uint256 private constant INITIALIZED = 2;
    
        uint256 private _status;
    
        modifier notInitialized() {
            if (_status == INITIALIZED) {
                revert AlreadyInitialized();
            }
    
            _status = INITIALIZED;
    
            _;
        }
    
        constructor() {
            _status = NOT_INITIALIZED;
        }
    
        function _setInitialized() internal notInitialized {}
    }

    <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol)
    
    pragma solidity ^0.8.20;
    
    import {IERC20} from "../IERC20.sol";
    import {IERC20Permit} from "../extensions/IERC20Permit.sol";
    import {Address} from "../../../utils/Address.sol";
    
    /**
     * @title SafeERC20
     * @dev Wrappers around ERC20 operations that throw on failure (when the token
     * contract returns false). Tokens that return no value (and instead revert or
     * throw on failure) are also supported, non-reverting calls are assumed to be
     * successful.
     * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
     * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
     */
    library SafeERC20 {
        using Address for address;
    
        /**
         * @dev An operation with an ERC20 token failed.
         */
        error SafeERC20FailedOperation(address token);
    
        /**
         * @dev Indicates a failed `decreaseAllowance` request.
         */
        error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);
    
        /**
         * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
         * non-reverting calls are assumed to be successful.
         */
        function safeTransfer(IERC20 token, address to, uint256 value) internal {
            _callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
        }
    
        /**
         * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
         * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
         */
        function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
            _callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
        }
    
        /**
         * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
         * non-reverting calls are assumed to be successful.
         */
        function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
            uint256 oldAllowance = token.allowance(address(this), spender);
            forceApprove(token, spender, oldAllowance + value);
        }
    
        /**
         * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
         * value, non-reverting calls are assumed to be successful.
         */
        function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
            unchecked {
                uint256 currentAllowance = token.allowance(address(this), spender);
                if (currentAllowance < requestedDecrease) {
                    revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
                }
                forceApprove(token, spender, currentAllowance - requestedDecrease);
            }
        }
    
        /**
         * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
         * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
         * to be set to zero before setting it to a non-zero value, such as USDT.
         */
        function forceApprove(IERC20 token, address spender, uint256 value) internal {
            bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));
    
            if (!_callOptionalReturnBool(token, approvalCall)) {
                _callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
                _callOptionalReturn(token, approvalCall);
            }
        }
    
        /**
         * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
         * on the return value: the return value is optional (but if data is returned, it must not be false).
         * @param token The token targeted by the call.
         * @param data The call data (encoded using abi.encode or one of its variants).
         */
        function _callOptionalReturn(IERC20 token, bytes memory data) private {
            // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
            // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
            // the target address contains contract code and also asserts for success in the low-level call.
    
            bytes memory returndata = address(token).functionCall(data);
            if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
                revert SafeERC20FailedOperation(address(token));
            }
        }
    
        /**
         * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
         * on the return value: the return value is optional (but if data is returned, it must not be false).
         * @param token The token targeted by the call.
         * @param data The call data (encoded using abi.encode or one of its variants).
         *
         * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
         */
        function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
            // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
            // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
            // and not revert is the subcall reverts.
    
            (bool success, bytes memory returndata) = address(token).call(data);
            return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0;
        }
    }

    <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

    // SPDX-License-Identifier: MIT
    
    pragma solidity ^0.8.0;
    
    import { ISemver } from "./ISemver.sol";
    
    import { ISchemaResolver } from "./resolver/ISchemaResolver.sol";
    
    /// @notice A struct representing a record for a submitted schema.
    struct SchemaRecord {
        bytes32 uid; // The unique identifier of the schema.
        ISchemaResolver resolver; // Optional schema resolver.
        bool revocable; // Whether the schema allows revocations explicitly.
        string schema; // Custom specification of the schema (e.g., an ABI).
    }
    
    /// @title ISchemaRegistry
    /// @notice The interface of global attestation schemas for the Ethereum Attestation Service protocol.
    interface ISchemaRegistry is ISemver {
        /// @notice Emitted when a new schema has been registered
        /// @param uid The schema UID.
        /// @param registerer The address of the account used to register the schema.
        /// @param schema The schema data.
        event Registered(bytes32 indexed uid, address indexed registerer, SchemaRecord schema);
    
        /// @notice Submits and reserves a new schema
        /// @param schema The schema data schema.
        /// @param resolver An optional schema resolver.
        /// @param revocable Whether the schema allows revocations explicitly.
        /// @return The UID of the new schema.
        function register(string calldata schema, ISchemaResolver resolver, bool revocable) external returns (bytes32);
    
        /// @notice Returns an existing schema by UID
        /// @param uid The UID of the schema to retrieve.
        /// @return The schema data members.
        function getSchema(bytes32 uid) external view returns (SchemaRecord memory);
    }

    <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

    // SPDX-License-Identifier: MIT
    
    pragma solidity ^0.8.0;
    
    /// @title ISemver
    /// @notice A semver interface.
    interface ISemver {
        /// @notice Returns the full semver contract version.
        /// @return Semver contract version as a string.
        function version() external view returns (string memory);
    }

    <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

    // SPDX-License-Identifier: MIT
    // ERC721A Contracts v4.3.0
    // 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);*/
    }

    <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

    // SPDX-License-Identifier: MIT
    // ERC721A Contracts v4.3.0
    // Creator: Chiru Labs
    
    pragma solidity ^0.8.4;
    
    import './IERC721A.sol';
    
    /**
     * @dev Interface of ERC721 token receiver.
     */
    interface ERC721A__IERC721Receiver {
        function onERC721Received(
            address operator,
            address from,
            uint256 tokenId,
            bytes calldata data
        ) external returns (bytes4);
    }
    
    /**
     * @title ERC721A
     *
     * @dev Implementation of the [ERC721](https://eips.ethereum.org/EIPS/eip-721)
     * Non-Fungible Token Standard, including the Metadata extension.
     * Optimized for lower gas during batch mints.
     *
     * Token IDs are minted in sequential order (e.g. 0, 1, 2, 3, ...)
     * starting from `_startTokenId()`.
     *
     * The `_sequentialUpTo()` function can be overriden to enable spot mints
     * (i.e. non-consecutive mints) for `tokenId`s greater than `_sequentialUpTo()`.
     *
     * Assumptions:
     *
     * - An owner cannot have more than 2**64 - 1 (max value of uint64) of supply.
     * - The maximum token ID cannot exceed 2**256 - 1 (max value of uint256).
     */
    contract ERC721A is IERC721A {
        // Bypass for a `--via-ir` bug (https://github.com/chiru-labs/ERC721A/pull/364).
        struct TokenApprovalRef {
            address value;
        }
    
        // =============================================================
        //                           CONSTANTS
        // =============================================================
    
        // Mask of an entry in packed address data.
        uint256 private constant _BITMASK_ADDRESS_DATA_ENTRY = (1 << 64) - 1;
    
        // The bit position of `numberMinted` in packed address data.
        uint256 private constant _BITPOS_NUMBER_MINTED = 64;
    
        // The bit position of `numberBurned` in packed address data.
        uint256 private constant _BITPOS_NUMBER_BURNED = 128;
    
        // The bit position of `aux` in packed address data.
        uint256 private constant _BITPOS_AUX = 192;
    
        // Mask of all 256 bits in packed address data except the 64 bits for `aux`.
        uint256 private constant _BITMASK_AUX_COMPLEMENT = (1 << 192) - 1;
    
        // The bit position of `startTimestamp` in packed ownership.
        uint256 private constant _BITPOS_START_TIMESTAMP = 160;
    
        // The bit mask of the `burned` bit in packed ownership.
        uint256 private constant _BITMASK_BURNED = 1 << 224;
    
        // The bit position of the `nextInitialized` bit in packed ownership.
        uint256 private constant _BITPOS_NEXT_INITIALIZED = 225;
    
        // The bit mask of the `nextInitialized` bit in packed ownership.
        uint256 private constant _BITMASK_NEXT_INITIALIZED = 1 << 225;
    
        // The bit position of `extraData` in packed ownership.
        uint256 private constant _BITPOS_EXTRA_DATA = 232;
    
        // Mask of all 256 bits in a packed ownership except the 24 bits for `extraData`.
        uint256 private constant _BITMASK_EXTRA_DATA_COMPLEMENT = (1 << 232) - 1;
    
        // The mask of the lower 160 bits for addresses.
        uint256 private constant _BITMASK_ADDRESS = (1 << 160) - 1;
    
        // The maximum `quantity` that can be minted with {_mintERC2309}.
        // This limit is to prevent overflows on the address data entries.
        // For a limit of 5000, a total of 3.689e15 calls to {_mintERC2309}
        // is required to cause an overflow, which is unrealistic.
        uint256 private constant _MAX_MINT_ERC2309_QUANTITY_LIMIT = 5000;
    
        // The `Transfer` event signature is given by:
        // `keccak256(bytes("Transfer(address,address,uint256)"))`.
        bytes32 private constant _TRANSFER_EVENT_SIGNATURE =
            0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef;
    
        // =============================================================
        //                            STORAGE
        // =============================================================
    
        // The next token ID to be minted.
        uint256 private _currentIndex;
    
        // The number of tokens burned.
        uint256 private _burnCounter;
    
        // Token name
        string private _name;
    
        // Token symbol
        string private _symbol;
    
        // Mapping from token ID to ownership details
        // An empty struct value does not necessarily mean the token is unowned.
        // See {_packedOwnershipOf} implementation for details.
        //
        // Bits Layout:
        // - [0..159]   `addr`
        // - [160..223] `startTimestamp`
        // - [224]      `burned`
        // - [225]      `nextInitialized`
        // - [232..255] `extraData`
        mapping(uint256 => uint256) private _packedOwnerships;
    
        // Mapping owner address to address data.
        //
        // Bits Layout:
        // - [0..63]    `balance`
        // - [64..127]  `numberMinted`
        // - [128..191] `numberBurned`
        // - [192..255] `aux`
        mapping(address => uint256) private _packedAddressData;
    
        // Mapping from token ID to approved address.
        mapping(uint256 => TokenApprovalRef) private _tokenApprovals;
    
        // Mapping from owner to operator approvals
        mapping(address => mapping(address => bool)) internal _operatorApprovals;
    
        // The amount of tokens minted above `_sequentialUpTo()`.
        // We call these spot mints (i.e. non-sequential mints).
        uint256 private _spotMinted;
    
        // =============================================================
        //                          CONSTRUCTOR
        // =============================================================
    
        constructor(string memory name_, string memory symbol_) {
            _name = name_;
            _symbol = symbol_;
            _currentIndex = _startTokenId();
    
            if (_sequentialUpTo() < _startTokenId()) _revert(SequentialUpToTooSmall.selector);
        }
    
        // =============================================================
        //                   TOKEN COUNTING OPERATIONS
        // =============================================================
    
        /**
         * @dev Returns the starting token ID for sequential mints.
         *
         * Override this function to change the starting token ID for sequential mints.
         *
         * Note: The value returned must never change after any tokens have been minted.
         */
        function _startTokenId() internal view virtual returns (uint256) {
            return 0;
        }
    
        /**
         * @dev Returns the maximum token ID (inclusive) for sequential mints.
         *
         * Override this function to return a value less than 2**256 - 1,
         * but greater than `_startTokenId()`, to enable spot (non-sequential) mints.
         *
         * Note: The value returned must never change after any tokens have been minted.
         */
        function _sequentialUpTo() internal view virtual returns (uint256) {
            return type(uint256).max;
        }
    
        /**
         * @dev Returns the next token ID to be minted.
         */
        function _nextTokenId() internal view virtual returns (uint256) {
            return _currentIndex;
        }
    
        /**
         * @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() public view virtual override returns (uint256 result) {
            // Counter underflow is impossible as `_burnCounter` cannot be incremented
            // more than `_currentIndex + _spotMinted - _startTokenId()` times.
            unchecked {
                // With spot minting, the intermediate `result` can be temporarily negative,
                // and the computation must be unchecked.
                result = _currentIndex - _burnCounter - _startTokenId();
                if (_sequentialUpTo() != type(uint256).max) result += _spotMinted;
            }
        }
    
        /**
         * @dev Returns the total amount of tokens minted in the contract.
         */
        function _totalMinted() internal view virtual returns (uint256 result) {
            // Counter underflow is impossible as `_currentIndex` does not decrement,
            // and it is initialized to `_startTokenId()`.
            unchecked {
                result = _currentIndex - _startTokenId();
                if (_sequentialUpTo() != type(uint256).max) result += _spotMinted;
            }
        }
    
        /**
         * @dev Returns the total number of tokens burned.
         */
        function _totalBurned() internal view virtual returns (uint256) {
            return _burnCounter;
        }
    
        /**
         * @dev Returns the total number of tokens that are spot-minted.
         */
        function _totalSpotMinted() internal view virtual returns (uint256) {
            return _spotMinted;
        }
    
        // =============================================================
        //                    ADDRESS DATA OPERATIONS
        // =============================================================
    
        /**
         * @dev Returns the number of tokens in `owner`'s account.
         */
        function balanceOf(address owner) public view virtual override returns (uint256) {
            if (owner == address(0)) _revert(BalanceQueryForZeroAddress.selector);
            return _packedAddressData[owner] & _BITMASK_ADDRESS_DATA_ENTRY;
        }
    
        /**
         * Returns the number of tokens minted by `owner`.
         */
        function _numberMinted(address owner) internal view returns (uint256) {
            return (_packedAddressData[owner] >> _BITPOS_NUMBER_MINTED) & _BITMASK_ADDRESS_DATA_ENTRY;
        }
    
        /**
         * Returns the number of tokens burned by or on behalf of `owner`.
         */
        function _numberBurned(address owner) internal view returns (uint256) {
            return (_packedAddressData[owner] >> _BITPOS_NUMBER_BURNED) & _BITMASK_ADDRESS_DATA_ENTRY;
        }
    
        /**
         * Returns the auxiliary data for `owner`. (e.g. number of whitelist mint slots used).
         */
        function _getAux(address owner) internal view returns (uint64) {
            return uint64(_packedAddressData[owner] >> _BITPOS_AUX);
        }
    
        /**
         * Sets the auxiliary data for `owner`. (e.g. number of whitelist mint slots used).
         * If there are multiple variables, please pack them into a uint64.
         */
        function _setAux(address owner, uint64 aux) internal virtual {
            uint256 packed = _packedAddressData[owner];
            uint256 auxCasted;
            // Cast `aux` with assembly to avoid redundant masking.
            assembly {
                auxCasted := aux
            }
            packed = (packed & _BITMASK_AUX_COMPLEMENT) | (auxCasted << _BITPOS_AUX);
            _packedAddressData[owner] = packed;
        }
    
        // =============================================================
        //                            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) public view virtual override returns (bool) {
            // The interface IDs are constants representing the first 4 bytes
            // of the XOR of all function selectors in the interface.
            // See: [ERC165](https://eips.ethereum.org/EIPS/eip-165)
            // (e.g. `bytes4(i.functionA.selector ^ i.functionB.selector ^ ...)`)
            return
                interfaceId == 0x01ffc9a7 || // ERC165 interface ID for ERC165.
                interfaceId == 0x80ac58cd || // ERC165 interface ID for ERC721.
                interfaceId == 0x5b5e139f; // ERC165 interface ID for ERC721Metadata.
        }
    
        // =============================================================
        //                        IERC721Metadata
        // =============================================================
    
        /**
         * @dev Returns the token collection name.
         */
        function name() public view virtual override returns (string memory) {
            return _name;
        }
    
        /**
         * @dev Returns the token collection symbol.
         */
        function symbol() public view virtual override returns (string memory) {
            return _symbol;
        }
    
        /**
         * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
         */
        function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
            if (!_exists(tokenId)) _revert(URIQueryForNonexistentToken.selector);
    
            string memory baseURI = _baseURI();
            return bytes(baseURI).length != 0 ? string(abi.encodePacked(baseURI, _toString(tokenId))) : '';
        }
    
        /**
         * @dev Base URI for computing {tokenURI}. If set, the resulting URI for each
         * token will be the concatenation of the `baseURI` and the `tokenId`. Empty
         * by default, it can be overridden in child contracts.
         */
        function _baseURI() internal view virtual returns (string memory) {
            return '';
        }
    
        // =============================================================
        //                     OWNERSHIPS OPERATIONS
        // =============================================================
    
        /**
         * @dev Returns the owner of the `tokenId` token.
         *
         * Requirements:
         *
         * - `tokenId` must exist.
         */
        function ownerOf(uint256 tokenId) public view virtual override returns (address) {
            return address(uint160(_packedOwnershipOf(tokenId)));
        }
    
        /**
         * @dev Gas spent here starts off proportional to the maximum mint batch size.
         * It gradually moves to O(1) as tokens get transferred around over time.
         */
        function _ownershipOf(uint256 tokenId) internal view virtual returns (TokenOwnership memory) {
            return _unpackedOwnership(_packedOwnershipOf(tokenId));
        }
    
        /**
         * @dev Returns the unpacked `TokenOwnership` struct at `index`.
         */
        function _ownershipAt(uint256 index) internal view virtual returns (TokenOwnership memory) {
            return _unpackedOwnership(_packedOwnerships[index]);
        }
    
        /**
         * @dev Returns whether the ownership slot at `index` is initialized.
         * An uninitialized slot does not necessarily mean that the slot has no owner.
         */
        function _ownershipIsInitialized(uint256 index) internal view virtual returns (bool) {
            return _packedOwnerships[index] != 0;
        }
    
        /**
         * @dev Initializes the ownership slot minted at `index` for efficiency purposes.
         */
        function _initializeOwnershipAt(uint256 index) internal virtual {
            if (_packedOwnerships[index] == 0) {
                _packedOwnerships[index] = _packedOwnershipOf(index);
            }
        }
    
        /**
         * @dev Returns the packed ownership data of `tokenId`.
         */
        function _packedOwnershipOf(uint256 tokenId) private view returns (uint256 packed) {
            if (_startTokenId() <= tokenId) {
                packed = _packedOwnerships[tokenId];
    
                if (tokenId > _sequentialUpTo()) {
                    if (_packedOwnershipExists(packed)) return packed;
                    _revert(OwnerQueryForNonexistentToken.selector);
                }
    
                // If the data at the starting slot does not exist, start the scan.
                if (packed == 0) {
                    if (tokenId >= _currentIndex) _revert(OwnerQueryForNonexistentToken.selector);
                    // Invariant:
                    // There will always be an initialized ownership slot
                    // (i.e. `ownership.addr != address(0) && ownership.burned == false`)
                    // before an unintialized ownership slot
                    // (i.e. `ownership.addr == address(0) && ownership.burned == false`)
                    // Hence, `tokenId` will not underflow.
                    //
                    // We can directly compare the packed value.
                    // If the address is zero, packed will be zero.
                    for (;;) {
                        unchecked {
                            packed = _packedOwnerships[--tokenId];
                        }
                        if (packed == 0) continue;
                        if (packed & _BITMASK_BURNED == 0) return packed;
                        // Otherwise, the token is burned, and we must revert.
                        // This handles the case of batch burned tokens, where only the burned bit
                        // of the starting slot is set, and remaining slots are left uninitialized.
                        _revert(OwnerQueryForNonexistentToken.selector);
                    }
                }
                // Otherwise, the data exists and we can skip the scan.
                // This is possible because we have already achieved the target condition.
                // This saves 2143 gas on transfers of initialized tokens.
                // If the token is not burned, return `packed`. Otherwise, revert.
                if (packed & _BITMASK_BURNED == 0) return packed;
            }
            _revert(OwnerQueryForNonexistentToken.selector);
        }
    
        /**
         * @dev Returns the unpacked `TokenOwnership` struct from `packed`.
         */
        function _unpackedOwnership(uint256 packed) private pure returns (TokenOwnership memory ownership) {
            ownership.addr = address(uint160(packed));
            ownership.startTimestamp = uint64(packed >> _BITPOS_START_TIMESTAMP);
            ownership.burned = packed & _BITMASK_BURNED != 0;
            ownership.extraData = uint24(packed >> _BITPOS_EXTRA_DATA);
        }
    
        /**
         * @dev Packs ownership data into a single uint256.
         */
        function _packOwnershipData(address owner, uint256 flags) private view returns (uint256 result) {
            assembly {
                // Mask `owner` to the lower 160 bits, in case the upper bits somehow aren't clean.
                owner := and(owner, _BITMASK_ADDRESS)
                // `owner | (block.timestamp << _BITPOS_START_TIMESTAMP) | flags`.
                result := or(owner, or(shl(_BITPOS_START_TIMESTAMP, timestamp()), flags))
            }
        }
    
        /**
         * @dev Returns the `nextInitialized` flag set if `quantity` equals 1.
         */
        function _nextInitializedFlag(uint256 quantity) private pure returns (uint256 result) {
            // For branchless setting of the `nextInitialized` flag.
            assembly {
                // `(quantity == 1) << _BITPOS_NEXT_INITIALIZED`.
                result := shl(_BITPOS_NEXT_INITIALIZED, eq(quantity, 1))
            }
        }
    
        // =============================================================
        //                      APPROVAL OPERATIONS
        // =============================================================
    
        /**
         * @dev Gives permission to `to` to transfer `tokenId` token to another account. See {ERC721A-_approve}.
         *
         * Requirements:
         *
         * - The caller must own the token or be an approved operator.
         */
        function approve(address to, uint256 tokenId) public payable virtual override {
            _approve(to, tokenId, true);
        }
    
        /**
         * @dev Returns the account approved for `tokenId` token.
         *
         * Requirements:
         *
         * - `tokenId` must exist.
         */
        function getApproved(uint256 tokenId) public view virtual override returns (address) {
            if (!_exists(tokenId)) _revert(ApprovalQueryForNonexistentToken.selector);
    
            return _tokenApprovals[tokenId].value;
        }
    
        /**
         * @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) public virtual override {
            _operatorApprovals[_msgSenderERC721A()][operator] = approved;
            emit ApprovalForAll(_msgSenderERC721A(), operator, approved);
        }
    
        /**
         * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
         *
         * See {setApprovalForAll}.
         */
        function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) {
            return _operatorApprovals[owner][operator];
        }
    
        /**
         * @dev Returns whether `tokenId` exists.
         *
         * Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}.
         *
         * Tokens start existing when they are minted. See {_mint}.
         */
        function _exists(uint256 tokenId) internal view virtual returns (bool result) {
            if (_startTokenId() <= tokenId) {
                if (tokenId > _sequentialUpTo()) return _packedOwnershipExists(_packedOwnerships[tokenId]);
    
                if (tokenId < _currentIndex) {
                    uint256 packed;
                    while ((packed = _packedOwnerships[tokenId]) == 0) --tokenId;
                    result = packed & _BITMASK_BURNED == 0;
                }
            }
        }
    
        /**
         * @dev Returns whether `packed` represents a token that exists.
         */
        function _packedOwnershipExists(uint256 packed) private pure returns (bool result) {
            assembly {
                // The following is equivalent to `owner != address(0) && burned == false`.
                // Symbolically tested.
                result := gt(and(packed, _BITMASK_ADDRESS), and(packed, _BITMASK_BURNED))
            }
        }
    
        /**
         * @dev Returns whether `msgSender` is equal to `approvedAddress` or `owner`.
         */
        function _isSenderApprovedOrOwner(
            address approvedAddress,
            address owner,
            address msgSender
        ) private pure returns (bool result) {
            assembly {
                // Mask `owner` to the lower 160 bits, in case the upper bits somehow aren't clean.
                owner := and(owner, _BITMASK_ADDRESS)
                // Mask `msgSender` to the lower 160 bits, in case the upper bits somehow aren't clean.
                msgSender := and(msgSender, _BITMASK_ADDRESS)
                // `msgSender == owner || msgSender == approvedAddress`.
                result := or(eq(msgSender, owner), eq(msgSender, approvedAddress))
            }
        }
    
        /**
         * @dev Returns the storage slot and value for the approved address of `tokenId`.
         */
        function _getApprovedSlotAndAddress(uint256 tokenId)
            private
            view
            returns (uint256 approvedAddressSlot, address approvedAddress)
        {
            TokenApprovalRef storage tokenApproval = _tokenApprovals[tokenId];
            // The following is equivalent to `approvedAddress = _tokenApprovals[tokenId].value`.
            assembly {
                approvedAddressSlot := tokenApproval.slot
                approvedAddress := sload(approvedAddressSlot)
            }
        }
    
        // =============================================================
        //                      TRANSFER OPERATIONS
        // =============================================================
    
        /**
         * @dev Transfers `tokenId` from `from` to `to`.
         *
         * 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
        ) public payable virtual override {
            uint256 prevOwnershipPacked = _packedOwnershipOf(tokenId);
    
            // Mask `from` to the lower 160 bits, in case the upper bits somehow aren't clean.
            from = address(uint160(uint256(uint160(from)) & _BITMASK_ADDRESS));
    
            if (address(uint160(prevOwnershipPacked)) != from) _revert(TransferFromIncorrectOwner.selector);
    
            (uint256 approvedAddressSlot, address approvedAddress) = _getApprovedSlotAndAddress(tokenId);
    
            // The nested ifs save around 20+ gas over a compound boolean condition.
            if (!_isSenderApprovedOrOwner(approvedAddress, from, _msgSenderERC721A()))
                if (!isApprovedForAll(from, _msgSenderERC721A())) _revert(TransferCallerNotOwnerNorApproved.selector);
    
            _beforeTokenTransfers(from, to, tokenId, 1);
    
            // Clear approvals from the previous owner.
            assembly {
                if approvedAddress {
                    // This is equivalent to `delete _tokenApprovals[tokenId]`.
                    sstore(approvedAddressSlot, 0)
                }
            }
    
            // Underflow of the sender's balance is impossible because we check for
            // ownership above and the recipient's balance can't realistically overflow.
            // Counter overflow is incredibly unrealistic as `tokenId` would have to be 2**256.
            unchecked {
                // We can directly increment and decrement the balances.
                --_packedAddressData[from]; // Updates: `balance -= 1`.
                ++_packedAddressData[to]; // Updates: `balance += 1`.
    
                // Updates:
                // - `address` to the next owner.
                // - `startTimestamp` to the timestamp of transfering.
                // - `burned` to `false`.
                // - `nextInitialized` to `true`.
                _packedOwnerships[tokenId] = _packOwnershipData(
                    to,
                    _BITMASK_NEXT_INITIALIZED | _nextExtraData(from, to, prevOwnershipPacked)
                );
    
                // If the next slot may not have been initialized (i.e. `nextInitialized == false`) .
                if (prevOwnershipPacked & _BITMASK_NEXT_INITIALIZED == 0) {
                    uint256 nextTokenId = tokenId + 1;
                    // If the next slot's address is zero and not burned (i.e. packed value is zero).
                    if (_packedOwnerships[nextTokenId] == 0) {
                        // If the next slot is within bounds.
                        if (nextTokenId != _currentIndex) {
                            // Initialize the next slot to maintain correctness for `ownerOf(tokenId + 1)`.
                            _packedOwnerships[nextTokenId] = prevOwnershipPacked;
                        }
                    }
                }
            }
    
            // Mask `to` to the lower 160 bits, in case the upper bits somehow aren't clean.
            uint256 toMasked = uint256(uint160(to)) & _BITMASK_ADDRESS;
            assembly {
                // Emit the `Transfer` event.
                log4(
                    0, // Start of data (0, since no data).
                    0, // End of data (0, since no data).
                    _TRANSFER_EVENT_SIGNATURE, // Signature.
                    from, // `from`.
                    toMasked, // `to`.
                    tokenId // `tokenId`.
                )
            }
            if (toMasked == 0) _revert(TransferToZeroAddress.selector);
    
            _afterTokenTransfers(from, to, tokenId, 1);
        }
    
        /**
         * @dev Equivalent to `safeTransferFrom(from, to, tokenId, '')`.
         */
        function safeTransferFrom(
            address from,
            address to,
            uint256 tokenId
        ) public payable virtual override {
            safeTransferFrom(from, to, tokenId, '');
        }
    
        /**
         * @dev Safely transfers `tokenId` token from `from` to `to`.
         *
         * 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 approved 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 memory _data
        ) public payable virtual override {
            transferFrom(from, to, tokenId);
            if (to.code.length != 0)
                if (!_checkContractOnERC721Received(from, to, tokenId, _data)) {
                    _revert(TransferToNonERC721ReceiverImplementer.selector);
                }
        }
    
        /**
         * @dev Hook that is called before a set of serially-ordered token IDs
         * are about to be transferred. This includes minting.
         * And also called before burning one token.
         *
         * `startTokenId` - the first token ID to be transferred.
         * `quantity` - the amount to be transferred.
         *
         * Calling conditions:
         *
         * - When `from` and `to` are both non-zero, `from`'s `tokenId` will be
         * transferred to `to`.
         * - When `from` is zero, `tokenId` will be minted for `to`.
         * - When `to` is zero, `tokenId` will be burned by `from`.
         * - `from` and `to` are never both zero.
         */
        function _beforeTokenTransfers(
            address from,
            address to,
            uint256 startTokenId,
            uint256 quantity
        ) internal virtual {}
    
        /**
         * @dev Hook that is called after a set of serially-ordered token IDs
         * have been transferred. This includes minting.
         * And also called after one token has been burned.
         *
         * `startTokenId` - the first token ID to be transferred.
         * `quantity` - the amount to be transferred.
         *
         * Calling conditions:
         *
         * - When `from` and `to` are both non-zero, `from`'s `tokenId` has been
         * transferred to `to`.
         * - When `from` is zero, `tokenId` has been minted for `to`.
         * - When `to` is zero, `tokenId` has been burned by `from`.
         * - `from` and `to` are never both zero.
         */
        function _afterTokenTransfers(
            address from,
            address to,
            uint256 startTokenId,
            uint256 quantity
        ) internal virtual {}
    
        /**
         * @dev Private function to invoke {IERC721Receiver-onERC721Received} on a target contract.
         *
         * `from` - Previous owner of the given token ID.
         * `to` - Target address that will receive the token.
         * `tokenId` - Token ID to be transferred.
         * `_data` - Optional data to send along with the call.
         *
         * Returns whether the call correctly returned the expected magic value.
         */
        function _checkContractOnERC721Received(
            address from,
            address to,
            uint256 tokenId,
            bytes memory _data
        ) private returns (bool) {
            try ERC721A__IERC721Receiver(to).onERC721Received(_msgSenderERC721A(), from, tokenId, _data) returns (
                bytes4 retval
            ) {
                return retval == ERC721A__IERC721Receiver(to).onERC721Received.selector;
            } catch (bytes memory reason) {
                if (reason.length == 0) {
                    _revert(TransferToNonERC721ReceiverImplementer.selector);
                }
                assembly {
                    revert(add(32, reason), mload(reason))
                }
            }
        }
    
        // =============================================================
        //                        MINT OPERATIONS
        // =============================================================
    
        /**
         * @dev Mints `quantity` tokens and transfers them to `to`.
         *
         * Requirements:
         *
         * - `to` cannot be the zero address.
         * - `quantity` must be greater than 0.
         *
         * Emits a {Transfer} event for each mint.
         */
        function _mint(address to, uint256 quantity) internal virtual {
            uint256 startTokenId = _currentIndex;
            if (quantity == 0) _revert(MintZeroQuantity.selector);
    
            _beforeTokenTransfers(address(0), to, startTokenId, quantity);
    
            // Overflows are incredibly unrealistic.
            // `balance` and `numberMinted` have a maximum limit of 2**64.
            // `tokenId` has a maximum limit of 2**256.
            unchecked {
                // Updates:
                // - `address` to the owner.
                // - `startTimestamp` to the timestamp of minting.
                // - `burned` to `false`.
                // - `nextInitialized` to `quantity == 1`.
                _packedOwnerships[startTokenId] = _packOwnershipData(
                    to,
                    _nextInitializedFlag(quantity) | _nextExtraData(address(0), to, 0)
                );
    
                // Updates:
                // - `balance += quantity`.
                // - `numberMinted += quantity`.
                //
                // We can directly add to the `balance` and `numberMinted`.
                _packedAddressData[to] += quantity * ((1 << _BITPOS_NUMBER_MINTED) | 1);
    
                // Mask `to` to the lower 160 bits, in case the upper bits somehow aren't clean.
                uint256 toMasked = uint256(uint160(to)) & _BITMASK_ADDRESS;
    
                if (toMasked == 0) _revert(MintToZeroAddress.selector);
    
                uint256 end = startTokenId + quantity;
                uint256 tokenId = startTokenId;
    
                if (end - 1 > _sequentialUpTo()) _revert(SequentialMintExceedsLimit.selector);
    
                do {
                    assembly {
                        // Emit the `Transfer` event.
                        log4(
                            0, // Start of data (0, since no data).
                            0, // End of data (0, since no data).
                            _TRANSFER_EVENT_SIGNATURE, // Signature.
                            0, // `address(0)`.
                            toMasked, // `to`.
                            tokenId // `tokenId`.
                        )
                    }
                    // The `!=` check ensures that large values of `quantity`
                    // that overflows uint256 will make the loop run out of gas.
                } while (++tokenId != end);
    
                _currentIndex = end;
            }
            _afterTokenTransfers(address(0), to, startTokenId, quantity);
        }
    
        /**
         * @dev Mints `quantity` tokens and transfers them to `to`.
         *
         * This function is intended for efficient minting only during contract creation.
         *
         * It emits only one {ConsecutiveTransfer} as defined in
         * [ERC2309](https://eips.ethereum.org/EIPS/eip-2309),
         * instead of a sequence of {Transfer} event(s).
         *
         * Calling this function outside of contract creation WILL make your contract
         * non-compliant with the ERC721 standard.
         * For full ERC721 compliance, substituting ERC721 {Transfer} event(s) with the ERC2309
         * {ConsecutiveTransfer} event is only permissible during contract creation.
         *
         * Requirements:
         *
         * - `to` cannot be the zero address.
         * - `quantity` must be greater than 0.
         *
         * Emits a {ConsecutiveTransfer} event.
         */
        function _mintERC2309(address to, uint256 quantity) internal virtual {
            uint256 startTokenId = _currentIndex;
            if (to == address(0)) _revert(MintToZeroAddress.selector);
            if (quantity == 0) _revert(MintZeroQuantity.selector);
            if (quantity > _MAX_MINT_ERC2309_QUANTITY_LIMIT) _revert(MintERC2309QuantityExceedsLimit.selector);
    
            _beforeTokenTransfers(address(0), to, startTokenId, quantity);
    
            // Overflows are unrealistic due to the above check for `quantity` to be below the limit.
            unchecked {
                // Updates:
                // - `balance += quantity`.
                // - `numberMinted += quantity`.
                //
                // We can directly add to the `balance` and `numberMinted`.
                _packedAddressData[to] += quantity * ((1 << _BITPOS_NUMBER_MINTED) | 1);
    
                // Updates:
                // - `address` to the owner.
                // - `startTimestamp` to the timestamp of minting.
                // - `burned` to `false`.
                // - `nextInitialized` to `quantity == 1`.
                _packedOwnerships[startTokenId] = _packOwnershipData(
                    to,
                    _nextInitializedFlag(quantity) | _nextExtraData(address(0), to, 0)
                );
    
                if (startTokenId + quantity - 1 > _sequentialUpTo()) _revert(SequentialMintExceedsLimit.selector);
    
                emit ConsecutiveTransfer(startTokenId, startTokenId + quantity - 1, address(0), to);
    
                _currentIndex = startTokenId + quantity;
            }
            _afterTokenTransfers(address(0), to, startTokenId, quantity);
        }
    
        /**
         * @dev Safely mints `quantity` tokens and transfers them to `to`.
         *
         * Requirements:
         *
         * - If `to` refers to a smart contract, it must implement
         * {IERC721Receiver-onERC721Received}, which is called for each safe transfer.
         * - `quantity` must be greater than 0.
         *
         * See {_mint}.
         *
         * Emits a {Transfer} event for each mint.
         */
        function _safeMint(
            address to,
            uint256 quantity,
            bytes memory _data
        ) internal virtual {
            _mint(to, quantity);
    
            unchecked {
                if (to.code.length != 0) {
                    uint256 end = _currentIndex;
                    uint256 index = end - quantity;
                    do {
                        if (!_checkContractOnERC721Received(address(0), to, index++, _data)) {
                            _revert(TransferToNonERC721ReceiverImplementer.selector);
                        }
                    } while (index < end);
                    // This prevents reentrancy to `_safeMint`.
                    // It does not prevent reentrancy to `_safeMintSpot`.
                    if (_currentIndex != end) revert();
                }
            }
        }
    
        /**
         * @dev Equivalent to `_safeMint(to, quantity, '')`.
         */
        function _safeMint(address to, uint256 quantity) internal virtual {
            _safeMint(to, quantity, '');
        }
    
        /**
         * @dev Mints a single token at `tokenId`.
         *
         * Note: A spot-minted `tokenId` that has been burned can be re-minted again.
         *
         * Requirements:
         *
         * - `to` cannot be the zero address.
         * - `tokenId` must be greater than `_sequentialUpTo()`.
         * - `tokenId` must not exist.
         *
         * Emits a {Transfer} event for each mint.
         */
        function _mintSpot(address to, uint256 tokenId) internal virtual {
            if (tokenId <= _sequentialUpTo()) _revert(SpotMintTokenIdTooSmall.selector);
            uint256 prevOwnershipPacked = _packedOwnerships[tokenId];
            if (_packedOwnershipExists(prevOwnershipPacked)) _revert(TokenAlreadyExists.selector);
    
            _beforeTokenTransfers(address(0), to, tokenId, 1);
    
            // Overflows are incredibly unrealistic.
            // The `numberMinted` for `to` is incremented by 1, and has a max limit of 2**64 - 1.
            // `_spotMinted` is incremented by 1, and has a max limit of 2**256 - 1.
            unchecked {
                // Updates:
                // - `address` to the owner.
                // - `startTimestamp` to the timestamp of minting.
                // - `burned` to `false`.
                // - `nextInitialized` to `true` (as `quantity == 1`).
                _packedOwnerships[tokenId] = _packOwnershipData(
                    to,
                    _nextInitializedFlag(1) | _nextExtraData(address(0), to, prevOwnershipPacked)
                );
    
                // Updates:
                // - `balance += 1`.
                // - `numberMinted += 1`.
                //
                // We can directly add to the `balance` and `numberMinted`.
                _packedAddressData[to] += (1 << _BITPOS_NUMBER_MINTED) | 1;
    
                // Mask `to` to the lower 160 bits, in case the upper bits somehow aren't clean.
                uint256 toMasked = uint256(uint160(to)) & _BITMASK_ADDRESS;
    
                if (toMasked == 0) _revert(MintToZeroAddress.selector);
    
                assembly {
                    // Emit the `Transfer` event.
                    log4(
                        0, // Start of data (0, since no data).
                        0, // End of data (0, since no data).
                        _TRANSFER_EVENT_SIGNATURE, // Signature.
                        0, // `address(0)`.
                        toMasked, // `to`.
                        tokenId // `tokenId`.
                    )
                }
    
                ++_spotMinted;
            }
    
            _afterTokenTransfers(address(0), to, tokenId, 1);
        }
    
        /**
         * @dev Safely mints a single token at `tokenId`.
         *
         * Note: A spot-minted `tokenId` that has been burned can be re-minted again.
         *
         * Requirements:
         *
         * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}.
         * - `tokenId` must be greater than `_sequentialUpTo()`.
         * - `tokenId` must not exist.
         *
         * See {_mintSpot}.
         *
         * Emits a {Transfer} event.
         */
        function _safeMintSpot(
            address to,
            uint256 tokenId,
            bytes memory _data
        ) internal virtual {
            _mintSpot(to, tokenId);
    
            unchecked {
                if (to.code.length != 0) {
                    uint256 currentSpotMinted = _spotMinted;
                    if (!_checkContractOnERC721Received(address(0), to, tokenId, _data)) {
                        _revert(TransferToNonERC721ReceiverImplementer.selector);
                    }
                    // This prevents reentrancy to `_safeMintSpot`.
                    // It does not prevent reentrancy to `_safeMint`.
                    if (_spotMinted != currentSpotMinted) revert();
                }
            }
        }
    
        /**
         * @dev Equivalent to `_safeMintSpot(to, tokenId, '')`.
         */
        function _safeMintSpot(address to, uint256 tokenId) internal virtual {
            _safeMintSpot(to, tokenId, '');
        }
    
        // =============================================================
        //                       APPROVAL OPERATIONS
        // =============================================================
    
        /**
         * @dev Equivalent to `_approve(to, tokenId, false)`.
         */
        function _approve(address to, uint256 tokenId) internal virtual {
            _approve(to, tokenId, false);
        }
    
        /**
         * @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:
         *
         * - `tokenId` must exist.
         *
         * Emits an {Approval} event.
         */
        function _approve(
            address to,
            uint256 tokenId,
            bool approvalCheck
        ) internal virtual {
            address owner = ownerOf(tokenId);
    
            if (approvalCheck && _msgSenderERC721A() != owner)
                if (!isApprovedForAll(owner, _msgSenderERC721A())) {
                    _revert(ApprovalCallerNotOwnerNorApproved.selector);
                }
    
            _tokenApprovals[tokenId].value = to;
            emit Approval(owner, to, tokenId);
        }
    
        // =============================================================
        //                        BURN OPERATIONS
        // =============================================================
    
        /**
         * @dev Equivalent to `_burn(tokenId, false)`.
         */
        function _burn(uint256 tokenId) internal virtual returns (address from) {
            return _burn(tokenId, false);
        }
    
        /**
         * @dev Destroys `tokenId`.
         * The approval is cleared when the token is burned.
         *
         * Requirements:
         *
         * - `tokenId` must exist.
         *
         * Emits a {Transfer} event.
         */
        function _burn(uint256 tokenId, bool approvalCheck) internal virtual returns (address from) {
            uint256 prevOwnershipPacked = _packedOwnershipOf(tokenId);
    
            from = address(uint160(prevOwnershipPacked));
    
            (uint256 approvedAddressSlot, address approvedAddress) = _getApprovedSlotAndAddress(tokenId);
    
            if (approvalCheck) {
                // The nested ifs save around 20+ gas over a compound boolean condition.
                if (!_isSenderApprovedOrOwner(approvedAddress, from, _msgSenderERC721A()))
                    if (!isApprovedForAll(from, _msgSenderERC721A())) _revert(TransferCallerNotOwnerNorApproved.selector);
            }
    
            _beforeTokenTransfers(from, address(0), tokenId, 1);
    
            // Clear approvals from the previous owner.
            assembly {
                if approvedAddress {
                    // This is equivalent to `delete _tokenApprovals[tokenId]`.
                    sstore(approvedAddressSlot, 0)
                }
            }
    
            // Underflow of the sender's balance is impossible because we check for
            // ownership above and the recipient's balance can't realistically overflow.
            // Counter overflow is incredibly unrealistic as `tokenId` would have to be 2**256.
            unchecked {
                // Updates:
                // - `balance -= 1`.
                // - `numberBurned += 1`.
                //
                // We can directly decrement the balance, and increment the number burned.
                // This is equivalent to `packed -= 1; packed += 1 << _BITPOS_NUMBER_BURNED;`.
                _packedAddressData[from] += (1 << _BITPOS_NUMBER_BURNED) - 1;
    
                // Updates:
                // - `address` to the last owner.
                // - `startTimestamp` to the timestamp of burning.
                // - `burned` to `true`.
                // - `nextInitialized` to `true`.
                _packedOwnerships[tokenId] = _packOwnershipData(
                    from,
                    (_BITMASK_BURNED | _BITMASK_NEXT_INITIALIZED) | _nextExtraData(from, address(0), prevOwnershipPacked)
                );
    
                // If the next slot may not have been initialized (i.e. `nextInitialized == false`) .
                if (prevOwnershipPacked & _BITMASK_NEXT_INITIALIZED == 0) {
                    uint256 nextTokenId = tokenId + 1;
                    // If the next slot's address is zero and not burned (i.e. packed value is zero).
                    if (_packedOwnerships[nextTokenId] == 0) {
                        // If the next slot is within bounds.
                        if (nextTokenId != _currentIndex) {
                            // Initialize the next slot to maintain correctness for `ownerOf(tokenId + 1)`.
                            _packedOwnerships[nextTokenId] = prevOwnershipPacked;
                        }
                    }
                }
            }
    
            emit Transfer(from, address(0), tokenId);
            _afterTokenTransfers(from, address(0), tokenId, 1);
    
            // Overflow not possible, as `_burnCounter` cannot be exceed `_currentIndex + _spotMinted` times.
            unchecked {
                _burnCounter++;
            }
        }
    
        // =============================================================
        //                     EXTRA DATA OPERATIONS
        // =============================================================
    
        /**
         * @dev Directly sets the extra data for the ownership data `index`.
         */
        function _setExtraDataAt(uint256 index, uint24 extraData) internal virtual {
            uint256 packed = _packedOwnerships[index];
            if (packed == 0) _revert(OwnershipNotInitializedForExtraData.selector);
            uint256 extraDataCasted;
            // Cast `extraData` with assembly to avoid redundant masking.
            assembly {
                extraDataCasted := extraData
            }
            packed = (packed & _BITMASK_EXTRA_DATA_COMPLEMENT) | (extraDataCasted << _BITPOS_EXTRA_DATA);
            _packedOwnerships[index] = packed;
        }
    
        /**
         * @dev Called during each token transfer to set the 24bit `extraData` field.
         * Intended to be overridden by the cosumer contract.
         *
         * `previousExtraData` - the value of `extraData` before transfer.
         *
         * Calling conditions:
         *
         * - When `from` and `to` are both non-zero, `from`'s `tokenId` will be
         * transferred to `to`.
         * - When `from` is zero, `tokenId` will be minted for `to`.
         * - When `to` is zero, `tokenId` will be burned by `from`.
         * - `from` and `to` are never both zero.
         */
        function _extraData(
            address from,
            address to,
            uint24 previousExtraData
        ) internal view virtual returns (uint24) {}
    
        /**
         * @dev Returns the next extra data for the packed ownership data.
         * The returned result is shifted into position.
         */
        function _nextExtraData(
            address from,
            address to,
            uint256 prevOwnershipPacked
        ) private view returns (uint256) {
            uint24 extraData = uint24(prevOwnershipPacked >> _BITPOS_EXTRA_DATA);
            return uint256(_extraData(from, to, extraData)) << _BITPOS_EXTRA_DATA;
        }
    
        // =============================================================
        //                       OTHER OPERATIONS
        // =============================================================
    
        /**
         * @dev Returns the message sender (defaults to `msg.sender`).
         *
         * If you are writing GSN compatible contracts, you need to override this function.
         */
        function _msgSenderERC721A() internal view virtual returns (address) {
            return msg.sender;
        }
    
        /**
         * @dev Converts a uint256 to its ASCII string decimal representation.
         */
        function _toString(uint256 value) internal pure virtual returns (string memory str) {
            assembly {
                // The maximum value of a uint256 contains 78 digits (1 byte per digit), but
                // we allocate 0xa0 bytes to keep the free memory pointer 32-byte word aligned.
                // We will need 1 word for the trailing zeros padding, 1 word for the length,
                // and 3 words for a maximum of 78 digits. Total: 5 * 0x20 = 0xa0.
                let m := add(mload(0x40), 0xa0)
                // Update the free memory pointer to allocate.
                mstore(0x40, m)
                // Assign the `str` to the end.
                str := sub(m, 0x20)
                // Zeroize the slot after the string.
                mstore(str, 0)
    
                // Cache the end of the memory to calculate the length later.
                let end := str
    
                // We write the string from rightmost digit to leftmost digit.
                // The following is essentially a do-while loop that also handles the zero case.
                // prettier-ignore
                for { let temp := value } 1 {} {
                    str := sub(str, 1)
                    // Write the character to the pointer.
                    // The ASCII index of the '0' character is 48.
                    mstore8(str, add(48, mod(temp, 10)))
                    // Keep dividing `temp` until zero.
                    temp := div(temp, 10)
                    // prettier-ignore
                    if iszero(temp) { break }
                }
    
                let length := sub(end, str)
                // Move the pointer 32 bytes leftwards to make room for the length.
                str := sub(str, 0x20)
                // Store the length.
                mstore(str, length)
            }
        }
    
        /**
         * @dev For more efficient reverts.
         */
        function _revert(bytes4 errorSelector) internal pure {
            assembly {
                mstore(0x00, errorSelector)
                revert(0x00, 0x04)
            }
        }
    }

    <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (access/extensions/IAccessControlDefaultAdminRules.sol)
    
    pragma solidity ^0.8.20;
    
    import {IAccessControl} from "../IAccessControl.sol";
    
    /**
     * @dev External interface of AccessControlDefaultAdminRules declared to support ERC165 detection.
     */
    interface IAccessControlDefaultAdminRules is IAccessControl {
        /**
         * @dev The new default admin is not a valid default admin.
         */
        error AccessControlInvalidDefaultAdmin(address defaultAdmin);
    
        /**
         * @dev At least one of the following rules was violated:
         *
         * - The `DEFAULT_ADMIN_ROLE` must only be managed by itself.
         * - The `DEFAULT_ADMIN_ROLE` must only be held by one account at the time.
         * - Any `DEFAULT_ADMIN_ROLE` transfer must be in two delayed steps.
         */
        error AccessControlEnforcedDefaultAdminRules();
    
        /**
         * @dev The delay for transferring the default admin delay is enforced and
         * the operation must wait until `schedule`.
         *
         * NOTE: `schedule` can be 0 indicating there's no transfer scheduled.
         */
        error AccessControlEnforcedDefaultAdminDelay(uint48 schedule);
    
        /**
         * @dev Emitted when a {defaultAdmin} transfer is started, setting `newAdmin` as the next
         * address to become the {defaultAdmin} by calling {acceptDefaultAdminTransfer} only after `acceptSchedule`
         * passes.
         */
        event DefaultAdminTransferScheduled(address indexed newAdmin, uint48 acceptSchedule);
    
        /**
         * @dev Emitted when a {pendingDefaultAdmin} is reset if it was never accepted, regardless of its schedule.
         */
        event DefaultAdminTransferCanceled();
    
        /**
         * @dev Emitted when a {defaultAdminDelay} change is started, setting `newDelay` as the next
         * delay to be applied between default admin transfer after `effectSchedule` has passed.
         */
        event DefaultAdminDelayChangeScheduled(uint48 newDelay, uint48 effectSchedule);
    
        /**
         * @dev Emitted when a {pendingDefaultAdminDelay} is reset if its schedule didn't pass.
         */
        event DefaultAdminDelayChangeCanceled();
    
        /**
         * @dev Returns the address of the current `DEFAULT_ADMIN_ROLE` holder.
         */
        function defaultAdmin() external view returns (address);
    
        /**
         * @dev Returns a tuple of a `newAdmin` and an accept schedule.
         *
         * After the `schedule` passes, the `newAdmin` will be able to accept the {defaultAdmin} role
         * by calling {acceptDefaultAdminTransfer}, completing the role transfer.
         *
         * A zero value only in `acceptSchedule` indicates no pending admin transfer.
         *
         * NOTE: A zero address `newAdmin` means that {defaultAdmin} is being renounced.
         */
        function pendingDefaultAdmin() external view returns (address newAdmin, uint48 acceptSchedule);
    
        /**
         * @dev Returns the delay required to schedule the acceptance of a {defaultAdmin} transfer started.
         *
         * This delay will be added to the current timestamp when calling {beginDefaultAdminTransfer} to set
         * the acceptance schedule.
         *
         * NOTE: If a delay change has been scheduled, it will take effect as soon as the schedule passes, making this
         * function returns the new delay. See {changeDefaultAdminDelay}.
         */
        function defaultAdminDelay() external view returns (uint48);
    
        /**
         * @dev Returns a tuple of `newDelay` and an effect schedule.
         *
         * After the `schedule` passes, the `newDelay` will get into effect immediately for every
         * new {defaultAdmin} transfer started with {beginDefaultAdminTransfer}.
         *
         * A zero value only in `effectSchedule` indicates no pending delay change.
         *
         * NOTE: A zero value only for `newDelay` means that the next {defaultAdminDelay}
         * will be zero after the effect schedule.
         */
        function pendingDefaultAdminDelay() external view returns (uint48 newDelay, uint48 effectSchedule);
    
        /**
         * @dev Starts a {defaultAdmin} transfer by setting a {pendingDefaultAdmin} scheduled for acceptance
         * after the current timestamp plus a {defaultAdminDelay}.
         *
         * Requirements:
         *
         * - Only can be called by the current {defaultAdmin}.
         *
         * Emits a DefaultAdminRoleChangeStarted event.
         */
        function beginDefaultAdminTransfer(address newAdmin) external;
    
        /**
         * @dev Cancels a {defaultAdmin} transfer previously started with {beginDefaultAdminTransfer}.
         *
         * A {pendingDefaultAdmin} not yet accepted can also be cancelled with this function.
         *
         * Requirements:
         *
         * - Only can be called by the current {defaultAdmin}.
         *
         * May emit a DefaultAdminTransferCanceled event.
         */
        function cancelDefaultAdminTransfer() external;
    
        /**
         * @dev Completes a {defaultAdmin} transfer previously started with {beginDefaultAdminTransfer}.
         *
         * After calling the function:
         *
         * - `DEFAULT_ADMIN_ROLE` should be granted to the caller.
         * - `DEFAULT_ADMIN_ROLE` should be revoked from the previous holder.
         * - {pendingDefaultAdmin} should be reset to zero values.
         *
         * Requirements:
         *
         * - Only can be called by the {pendingDefaultAdmin}'s `newAdmin`.
         * - The {pendingDefaultAdmin}'s `acceptSchedule` should've passed.
         */
        function acceptDefaultAdminTransfer() external;
    
        /**
         * @dev Initiates a {defaultAdminDelay} update by setting a {pendingDefaultAdminDelay} scheduled for getting
         * into effect after the current timestamp plus a {defaultAdminDelay}.
         *
         * This function guarantees that any call to {beginDefaultAdminTransfer} done between the timestamp this
         * method is called and the {pendingDefaultAdminDelay} effect schedule will use the current {defaultAdminDelay}
         * set before calling.
         *
         * The {pendingDefaultAdminDelay}'s effect schedule is defined in a way that waiting until the schedule and then
         * calling {beginDefaultAdminTransfer} with the new delay will take at least the same as another {defaultAdmin}
         * complete transfer (including acceptance).
         *
         * The schedule is designed for two scenarios:
         *
         * - When the delay is changed for a larger one the schedule is `block.timestamp + newDelay` capped by
         * {defaultAdminDelayIncreaseWait}.
         * - When the delay is changed for a shorter one, the schedule is `block.timestamp + (current delay - new delay)`.
         *
         * A {pendingDefaultAdminDelay} that never got into effect will be canceled in favor of a new scheduled change.
         *
         * Requirements:
         *
         * - Only can be called by the current {defaultAdmin}.
         *
         * Emits a DefaultAdminDelayChangeScheduled event and may emit a DefaultAdminDelayChangeCanceled event.
         */
        function changeDefaultAdminDelay(uint48 newDelay) external;
    
        /**
         * @dev Cancels a scheduled {defaultAdminDelay} change.
         *
         * Requirements:
         *
         * - Only can be called by the current {defaultAdmin}.
         *
         * May emit a DefaultAdminDelayChangeCanceled event.
         */
        function rollbackDefaultAdminDelay() external;
    
        /**
         * @dev Maximum time in seconds for an increase to {defaultAdminDelay} (that is scheduled using {changeDefaultAdminDelay})
         * to take effect. Default to 5 days.
         *
         * When the {defaultAdminDelay} is scheduled to be increased, it goes into effect after the new delay has passed with
         * the purpose of giving enough time for reverting any accidental change (i.e. using milliseconds instead of seconds)
         * that may lock the contract. However, to avoid excessive schedules, the wait is capped by this function and it can
         * be overrode for a custom {defaultAdminDelay} increase scheduling.
         *
         * IMPORTANT: Make sure to add a reasonable amount of time while overriding this value, otherwise,
         * there's a risk of setting a high new delay that goes into effect almost immediately without the
         * possibility of human intervention in the case of an input error (eg. set milliseconds instead of seconds).
         */
        function defaultAdminDelayIncreaseWait() external view returns (uint48);
    }

    <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (access/AccessControl.sol)
    
    pragma solidity ^0.8.20;
    
    import {IAccessControl} from "./IAccessControl.sol";
    import {Context} from "../utils/Context.sol";
    import {ERC165} from "../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:
     *
     * ```solidity
     * 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}:
     *
     * ```solidity
     * 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. We recommend using {AccessControlDefaultAdminRules}
     * to enforce additional security measures for this role.
     */
    abstract contract AccessControl is Context, IAccessControl, ERC165 {
        struct RoleData {
            mapping(address account => bool) hasRole;
            bytes32 adminRole;
        }
    
        mapping(bytes32 role => RoleData) private _roles;
    
        bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
    
        /**
         * @dev Modifier that checks that an account has a specific role. Reverts
         * with an {AccessControlUnauthorizedAccount} error including the required role.
         */
        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 returns (bool) {
            return _roles[role].hasRole[account];
        }
    
        /**
         * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `_msgSender()`
         * is missing `role`. Overriding this function changes the behavior of the {onlyRole} modifier.
         */
        function _checkRole(bytes32 role) internal view virtual {
            _checkRole(role, _msgSender());
        }
    
        /**
         * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `account`
         * is missing `role`.
         */
        function _checkRole(bytes32 role, address account) internal view virtual {
            if (!hasRole(role, account)) {
                revert AccessControlUnauthorizedAccount(account, role);
            }
        }
    
        /**
         * @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 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 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 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 `callerConfirmation`.
         *
         * May emit a {RoleRevoked} event.
         */
        function renounceRole(bytes32 role, address callerConfirmation) public virtual {
            if (callerConfirmation != _msgSender()) {
                revert AccessControlBadConfirmation();
            }
    
            _revokeRole(role, callerConfirmation);
        }
    
        /**
         * @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 Attempts to grant `role` to `account` and returns a boolean indicating if `role` was granted.
         *
         * Internal function without access restriction.
         *
         * May emit a {RoleGranted} event.
         */
        function _grantRole(bytes32 role, address account) internal virtual returns (bool) {
            if (!hasRole(role, account)) {
                _roles[role].hasRole[account] = true;
                emit RoleGranted(role, account, _msgSender());
                return true;
            } else {
                return false;
            }
        }
    
        /**
         * @dev Attempts to revoke `role` to `account` and returns a boolean indicating if `role` was revoked.
         *
         * Internal function without access restriction.
         *
         * May emit a {RoleRevoked} event.
         */
        function _revokeRole(bytes32 role, address account) internal virtual returns (bool) {
            if (hasRole(role, account)) {
                _roles[role].hasRole[account] = false;
                emit RoleRevoked(role, account, _msgSender());
                return true;
            } else {
                return false;
            }
        }
    }

    <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (utils/math/SafeCast.sol)
    // This file was procedurally generated from scripts/generate/templates/SafeCast.js.
    
    pragma solidity ^0.8.20;
    
    /**
     * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow
     * checks.
     *
     * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
     * easily result in undesired exploitation or bugs, since developers usually
     * assume that overflows raise errors. `SafeCast` restores this intuition by
     * reverting the transaction when such an operation overflows.
     *
     * Using this library instead of the unchecked operations eliminates an entire
     * class of bugs, so it's recommended to use it always.
     */
    library SafeCast {
        /**
         * @dev Value doesn't fit in an uint of `bits` size.
         */
        error SafeCastOverflowedUintDowncast(uint8 bits, uint256 value);
    
        /**
         * @dev An int value doesn't fit in an uint of `bits` size.
         */
        error SafeCastOverflowedIntToUint(int256 value);
    
        /**
         * @dev Value doesn't fit in an int of `bits` size.
         */
        error SafeCastOverflowedIntDowncast(uint8 bits, int256 value);
    
        /**
         * @dev An uint value doesn't fit in an int of `bits` size.
         */
        error SafeCastOverflowedUintToInt(uint256 value);
    
        /**
         * @dev Returns the downcasted uint248 from uint256, reverting on
         * overflow (when the input is greater than largest uint248).
         *
         * Counterpart to Solidity's `uint248` operator.
         *
         * Requirements:
         *
         * - input must fit into 248 bits
         */
        function toUint248(uint256 value) internal pure returns (uint248) {
            if (value > type(uint248).max) {
                revert SafeCastOverflowedUintDowncast(248, value);
            }
            return uint248(value);
        }
    
        /**
         * @dev Returns the downcasted uint240 from uint256, reverting on
         * overflow (when the input is greater than largest uint240).
         *
         * Counterpart to Solidity's `uint240` operator.
         *
         * Requirements:
         *
         * - input must fit into 240 bits
         */
        function toUint240(uint256 value) internal pure returns (uint240) {
            if (value > type(uint240).max) {
                revert SafeCastOverflowedUintDowncast(240, value);
            }
            return uint240(value);
        }
    
        /**
         * @dev Returns the downcasted uint232 from uint256, reverting on
         * overflow (when the input is greater than largest uint232).
         *
         * Counterpart to Solidity's `uint232` operator.
         *
         * Requirements:
         *
         * - input must fit into 232 bits
         */
        function toUint232(uint256 value) internal pure returns (uint232) {
            if (value > type(uint232).max) {
                revert SafeCastOverflowedUintDowncast(232, value);
            }
            return uint232(value);
        }
    
        /**
         * @dev Returns the downcasted uint224 from uint256, reverting on
         * overflow (when the input is greater than largest uint224).
         *
         * Counterpart to Solidity's `uint224` operator.
         *
         * Requirements:
         *
         * - input must fit into 224 bits
         */
        function toUint224(uint256 value) internal pure returns (uint224) {
            if (value > type(uint224).max) {
                revert SafeCastOverflowedUintDowncast(224, value);
            }
            return uint224(value);
        }
    
        /**
         * @dev Returns the downcasted uint216 from uint256, reverting on
         * overflow (when the input is greater than largest uint216).
         *
         * Counterpart to Solidity's `uint216` operator.
         *
         * Requirements:
         *
         * - input must fit into 216 bits
         */
        function toUint216(uint256 value) internal pure returns (uint216) {
            if (value > type(uint216).max) {
                revert SafeCastOverflowedUintDowncast(216, value);
            }
            return uint216(value);
        }
    
        /**
         * @dev Returns the downcasted uint208 from uint256, reverting on
         * overflow (when the input is greater than largest uint208).
         *
         * Counterpart to Solidity's `uint208` operator.
         *
         * Requirements:
         *
         * - input must fit into 208 bits
         */
        function toUint208(uint256 value) internal pure returns (uint208) {
            if (value > type(uint208).max) {
                revert SafeCastOverflowedUintDowncast(208, value);
            }
            return uint208(value);
        }
    
        /**
         * @dev Returns the downcasted uint200 from uint256, reverting on
         * overflow (when the input is greater than largest uint200).
         *
         * Counterpart to Solidity's `uint200` operator.
         *
         * Requirements:
         *
         * - input must fit into 200 bits
         */
        function toUint200(uint256 value) internal pure returns (uint200) {
            if (value > type(uint200).max) {
                revert SafeCastOverflowedUintDowncast(200, value);
            }
            return uint200(value);
        }
    
        /**
         * @dev Returns the downcasted uint192 from uint256, reverting on
         * overflow (when the input is greater than largest uint192).
         *
         * Counterpart to Solidity's `uint192` operator.
         *
         * Requirements:
         *
         * - input must fit into 192 bits
         */
        function toUint192(uint256 value) internal pure returns (uint192) {
            if (value > type(uint192).max) {
                revert SafeCastOverflowedUintDowncast(192, value);
            }
            return uint192(value);
        }
    
        /**
         * @dev Returns the downcasted uint184 from uint256, reverting on
         * overflow (when the input is greater than largest uint184).
         *
         * Counterpart to Solidity's `uint184` operator.
         *
         * Requirements:
         *
         * - input must fit into 184 bits
         */
        function toUint184(uint256 value) internal pure returns (uint184) {
            if (value > type(uint184).max) {
                revert SafeCastOverflowedUintDowncast(184, value);
            }
            return uint184(value);
        }
    
        /**
         * @dev Returns the downcasted uint176 from uint256, reverting on
         * overflow (when the input is greater than largest uint176).
         *
         * Counterpart to Solidity's `uint176` operator.
         *
         * Requirements:
         *
         * - input must fit into 176 bits
         */
        function toUint176(uint256 value) internal pure returns (uint176) {
            if (value > type(uint176).max) {
                revert SafeCastOverflowedUintDowncast(176, value);
            }
            return uint176(value);
        }
    
        /**
         * @dev Returns the downcasted uint168 from uint256, reverting on
         * overflow (when the input is greater than largest uint168).
         *
         * Counterpart to Solidity's `uint168` operator.
         *
         * Requirements:
         *
         * - input must fit into 168 bits
         */
        function toUint168(uint256 value) internal pure returns (uint168) {
            if (value > type(uint168).max) {
                revert SafeCastOverflowedUintDowncast(168, value);
            }
            return uint168(value);
        }
    
        /**
         * @dev Returns the downcasted uint160 from uint256, reverting on
         * overflow (when the input is greater than largest uint160).
         *
         * Counterpart to Solidity's `uint160` operator.
         *
         * Requirements:
         *
         * - input must fit into 160 bits
         */
        function toUint160(uint256 value) internal pure returns (uint160) {
            if (value > type(uint160).max) {
                revert SafeCastOverflowedUintDowncast(160, value);
            }
            return uint160(value);
        }
    
        /**
         * @dev Returns the downcasted uint152 from uint256, reverting on
         * overflow (when the input is greater than largest uint152).
         *
         * Counterpart to Solidity's `uint152` operator.
         *
         * Requirements:
         *
         * - input must fit into 152 bits
         */
        function toUint152(uint256 value) internal pure returns (uint152) {
            if (value > type(uint152).max) {
                revert SafeCastOverflowedUintDowncast(152, value);
            }
            return uint152(value);
        }
    
        /**
         * @dev Returns the downcasted uint144 from uint256, reverting on
         * overflow (when the input is greater than largest uint144).
         *
         * Counterpart to Solidity's `uint144` operator.
         *
         * Requirements:
         *
         * - input must fit into 144 bits
         */
        function toUint144(uint256 value) internal pure returns (uint144) {
            if (value > type(uint144).max) {
                revert SafeCastOverflowedUintDowncast(144, value);
            }
            return uint144(value);
        }
    
        /**
         * @dev Returns the downcasted uint136 from uint256, reverting on
         * overflow (when the input is greater than largest uint136).
         *
         * Counterpart to Solidity's `uint136` operator.
         *
         * Requirements:
         *
         * - input must fit into 136 bits
         */
        function toUint136(uint256 value) internal pure returns (uint136) {
            if (value > type(uint136).max) {
                revert SafeCastOverflowedUintDowncast(136, value);
            }
            return uint136(value);
        }
    
        /**
         * @dev Returns the downcasted uint128 from uint256, reverting on
         * overflow (when the input is greater than largest uint128).
         *
         * Counterpart to Solidity's `uint128` operator.
         *
         * Requirements:
         *
         * - input must fit into 128 bits
         */
        function toUint128(uint256 value) internal pure returns (uint128) {
            if (value > type(uint128).max) {
                revert SafeCastOverflowedUintDowncast(128, value);
            }
            return uint128(value);
        }
    
        /**
         * @dev Returns the downcasted uint120 from uint256, reverting on
         * overflow (when the input is greater than largest uint120).
         *
         * Counterpart to Solidity's `uint120` operator.
         *
         * Requirements:
         *
         * - input must fit into 120 bits
         */
        function toUint120(uint256 value) internal pure returns (uint120) {
            if (value > type(uint120).max) {
                revert SafeCastOverflowedUintDowncast(120, value);
            }
            return uint120(value);
        }
    
        /**
         * @dev Returns the downcasted uint112 from uint256, reverting on
         * overflow (when the input is greater than largest uint112).
         *
         * Counterpart to Solidity's `uint112` operator.
         *
         * Requirements:
         *
         * - input must fit into 112 bits
         */
        function toUint112(uint256 value) internal pure returns (uint112) {
            if (value > type(uint112).max) {
                revert SafeCastOverflowedUintDowncast(112, value);
            }
            return uint112(value);
        }
    
        /**
         * @dev Returns the downcasted uint104 from uint256, reverting on
         * overflow (when the input is greater than largest uint104).
         *
         * Counterpart to Solidity's `uint104` operator.
         *
         * Requirements:
         *
         * - input must fit into 104 bits
         */
        function toUint104(uint256 value) internal pure returns (uint104) {
            if (value > type(uint104).max) {
                revert SafeCastOverflowedUintDowncast(104, value);
            }
            return uint104(value);
        }
    
        /**
         * @dev Returns the downcasted uint96 from uint256, reverting on
         * overflow (when the input is greater than largest uint96).
         *
         * Counterpart to Solidity's `uint96` operator.
         *
         * Requirements:
         *
         * - input must fit into 96 bits
         */
        function toUint96(uint256 value) internal pure returns (uint96) {
            if (value > type(uint96).max) {
                revert SafeCastOverflowedUintDowncast(96, value);
            }
            return uint96(value);
        }
    
        /**
         * @dev Returns the downcasted uint88 from uint256, reverting on
         * overflow (when the input is greater than largest uint88).
         *
         * Counterpart to Solidity's `uint88` operator.
         *
         * Requirements:
         *
         * - input must fit into 88 bits
         */
        function toUint88(uint256 value) internal pure returns (uint88) {
            if (value > type(uint88).max) {
                revert SafeCastOverflowedUintDowncast(88, value);
            }
            return uint88(value);
        }
    
        /**
         * @dev Returns the downcasted uint80 from uint256, reverting on
         * overflow (when the input is greater than largest uint80).
         *
         * Counterpart to Solidity's `uint80` operator.
         *
         * Requirements:
         *
         * - input must fit into 80 bits
         */
        function toUint80(uint256 value) internal pure returns (uint80) {
            if (value > type(uint80).max) {
                revert SafeCastOverflowedUintDowncast(80, value);
            }
            return uint80(value);
        }
    
        /**
         * @dev Returns the downcasted uint72 from uint256, reverting on
         * overflow (when the input is greater than largest uint72).
         *
         * Counterpart to Solidity's `uint72` operator.
         *
         * Requirements:
         *
         * - input must fit into 72 bits
         */
        function toUint72(uint256 value) internal pure returns (uint72) {
            if (value > type(uint72).max) {
                revert SafeCastOverflowedUintDowncast(72, value);
            }
            return uint72(value);
        }
    
        /**
         * @dev Returns the downcasted uint64 from uint256, reverting on
         * overflow (when the input is greater than largest uint64).
         *
         * Counterpart to Solidity's `uint64` operator.
         *
         * Requirements:
         *
         * - input must fit into 64 bits
         */
        function toUint64(uint256 value) internal pure returns (uint64) {
            if (value > type(uint64).max) {
                revert SafeCastOverflowedUintDowncast(64, value);
            }
            return uint64(value);
        }
    
        /**
         * @dev Returns the downcasted uint56 from uint256, reverting on
         * overflow (when the input is greater than largest uint56).
         *
         * Counterpart to Solidity's `uint56` operator.
         *
         * Requirements:
         *
         * - input must fit into 56 bits
         */
        function toUint56(uint256 value) internal pure returns (uint56) {
            if (value > type(uint56).max) {
                revert SafeCastOverflowedUintDowncast(56, value);
            }
            return uint56(value);
        }
    
        /**
         * @dev Returns the downcasted uint48 from uint256, reverting on
         * overflow (when the input is greater than largest uint48).
         *
         * Counterpart to Solidity's `uint48` operator.
         *
         * Requirements:
         *
         * - input must fit into 48 bits
         */
        function toUint48(uint256 value) internal pure returns (uint48) {
            if (value > type(uint48).max) {
                revert SafeCastOverflowedUintDowncast(48, value);
            }
            return uint48(value);
        }
    
        /**
         * @dev Returns the downcasted uint40 from uint256, reverting on
         * overflow (when the input is greater than largest uint40).
         *
         * Counterpart to Solidity's `uint40` operator.
         *
         * Requirements:
         *
         * - input must fit into 40 bits
         */
        function toUint40(uint256 value) internal pure returns (uint40) {
            if (value > type(uint40).max) {
                revert SafeCastOverflowedUintDowncast(40, value);
            }
            return uint40(value);
        }
    
        /**
         * @dev Returns the downcasted uint32 from uint256, reverting on
         * overflow (when the input is greater than largest uint32).
         *
         * Counterpart to Solidity's `uint32` operator.
         *
         * Requirements:
         *
         * - input must fit into 32 bits
         */
        function toUint32(uint256 value) internal pure returns (uint32) {
            if (value > type(uint32).max) {
                revert SafeCastOverflowedUintDowncast(32, value);
            }
            return uint32(value);
        }
    
        /**
         * @dev Returns the downcasted uint24 from uint256, reverting on
         * overflow (when the input is greater than largest uint24).
         *
         * Counterpart to Solidity's `uint24` operator.
         *
         * Requirements:
         *
         * - input must fit into 24 bits
         */
        function toUint24(uint256 value) internal pure returns (uint24) {
            if (value > type(uint24).max) {
                revert SafeCastOverflowedUintDowncast(24, value);
            }
            return uint24(value);
        }
    
        /**
         * @dev Returns the downcasted uint16 from uint256, reverting on
         * overflow (when the input is greater than largest uint16).
         *
         * Counterpart to Solidity's `uint16` operator.
         *
         * Requirements:
         *
         * - input must fit into 16 bits
         */
        function toUint16(uint256 value) internal pure returns (uint16) {
            if (value > type(uint16).max) {
                revert SafeCastOverflowedUintDowncast(16, value);
            }
            return uint16(value);
        }
    
        /**
         * @dev Returns the downcasted uint8 from uint256, reverting on
         * overflow (when the input is greater than largest uint8).
         *
         * Counterpart to Solidity's `uint8` operator.
         *
         * Requirements:
         *
         * - input must fit into 8 bits
         */
        function toUint8(uint256 value) internal pure returns (uint8) {
            if (value > type(uint8).max) {
                revert SafeCastOverflowedUintDowncast(8, value);
            }
            return uint8(value);
        }
    
        /**
         * @dev Converts a signed int256 into an unsigned uint256.
         *
         * Requirements:
         *
         * - input must be greater than or equal to 0.
         */
        function toUint256(int256 value) internal pure returns (uint256) {
            if (value < 0) {
                revert SafeCastOverflowedIntToUint(value);
            }
            return uint256(value);
        }
    
        /**
         * @dev Returns the downcasted int248 from int256, reverting on
         * overflow (when the input is less than smallest int248 or
         * greater than largest int248).
         *
         * Counterpart to Solidity's `int248` operator.
         *
         * Requirements:
         *
         * - input must fit into 248 bits
         */
        function toInt248(int256 value) internal pure returns (int248 downcasted) {
            downcasted = int248(value);
            if (downcasted != value) {
                revert SafeCastOverflowedIntDowncast(248, value);
            }
        }
    
        /**
         * @dev Returns the downcasted int240 from int256, reverting on
         * overflow (when the input is less than smallest int240 or
         * greater than largest int240).
         *
         * Counterpart to Solidity's `int240` operator.
         *
         * Requirements:
         *
         * - input must fit into 240 bits
         */
        function toInt240(int256 value) internal pure returns (int240 downcasted) {
            downcasted = int240(value);
            if (downcasted != value) {
                revert SafeCastOverflowedIntDowncast(240, value);
            }
        }
    
        /**
         * @dev Returns the downcasted int232 from int256, reverting on
         * overflow (when the input is less than smallest int232 or
         * greater than largest int232).
         *
         * Counterpart to Solidity's `int232` operator.
         *
         * Requirements:
         *
         * - input must fit into 232 bits
         */
        function toInt232(int256 value) internal pure returns (int232 downcasted) {
            downcasted = int232(value);
            if (downcasted != value) {
                revert SafeCastOverflowedIntDowncast(232, value);
            }
        }
    
        /**
         * @dev Returns the downcasted int224 from int256, reverting on
         * overflow (when the input is less than smallest int224 or
         * greater than largest int224).
         *
         * Counterpart to Solidity's `int224` operator.
         *
         * Requirements:
         *
         * - input must fit into 224 bits
         */
        function toInt224(int256 value) internal pure returns (int224 downcasted) {
            downcasted = int224(value);
            if (downcasted != value) {
                revert SafeCastOverflowedIntDowncast(224, value);
            }
        }
    
        /**
         * @dev Returns the downcasted int216 from int256, reverting on
         * overflow (when the input is less than smallest int216 or
         * greater than largest int216).
         *
         * Counterpart to Solidity's `int216` operator.
         *
         * Requirements:
         *
         * - input must fit into 216 bits
         */
        function toInt216(int256 value) internal pure returns (int216 downcasted) {
            downcasted = int216(value);
            if (downcasted != value) {
                revert SafeCastOverflowedIntDowncast(216, value);
            }
        }
    
        /**
         * @dev Returns the downcasted int208 from int256, reverting on
         * overflow (when the input is less than smallest int208 or
         * greater than largest int208).
         *
         * Counterpart to Solidity's `int208` operator.
         *
         * Requirements:
         *
         * - input must fit into 208 bits
         */
        function toInt208(int256 value) internal pure returns (int208 downcasted) {
            downcasted = int208(value);
            if (downcasted != value) {
                revert SafeCastOverflowedIntDowncast(208, value);
            }
        }
    
        /**
         * @dev Returns the downcasted int200 from int256, reverting on
         * overflow (when the input is less than smallest int200 or
         * greater than largest int200).
         *
         * Counterpart to Solidity's `int200` operator.
         *
         * Requirements:
         *
         * - input must fit into 200 bits
         */
        function toInt200(int256 value) internal pure returns (int200 downcasted) {
            downcasted = int200(value);
            if (downcasted != value) {
                revert SafeCastOverflowedIntDowncast(200, value);
            }
        }
    
        /**
         * @dev Returns the downcasted int192 from int256, reverting on
         * overflow (when the input is less than smallest int192 or
         * greater than largest int192).
         *
         * Counterpart to Solidity's `int192` operator.
         *
         * Requirements:
         *
         * - input must fit into 192 bits
         */
        function toInt192(int256 value) internal pure returns (int192 downcasted) {
            downcasted = int192(value);
            if (downcasted != value) {
                revert SafeCastOverflowedIntDowncast(192, value);
            }
        }
    
        /**
         * @dev Returns the downcasted int184 from int256, reverting on
         * overflow (when the input is less than smallest int184 or
         * greater than largest int184).
         *
         * Counterpart to Solidity's `int184` operator.
         *
         * Requirements:
         *
         * - input must fit into 184 bits
         */
        function toInt184(int256 value) internal pure returns (int184 downcasted) {
            downcasted = int184(value);
            if (downcasted != value) {
                revert SafeCastOverflowedIntDowncast(184, value);
            }
        }
    
        /**
         * @dev Returns the downcasted int176 from int256, reverting on
         * overflow (when the input is less than smallest int176 or
         * greater than largest int176).
         *
         * Counterpart to Solidity's `int176` operator.
         *
         * Requirements:
         *
         * - input must fit into 176 bits
         */
        function toInt176(int256 value) internal pure returns (int176 downcasted) {
            downcasted = int176(value);
            if (downcasted != value) {
                revert SafeCastOverflowedIntDowncast(176, value);
            }
        }
    
        /**
         * @dev Returns the downcasted int168 from int256, reverting on
         * overflow (when the input is less than smallest int168 or
         * greater than largest int168).
         *
         * Counterpart to Solidity's `int168` operator.
         *
         * Requirements:
         *
         * - input must fit into 168 bits
         */
        function toInt168(int256 value) internal pure returns (int168 downcasted) {
            downcasted = int168(value);
            if (downcasted != value) {
                revert SafeCastOverflowedIntDowncast(168, value);
            }
        }
    
        /**
         * @dev Returns the downcasted int160 from int256, reverting on
         * overflow (when the input is less than smallest int160 or
         * greater than largest int160).
         *
         * Counterpart to Solidity's `int160` operator.
         *
         * Requirements:
         *
         * - input must fit into 160 bits
         */
        function toInt160(int256 value) internal pure returns (int160 downcasted) {
            downcasted = int160(value);
            if (downcasted != value) {
                revert SafeCastOverflowedIntDowncast(160, value);
            }
        }
    
        /**
         * @dev Returns the downcasted int152 from int256, reverting on
         * overflow (when the input is less than smallest int152 or
         * greater than largest int152).
         *
         * Counterpart to Solidity's `int152` operator.
         *
         * Requirements:
         *
         * - input must fit into 152 bits
         */
        function toInt152(int256 value) internal pure returns (int152 downcasted) {
            downcasted = int152(value);
            if (downcasted != value) {
                revert SafeCastOverflowedIntDowncast(152, value);
            }
        }
    
        /**
         * @dev Returns the downcasted int144 from int256, reverting on
         * overflow (when the input is less than smallest int144 or
         * greater than largest int144).
         *
         * Counterpart to Solidity's `int144` operator.
         *
         * Requirements:
         *
         * - input must fit into 144 bits
         */
        function toInt144(int256 value) internal pure returns (int144 downcasted) {
            downcasted = int144(value);
            if (downcasted != value) {
                revert SafeCastOverflowedIntDowncast(144, value);
            }
        }
    
        /**
         * @dev Returns the downcasted int136 from int256, reverting on
         * overflow (when the input is less than smallest int136 or
         * greater than largest int136).
         *
         * Counterpart to Solidity's `int136` operator.
         *
         * Requirements:
         *
         * - input must fit into 136 bits
         */
        function toInt136(int256 value) internal pure returns (int136 downcasted) {
            downcasted = int136(value);
            if (downcasted != value) {
                revert SafeCastOverflowedIntDowncast(136, value);
            }
        }
    
        /**
         * @dev Returns the downcasted int128 from int256, reverting on
         * overflow (when the input is less than smallest int128 or
         * greater than largest int128).
         *
         * Counterpart to Solidity's `int128` operator.
         *
         * Requirements:
         *
         * - input must fit into 128 bits
         */
        function toInt128(int256 value) internal pure returns (int128 downcasted) {
            downcasted = int128(value);
            if (downcasted != value) {
                revert SafeCastOverflowedIntDowncast(128, value);
            }
        }
    
        /**
         * @dev Returns the downcasted int120 from int256, reverting on
         * overflow (when the input is less than smallest int120 or
         * greater than largest int120).
         *
         * Counterpart to Solidity's `int120` operator.
         *
         * Requirements:
         *
         * - input must fit into 120 bits
         */
        function toInt120(int256 value) internal pure returns (int120 downcasted) {
            downcasted = int120(value);
            if (downcasted != value) {
                revert SafeCastOverflowedIntDowncast(120, value);
            }
        }
    
        /**
         * @dev Returns the downcasted int112 from int256, reverting on
         * overflow (when the input is less than smallest int112 or
         * greater than largest int112).
         *
         * Counterpart to Solidity's `int112` operator.
         *
         * Requirements:
         *
         * - input must fit into 112 bits
         */
        function toInt112(int256 value) internal pure returns (int112 downcasted) {
            downcasted = int112(value);
            if (downcasted != value) {
                revert SafeCastOverflowedIntDowncast(112, value);
            }
        }
    
        /**
         * @dev Returns the downcasted int104 from int256, reverting on
         * overflow (when the input is less than smallest int104 or
         * greater than largest int104).
         *
         * Counterpart to Solidity's `int104` operator.
         *
         * Requirements:
         *
         * - input must fit into 104 bits
         */
        function toInt104(int256 value) internal pure returns (int104 downcasted) {
            downcasted = int104(value);
            if (downcasted != value) {
                revert SafeCastOverflowedIntDowncast(104, value);
            }
        }
    
        /**
         * @dev Returns the downcasted int96 from int256, reverting on
         * overflow (when the input is less than smallest int96 or
         * greater than largest int96).
         *
         * Counterpart to Solidity's `int96` operator.
         *
         * Requirements:
         *
         * - input must fit into 96 bits
         */
        function toInt96(int256 value) internal pure returns (int96 downcasted) {
            downcasted = int96(value);
            if (downcasted != value) {
                revert SafeCastOverflowedIntDowncast(96, value);
            }
        }
    
        /**
         * @dev Returns the downcasted int88 from int256, reverting on
         * overflow (when the input is less than smallest int88 or
         * greater than largest int88).
         *
         * Counterpart to Solidity's `int88` operator.
         *
         * Requirements:
         *
         * - input must fit into 88 bits
         */
        function toInt88(int256 value) internal pure returns (int88 downcasted) {
            downcasted = int88(value);
            if (downcasted != value) {
                revert SafeCastOverflowedIntDowncast(88, value);
            }
        }
    
        /**
         * @dev Returns the downcasted int80 from int256, reverting on
         * overflow (when the input is less than smallest int80 or
         * greater than largest int80).
         *
         * Counterpart to Solidity's `int80` operator.
         *
         * Requirements:
         *
         * - input must fit into 80 bits
         */
        function toInt80(int256 value) internal pure returns (int80 downcasted) {
            downcasted = int80(value);
            if (downcasted != value) {
                revert SafeCastOverflowedIntDowncast(80, value);
            }
        }
    
        /**
         * @dev Returns the downcasted int72 from int256, reverting on
         * overflow (when the input is less than smallest int72 or
         * greater than largest int72).
         *
         * Counterpart to Solidity's `int72` operator.
         *
         * Requirements:
         *
         * - input must fit into 72 bits
         */
        function toInt72(int256 value) internal pure returns (int72 downcasted) {
            downcasted = int72(value);
            if (downcasted != value) {
                revert SafeCastOverflowedIntDowncast(72, value);
            }
        }
    
        /**
         * @dev Returns the downcasted int64 from int256, reverting on
         * overflow (when the input is less than smallest int64 or
         * greater than largest int64).
         *
         * Counterpart to Solidity's `int64` operator.
         *
         * Requirements:
         *
         * - input must fit into 64 bits
         */
        function toInt64(int256 value) internal pure returns (int64 downcasted) {
            downcasted = int64(value);
            if (downcasted != value) {
                revert SafeCastOverflowedIntDowncast(64, value);
            }
        }
    
        /**
         * @dev Returns the downcasted int56 from int256, reverting on
         * overflow (when the input is less than smallest int56 or
         * greater than largest int56).
         *
         * Counterpart to Solidity's `int56` operator.
         *
         * Requirements:
         *
         * - input must fit into 56 bits
         */
        function toInt56(int256 value) internal pure returns (int56 downcasted) {
            downcasted = int56(value);
            if (downcasted != value) {
                revert SafeCastOverflowedIntDowncast(56, value);
            }
        }
    
        /**
         * @dev Returns the downcasted int48 from int256, reverting on
         * overflow (when the input is less than smallest int48 or
         * greater than largest int48).
         *
         * Counterpart to Solidity's `int48` operator.
         *
         * Requirements:
         *
         * - input must fit into 48 bits
         */
        function toInt48(int256 value) internal pure returns (int48 downcasted) {
            downcasted = int48(value);
            if (downcasted != value) {
                revert SafeCastOverflowedIntDowncast(48, value);
            }
        }
    
        /**
         * @dev Returns the downcasted int40 from int256, reverting on
         * overflow (when the input is less than smallest int40 or
         * greater than largest int40).
         *
         * Counterpart to Solidity's `int40` operator.
         *
         * Requirements:
         *
         * - input must fit into 40 bits
         */
        function toInt40(int256 value) internal pure returns (int40 downcasted) {
            downcasted = int40(value);
            if (downcasted != value) {
                revert SafeCastOverflowedIntDowncast(40, value);
            }
        }
    
        /**
         * @dev Returns the downcasted int32 from int256, reverting on
         * overflow (when the input is less than smallest int32 or
         * greater than largest int32).
         *
         * Counterpart to Solidity's `int32` operator.
         *
         * Requirements:
         *
         * - input must fit into 32 bits
         */
        function toInt32(int256 value) internal pure returns (int32 downcasted) {
            downcasted = int32(value);
            if (downcasted != value) {
                revert SafeCastOverflowedIntDowncast(32, value);
            }
        }
    
        /**
         * @dev Returns the downcasted int24 from int256, reverting on
         * overflow (when the input is less than smallest int24 or
         * greater than largest int24).
         *
         * Counterpart to Solidity's `int24` operator.
         *
         * Requirements:
         *
         * - input must fit into 24 bits
         */
        function toInt24(int256 value) internal pure returns (int24 downcasted) {
            downcasted = int24(value);
            if (downcasted != value) {
                revert SafeCastOverflowedIntDowncast(24, value);
            }
        }
    
        /**
         * @dev Returns the downcasted int16 from int256, reverting on
         * overflow (when the input is less than smallest int16 or
         * greater than largest int16).
         *
         * Counterpart to Solidity's `int16` operator.
         *
         * Requirements:
         *
         * - input must fit into 16 bits
         */
        function toInt16(int256 value) internal pure returns (int16 downcasted) {
            downcasted = int16(value);
            if (downcasted != value) {
                revert SafeCastOverflowedIntDowncast(16, value);
            }
        }
    
        /**
         * @dev Returns the downcasted int8 from int256, reverting on
         * overflow (when the input is less than smallest int8 or
         * greater than largest int8).
         *
         * Counterpart to Solidity's `int8` operator.
         *
         * Requirements:
         *
         * - input must fit into 8 bits
         */
        function toInt8(int256 value) internal pure returns (int8 downcasted) {
            downcasted = int8(value);
            if (downcasted != value) {
                revert SafeCastOverflowedIntDowncast(8, value);
            }
        }
    
        /**
         * @dev Converts an unsigned uint256 into a signed int256.
         *
         * Requirements:
         *
         * - input must be less than or equal to maxInt256.
         */
        function toInt256(uint256 value) internal pure returns (int256) {
            // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
            if (value > uint256(type(int256).max)) {
                revert SafeCastOverflowedUintToInt(value);
            }
            return int256(value);
        }
    }

    <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC5313.sol)
    
    pragma solidity ^0.8.20;
    
    /**
     * @dev Interface for the Light Contract Ownership Standard.
     *
     * A standardized minimal interface required to identify an account that controls a contract
     */
    interface IERC5313 {
        /**
         * @dev Gets the address of the owner.
         */
        function owner() external view returns (address);
    }

    <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)
    
    pragma solidity ^0.8.20;
    
    /**
     * @dev Interface of the ERC20 standard as defined in the EIP.
     */
    interface IERC20 {
        /**
         * @dev Emitted when `value` tokens are moved from one account (`from`) to
         * another (`to`).
         *
         * Note that `value` may be zero.
         */
        event Transfer(address indexed from, address indexed to, uint256 value);
    
        /**
         * @dev Emitted when the allowance of a `spender` for an `owner` is set by
         * a call to {approve}. `value` is the new allowance.
         */
        event Approval(address indexed owner, address indexed spender, uint256 value);
    
        /**
         * @dev Returns the value of tokens in existence.
         */
        function totalSupply() external view returns (uint256);
    
        /**
         * @dev Returns the value of tokens owned by `account`.
         */
        function balanceOf(address account) external view returns (uint256);
    
        /**
         * @dev Moves a `value` amount of tokens from the caller's account to `to`.
         *
         * Returns a boolean value indicating whether the operation succeeded.
         *
         * Emits a {Transfer} event.
         */
        function transfer(address to, uint256 value) external returns (bool);
    
        /**
         * @dev Returns the remaining number of tokens that `spender` will be
         * allowed to spend on behalf of `owner` through {transferFrom}. This is
         * zero by default.
         *
         * This value changes when {approve} or {transferFrom} are called.
         */
        function allowance(address owner, address spender) external view returns (uint256);
    
        /**
         * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
         * caller's tokens.
         *
         * Returns a boolean value indicating whether the operation succeeded.
         *
         * IMPORTANT: Beware that changing an allowance with this method brings the risk
         * that someone may use both the old and the new allowance by unfortunate
         * transaction ordering. One possible solution to mitigate this race
         * condition is to first reduce the spender's allowance to 0 and set the
         * desired value afterwards:
         * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
         *
         * Emits an {Approval} event.
         */
        function approve(address spender, uint256 value) external returns (bool);
    
        /**
         * @dev Moves a `value` amount of tokens from `from` to `to` using the
         * allowance mechanism. `value` is then deducted from the caller's
         * allowance.
         *
         * Returns a boolean value indicating whether the operation succeeded.
         *
         * Emits a {Transfer} event.
         */
        function transferFrom(address from, address to, uint256 value) external returns (bool);
    }

    <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol)
    
    pragma solidity ^0.8.20;
    
    /**
     * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
     * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
     *
     * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
     * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
     * need to send a transaction, and thus is not required to hold Ether at all.
     *
     * ==== Security Considerations
     *
     * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
     * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
     * considered as an intention to spend the allowance in any specific way. The second is that because permits have
     * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
     * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
     * generally recommended is:
     *
     * ```solidity
     * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
     *     try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
     *     doThing(..., value);
     * }
     *
     * function doThing(..., uint256 value) public {
     *     token.safeTransferFrom(msg.sender, address(this), value);
     *     ...
     * }
     * ```
     *
     * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
     * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
     * {SafeERC20-safeTransferFrom}).
     *
     * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
     * contracts should have entry points that don't rely on permit.
     */
    interface IERC20Permit {
        /**
         * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
         * given ``owner``'s signed approval.
         *
         * IMPORTANT: The same issues {IERC20-approve} has related to transaction
         * ordering also apply here.
         *
         * Emits an {Approval} event.
         *
         * Requirements:
         *
         * - `spender` cannot be the zero address.
         * - `deadline` must be a timestamp in the future.
         * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
         * over the EIP712-formatted function arguments.
         * - the signature must use ``owner``'s current nonce (see {nonces}).
         *
         * For more information on the signature format, see the
         * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
         * section].
         *
         * CAUTION: See Security Considerations above.
         */
        function permit(
            address owner,
            address spender,
            uint256 value,
            uint256 deadline,
            uint8 v,
            bytes32 r,
            bytes32 s
        ) external;
    
        /**
         * @dev Returns the current nonce for `owner`. This value must be
         * included whenever a signature is generated for {permit}.
         *
         * Every successful call to {permit} increases ``owner``'s nonce by one. This
         * prevents a signature from being used multiple times.
         */
        function nonces(address owner) external view returns (uint256);
    
        /**
         * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
         */
        // solhint-disable-next-line func-name-mixedcase
        function DOMAIN_SEPARATOR() external view returns (bytes32);
    }

    <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)
    
    pragma solidity ^0.8.20;
    
    /**
     * @dev Collection of functions related to the address type
     */
    library Address {
        /**
         * @dev The ETH balance of the account is not enough to perform the operation.
         */
        error AddressInsufficientBalance(address account);
    
        /**
         * @dev There's no code at `target` (it is not a contract).
         */
        error AddressEmptyCode(address target);
    
        /**
         * @dev A call to an address target failed. The target may have reverted.
         */
        error FailedInnerCall();
    
        /**
         * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
         * `recipient`, forwarding all available gas and reverting on errors.
         *
         * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
         * of certain opcodes, possibly making contracts go over the 2300 gas limit
         * imposed by `transfer`, making them unable to receive funds via
         * `transfer`. {sendValue} removes this limitation.
         *
         * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
         *
         * IMPORTANT: because control is transferred to `recipient`, care must be
         * taken to not create reentrancy vulnerabilities. Consider using
         * {ReentrancyGuard} or the
         * https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
         */
        function sendValue(address payable recipient, uint256 amount) internal {
            if (address(this).balance < amount) {
                revert AddressInsufficientBalance(address(this));
            }
    
            (bool success, ) = recipient.call{value: amount}("");
            if (!success) {
                revert FailedInnerCall();
            }
        }
    
        /**
         * @dev Performs a Solidity function call using a low level `call`. A
         * plain `call` is an unsafe replacement for a function call: use this
         * function instead.
         *
         * If `target` reverts with a revert reason or custom error, it is bubbled
         * up by this function (like regular Solidity function calls). However, if
         * the call reverted with no returned reason, this function reverts with a
         * {FailedInnerCall} error.
         *
         * Returns the raw returned data. To convert to the expected return value,
         * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
         *
         * Requirements:
         *
         * - `target` must be a contract.
         * - calling `target` with `data` must not revert.
         */
        function functionCall(address target, bytes memory data) internal returns (bytes memory) {
            return functionCallWithValue(target, data, 0);
        }
    
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
         * but also transferring `value` wei to `target`.
         *
         * Requirements:
         *
         * - the calling contract must have an ETH balance of at least `value`.
         * - the called Solidity function must be `payable`.
         */
        function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
            if (address(this).balance < value) {
                revert AddressInsufficientBalance(address(this));
            }
            (bool success, bytes memory returndata) = target.call{value: value}(data);
            return verifyCallResultFromTarget(target, success, returndata);
        }
    
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
         * but performing a static call.
         */
        function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
            (bool success, bytes memory returndata) = target.staticcall(data);
            return verifyCallResultFromTarget(target, success, returndata);
        }
    
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
         * but performing a delegate call.
         */
        function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
            (bool success, bytes memory returndata) = target.delegatecall(data);
            return verifyCallResultFromTarget(target, success, returndata);
        }
    
        /**
         * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
         * was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
         * unsuccessful call.
         */
        function verifyCallResultFromTarget(
            address target,
            bool success,
            bytes memory returndata
        ) internal view returns (bytes memory) {
            if (!success) {
                _revert(returndata);
            } else {
                // only check if target is a contract if the call was successful and the return data is empty
                // otherwise we already know that it was a contract
                if (returndata.length == 0 && target.code.length == 0) {
                    revert AddressEmptyCode(target);
                }
                return returndata;
            }
        }
    
        /**
         * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
         * revert reason or with a default {FailedInnerCall} error.
         */
        function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
            if (!success) {
                _revert(returndata);
            } else {
                return returndata;
            }
        }
    
        /**
         * @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.
         */
        function _revert(bytes memory returndata) private pure {
            // Look for revert reason and bubble it up if present
            if (returndata.length > 0) {
                // The easiest way to bubble the revert reason is using memory via assembly
                /// @solidity memory-safe-assembly
                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert FailedInnerCall();
            }
        }
    }

    <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (access/IAccessControl.sol)
    
    pragma solidity ^0.8.20;
    
    /**
     * @dev External interface of AccessControl declared to support ERC165 detection.
     */
    interface IAccessControl {
        /**
         * @dev The `account` is missing a role.
         */
        error AccessControlUnauthorizedAccount(address account, bytes32 neededRole);
    
        /**
         * @dev The caller of a function is not the expected one.
         *
         * NOTE: Don't confuse with {AccessControlUnauthorizedAccount}.
         */
        error AccessControlBadConfirmation();
    
        /**
         * @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.
         */
        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 `callerConfirmation`.
         */
        function renounceRole(bytes32 role, address callerConfirmation) external;
    }

    <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
    
    pragma solidity ^0.8.20;
    
    /**
     * @dev Provides information about the current execution context, including the
     * sender of the transaction and its data. While these are generally available
     * via msg.sender and msg.data, they should not be accessed in such a direct
     * manner, since when dealing with meta-transactions the account sending and
     * paying for execution may not be the actual sender (as far as an application
     * is concerned).
     *
     * This contract is only required for intermediate, library-like contracts.
     */
    abstract contract Context {
        function _msgSender() internal view virtual returns (address) {
            return msg.sender;
        }
    
        function _msgData() internal view virtual returns (bytes calldata) {
            return msg.data;
        }
    
        function _contextSuffixLength() internal view virtual returns (uint256) {
            return 0;
        }
    }

    <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/ERC165.sol)
    
    pragma solidity ^0.8.20;
    
    import {IERC165} from "./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);
     * }
     * ```
     */
    abstract contract ERC165 is IERC165 {
        /**
         * @dev See {IERC165-supportsInterface}.
         */
        function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
            return interfaceId == type(IERC165).interfaceId;
        }
    }

    <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol)
    
    pragma solidity ^0.8.20;
    
    /**
     * @dev Interface of the ERC165 standard, as defined in the
     * https://eips.ethereum.org/EIPS/eip-165[EIP].
     *
     * Implementers can declare support of contract interfaces, which can then be
     * queried by others ({ERC165Checker}).
     *
     * For an implementation, see {ERC165}.
     */
    interface IERC165 {
        /**
         * @dev Returns true if this contract implements the interface defined by
         * `interfaceId`. See the corresponding
         * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
         * to learn more about how these ids are created.
         *
         * This function call must use less than 30 000 gas.
         */
        function supportsInterface(bytes4 interfaceId) external view returns (bool);
    }

    Please enter a contract address above to load the contract details and source code.

    Context size (optional):