// File: src/Create/contracts/v1/MintPayout.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;

import {Ownable} from "solady/auth/Ownable.sol";
import {IMintPayout} from "create/interfaces/v1/IMintPayout.sol";
import {IMintContract} from "create/interfaces/v1/IMintContract.sol";
import {Version} from "create/contracts/v1/Version.sol";
import {
    MINT_REFERRAL_REASON,
    PROTOCOL_FEE_REASON,
    PURCHASE_AMOUNT_REASON
} from "create/interfaces/v1/MintPayoutReasons.sol";

contract MintPayout is IMintPayout, Version, Ownable {
    /// @inheritdoc IMintPayout
    uint256 public protocolFee;
    /// @inheritdoc IMintPayout
    address public constant protocolFeeRecipientAccount = address(0x4444444444444444444444444444444444444444);

    /// @inheritdoc IMintPayout
    mapping(address => uint256) public balanceOf;

    error InvalidAddress();
    error InvalidArrayLength();
    error IncorrectDepositAmount();
    error InvalidWithdrawAmount();
    error TransferFailed();

    constructor() Version(1) {
        _initializeOwner(tx.origin);
    }

    /// @inheritdoc IMintPayout
    function totalSupply() external view returns (uint256) {
        return address(this).balance;
    }

    /// @inheritdoc IMintPayout
    function setProtocolFee(uint256 _protocolFee) external onlyOwner {
        protocolFee = _protocolFee;
        emit ProtocolFeeUpdated(_protocolFee);
    }

    /// @inheritdoc IMintPayout
    function withdrawProtocolFee(address to, uint256 amount) external onlyOwner {
        _withdraw(protocolFeeRecipientAccount, to, amount);
    }

    /// @inheritdoc IMintPayout
    function mintDeposit(address mintContract, address minter, address referrer, uint256 quantity) external payable {
        if (mintContract == address(0)) revert InvalidAddress();
        if (quantity == 0) revert IncorrectDepositAmount();
        if (msg.value == 0) revert IncorrectDepositAmount();

        uint256 protocolPayout = protocolFee * quantity;
        uint256 referralPayout;
        if (referrer != address(0)) {
            referralPayout = protocolPayout / 2;
            protocolPayout = referralPayout;
        }

        if (msg.value < protocolPayout + referralPayout) revert IncorrectDepositAmount();

        uint256 creatorPayout = msg.value - protocolPayout - referralPayout;
        address creator;

        if (creatorPayout > 0) {
            creator = IMintContract(mintContract).payoutRecipient();
            if (creator == address(0)) {
                creator = protocolFeeRecipientAccount;
            }

            _deposit(msg.sender, creator, PURCHASE_AMOUNT_REASON, creatorPayout);
        }

        if (referralPayout > 0) {
            _deposit(msg.sender, referrer, MINT_REFERRAL_REASON, referralPayout);
        }

        if (protocolPayout > 0) {
            _deposit(msg.sender, protocolFeeRecipientAccount, PROTOCOL_FEE_REASON, protocolPayout);
        }

        emit MintDeposit(
            msg.sender,
            mintContract,
            minter,
            referrer,
            creator,
            creatorPayout,
            referralPayout,
            protocolPayout,
            msg.value,
            quantity,
            protocolFee
        );
    }

    /// @inheritdoc IMintPayout
    function deposit(address to, bytes4 reason) external payable {
        if (to == address(0)) revert InvalidAddress();
        _deposit(msg.sender, to, reason, msg.value);
    }

    /// @inheritdoc IMintPayout
    function depositBatch(address[] calldata recipients, uint256[] calldata amounts, bytes4[] calldata reasons)
        external
        payable
    {
        uint256 numRecipients = recipients.length;
        if (numRecipients != amounts.length || numRecipients != reasons.length) {
            revert InvalidArrayLength();
        }

        uint256 expectedTotalValue;
        for (uint256 i; i < numRecipients;) {
            expectedTotalValue += amounts[i];

            unchecked {
                ++i;
            }
        }

        if (msg.value != expectedTotalValue) revert IncorrectDepositAmount();

        address currentRecipient;
        uint256 currentAmount;
        for (uint256 i; i < numRecipients;) {
            currentRecipient = recipients[i];
            currentAmount = amounts[i];

            if (currentRecipient == address(0)) revert InvalidAddress();

            _deposit(msg.sender, currentRecipient, reasons[i], currentAmount);

            unchecked {
                ++i;
            }
        }
    }

    function _deposit(address from, address to, bytes4 reason, uint256 amount) internal {
        balanceOf[to] += amount;
        emit Deposit(from, to, reason, amount);
    }

    /// @inheritdoc IMintPayout
    function withdraw(address to, uint256 amount) external {
        _withdraw(msg.sender, to, amount);
    }

    /// @inheritdoc IMintPayout
    function withdrawAll(address to) external {
        _withdraw(msg.sender, to, balanceOf[msg.sender]);
    }

    function _withdraw(address from, address to, uint256 amount) internal {
        if (to == address(0)) revert InvalidAddress();

        if (amount == 0 || amount > balanceOf[from]) revert InvalidWithdrawAmount();

        balanceOf[from] -= amount;
        emit Withdraw(from, to, amount);

        (bool success,) = to.call{value: amount}("");
        if (!success) revert TransferFailed();
    }
}

// File: lib/solady/src/auth/Ownable.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Simple single owner authorization mixin.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/auth/Ownable.sol)
///
/// @dev Note:
/// This implementation does NOT auto-initialize the owner to `msg.sender`.
/// You MUST call the `_initializeOwner` in the constructor / initializer.
///
/// While the ownable portion follows
/// [EIP-173](https://eips.ethereum.org/EIPS/eip-173) for compatibility,
/// the nomenclature for the 2-step ownership handover may be unique to this codebase.
abstract contract Ownable {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       CUSTOM ERRORS                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The caller is not authorized to call the function.
    error Unauthorized();

    /// @dev The `newOwner` cannot be the zero address.
    error NewOwnerIsZeroAddress();

    /// @dev The `pendingOwner` does not have a valid handover request.
    error NoHandoverRequest();

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                           EVENTS                           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The ownership is transferred from `oldOwner` to `newOwner`.
    /// This event is intentionally kept the same as OpenZeppelin's Ownable to be
    /// compatible with indexers and [EIP-173](https://eips.ethereum.org/EIPS/eip-173),
    /// despite it not being as lightweight as a single argument event.
    event OwnershipTransferred(address indexed oldOwner, address indexed newOwner);

    /// @dev An ownership handover to `pendingOwner` has been requested.
    event OwnershipHandoverRequested(address indexed pendingOwner);

    /// @dev The ownership handover to `pendingOwner` has been canceled.
    event OwnershipHandoverCanceled(address indexed pendingOwner);

    /// @dev `keccak256(bytes("OwnershipTransferred(address,address)"))`.
    uint256 private constant _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE =
        0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0;

    /// @dev `keccak256(bytes("OwnershipHandoverRequested(address)"))`.
    uint256 private constant _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE =
        0xdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d;

    /// @dev `keccak256(bytes("OwnershipHandoverCanceled(address)"))`.
    uint256 private constant _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE =
        0xfa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                          STORAGE                           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The owner slot is given by: `not(_OWNER_SLOT_NOT)`.
    /// It is intentionally chosen to be a high value
    /// to avoid collision with lower slots.
    /// The choice of manual storage layout is to enable compatibility
    /// with both regular and upgradeable contracts.
    uint256 private constant _OWNER_SLOT_NOT = 0x8b78c6d8;

    /// The ownership handover slot of `newOwner` is given by:
    /// ```
    ///     mstore(0x00, or(shl(96, user), _HANDOVER_SLOT_SEED))
    ///     let handoverSlot := keccak256(0x00, 0x20)
    /// ```
    /// It stores the expiry timestamp of the two-step ownership handover.
    uint256 private constant _HANDOVER_SLOT_SEED = 0x389a75e1;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                     INTERNAL FUNCTIONS                     */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Initializes the owner directly without authorization guard.
    /// This function must be called upon initialization,
    /// regardless of whether the contract is upgradeable or not.
    /// This is to enable generalization to both regular and upgradeable contracts,
    /// and to save gas in case the initial owner is not the caller.
    /// For performance reasons, this function will not check if there
    /// is an existing owner.
    function _initializeOwner(address newOwner) internal virtual {
        /// @solidity memory-safe-assembly
        assembly {
            // Clean the upper 96 bits.
            newOwner := shr(96, shl(96, newOwner))
            // Store the new value.
            sstore(not(_OWNER_SLOT_NOT), newOwner)
            // Emit the {OwnershipTransferred} event.
            log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)
        }
    }

    /// @dev Sets the owner directly without authorization guard.
    function _setOwner(address newOwner) internal virtual {
        /// @solidity memory-safe-assembly
        assembly {
            let ownerSlot := not(_OWNER_SLOT_NOT)
            // Clean the upper 96 bits.
            newOwner := shr(96, shl(96, newOwner))
            // Emit the {OwnershipTransferred} event.
            log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)
            // Store the new value.
            sstore(ownerSlot, newOwner)
        }
    }

    /// @dev Throws if the sender is not the owner.
    function _checkOwner() internal view virtual {
        /// @solidity memory-safe-assembly
        assembly {
            // If the caller is not the stored owner, revert.
            if iszero(eq(caller(), sload(not(_OWNER_SLOT_NOT)))) {
                mstore(0x00, 0x82b42900) // `Unauthorized()`.
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Returns how long a two-step ownership handover is valid for in seconds.
    /// Override to return a different value if needed.
    /// Made internal to conserve bytecode. Wrap it in a public function if needed.
    function _ownershipHandoverValidFor() internal view virtual returns (uint64) {
        return 48 * 3600;
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                  PUBLIC UPDATE FUNCTIONS                   */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Allows the owner to transfer the ownership to `newOwner`.
    function transferOwnership(address newOwner) public payable virtual onlyOwner {
        /// @solidity memory-safe-assembly
        assembly {
            if iszero(shl(96, newOwner)) {
                mstore(0x00, 0x7448fbae) // `NewOwnerIsZeroAddress()`.
                revert(0x1c, 0x04)
            }
        }
        _setOwner(newOwner);
    }

    /// @dev Allows the owner to renounce their ownership.
    function renounceOwnership() public payable virtual onlyOwner {
        _setOwner(address(0));
    }

    /// @dev Request a two-step ownership handover to the caller.
    /// The request will automatically expire in 48 hours (172800 seconds) by default.
    function requestOwnershipHandover() public payable virtual {
        unchecked {
            uint256 expires = block.timestamp + _ownershipHandoverValidFor();
            /// @solidity memory-safe-assembly
            assembly {
                // Compute and set the handover slot to `expires`.
                mstore(0x0c, _HANDOVER_SLOT_SEED)
                mstore(0x00, caller())
                sstore(keccak256(0x0c, 0x20), expires)
                // Emit the {OwnershipHandoverRequested} event.
                log2(0, 0, _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE, caller())
            }
        }
    }

    /// @dev Cancels the two-step ownership handover to the caller, if any.
    function cancelOwnershipHandover() public payable virtual {
        /// @solidity memory-safe-assembly
        assembly {
            // Compute and set the handover slot to 0.
            mstore(0x0c, _HANDOVER_SLOT_SEED)
            mstore(0x00, caller())
            sstore(keccak256(0x0c, 0x20), 0)
            // Emit the {OwnershipHandoverCanceled} event.
            log2(0, 0, _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE, caller())
        }
    }

    /// @dev Allows the owner to complete the two-step ownership handover to `pendingOwner`.
    /// Reverts if there is no existing ownership handover requested by `pendingOwner`.
    function completeOwnershipHandover(address pendingOwner) public payable virtual onlyOwner {
        /// @solidity memory-safe-assembly
        assembly {
            // Compute and set the handover slot to 0.
            mstore(0x0c, _HANDOVER_SLOT_SEED)
            mstore(0x00, pendingOwner)
            let handoverSlot := keccak256(0x0c, 0x20)
            // If the handover does not exist, or has expired.
            if gt(timestamp(), sload(handoverSlot)) {
                mstore(0x00, 0x6f5e8818) // `NoHandoverRequest()`.
                revert(0x1c, 0x04)
            }
            // Set the handover slot to 0.
            sstore(handoverSlot, 0)
        }
        _setOwner(pendingOwner);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                   PUBLIC READ FUNCTIONS                    */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns the owner of the contract.
    function owner() public view virtual returns (address result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := sload(not(_OWNER_SLOT_NOT))
        }
    }

    /// @dev Returns the expiry timestamp for the two-step ownership handover to `pendingOwner`.
    function ownershipHandoverExpiresAt(address pendingOwner)
        public
        view
        virtual
        returns (uint256 result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            // Compute the handover slot.
            mstore(0x0c, _HANDOVER_SLOT_SEED)
            mstore(0x00, pendingOwner)
            // Load the handover slot.
            result := sload(keccak256(0x0c, 0x20))
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         MODIFIERS                          */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Marks a function as only callable by the owner.
    modifier onlyOwner() virtual {
        _checkOwner();
        _;
    }
}

// File: src/Create/interfaces/v1/IMintPayout.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;

interface IMintPayoutEvents {
    /// @notice Emitted when a deposit has been made.
    /// @param from The depositor's address.
    /// @param to The address receiving the deposit.
    /// @param reason The reason code for the deposit.
    /// @param amount The deposit amount.
    event Deposit(address from, address to, bytes4 reason, uint256 amount);

    /// @notice Emitted when a withdrawal has been made.
    /// @param from The address withdrawing.
    /// @param to The address receiving the withdrawn funds.
    /// @param amount The withdrawal amount.
    event Withdraw(address from, address to, uint256 amount);

    /// @notice Emitted during a mint deposit to provide additional context.
    /// @param depositedBy The address of the mint initiator.
    /// @param mintContract The mint contract address this mint deposit refers to.
    /// @param minter The address of the person minting.
    /// @param referrer The address of the referrer, or the zero address for no referrer.
    /// @param creator The address of the contract creator, or the protocol fee recipient if none.
    /// @param creatorPayout The amount being paid to the creator.
    /// @param referralPayout The amount being paid to the referrer.
    /// @param protocolPayout The amount being paid to the protocol.
    /// @param totalAmount The total deposit amount.
    /// @param quantity The number of tokens being minted.
    /// @param protocolFee The per-mint fee for the protocol.
    event MintDeposit(
        address depositedBy,
        address mintContract,
        address minter,
        address referrer,
        address creator,
        uint256 creatorPayout,
        uint256 referralPayout,
        uint256 protocolPayout,
        uint256 totalAmount,
        uint256 quantity,
        uint256 protocolFee
    );

    /// @notice Emitted when the protocol fee is updated.
    /// @param fee The new protocol fee.
    event ProtocolFeeUpdated(uint256 fee);
}

interface IMintPayout is IMintPayoutEvents {
    function balanceOf(address owner) external view returns (uint256);
    function totalSupply() external view returns (uint256);

    /// @notice The current protocol fee per-mint.
    function protocolFee() external view returns (uint256 fee);

    /// @notice Sets the protocol fee per-mint.
    /// @dev Only callable by the owner.
    /// @param fee The new protocol fee.
    function setProtocolFee(uint256 fee) external;

    /// @notice Magic value used to represent the fees belonging to the protocol.
    function protocolFeeRecipientAccount() external view returns (address);

    /// @notice Withdraws from the protocol fee balance.
    /// @dev Only callable by the owner.
    /// @param to The address receiving the withdrawn funds.
    /// @param amount The withdrawal amount.
    function withdrawProtocolFee(address to, uint256 amount) external;

    /// @notice Deposits ether for a mint.
    /// @dev Ensure that `quantity` is > 0. The `protocolFee` should be per-mint, not the total taken.
    /// Will trigger a `MintDeposit` event, followed by `Deposit` events for:
    /// creator payout, protocol payout, and referrer payout (if a referrer is specified).
    /// @param mintContract The mint contract address this mint deposit refers to.
    /// @param minter The address of the minter.
    /// @param referrer The address of the referrer, or the zero address for no referrer.
    /// @param quantity The amount being minted.
    function mintDeposit(address mintContract, address minter, address referrer, uint256 quantity) external payable;

    /// @notice Deposits ether to an address.
    /// @param to The address receiving the deposit.
    /// @param reason The reason code for the deposit.
    function deposit(address to, bytes4 reason) external payable;

    /// @notice Deposits ether to multiple addresses.
    /// @dev The length of `recipients`, `amounts`, and `reasons` must be the same.
    /// @param recipients List of addresses receiving the deposits.
    /// @param amounts List of deposit amounts.
    /// @param reasons List of reason codes for the deposits.
    function depositBatch(address[] calldata recipients, uint256[] calldata amounts, bytes4[] calldata reasons)
        external
        payable;

    /// @notice Withdraws ether from the `msg.sender`'s account to a specified address.
    /// @param to The address receiving the withdrawn funds.
    /// @param amount The withdrawal amount.
    function withdraw(address to, uint256 amount) external;

    /// @notice Withdraws all ether from the `msg.sender`'s account to a specified address.
    /// @param to The address receiving the withdrawn funds.
    function withdrawAll(address to) external;
}

// File: src/Create/interfaces/v1/IMintContract.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;

import {IMetadataRenderer} from "create/interfaces/v1/IMetadataRenderer.sol";

interface IMintContractEvents {
    /// @notice Emitted when the royalty is updated.
    event RoyaltyUpdated(uint256 bps);
    /// @notice Emitted when a new mint module is added.
    event ModuleAdded(address module);
    /// @notice Emitted when a mint module is removed.
    event ModuleRemoved(address module);
    /// @notice Emitted when the metadata renderer is updated.
    event MetadataRendererUpdated(address renderer);
}

interface IMintContract is IMintContractEvents {
    /// @notice Mints tokens using approved mint modules.
    /// @param to The address receiving the minted tokens.
    /// @param quantity The quantity of tokens to mint.
    function mint(address to, uint256 quantity) external;

    /// @notice Mints tokens, callable only by the contract owner.
    /// @param to The address receiving the minted tokens.
    /// @param quantity The quantity of tokens to mint.
    function adminMint(address to, uint256 quantity) external;

    /// @notice Retrieves the payout recipient address for this mint contract.
    /// @return recipient address of the payout recipient.
    function payoutRecipient() external view returns (address recipient);

    /// @notice Returns the total number of tokens minted.
    /// @return total number of tokens minted.
    function totalMinted() external view returns (uint256 total);

    /// @notice Adds a new mint module as an approved minter.
    /// @dev Can only be executed by the owner of the contract.
    /// Must be approved in the MintModuleRegistry.
    /// @param mintModule The contract address of the mint module.
    function addMintModule(address mintModule) external;

    /// @notice Removes a mint module as an approved minter.
    /// @dev Can only be executed by the owner of the contract.
    /// @param mintModule The contract address of the mint module.
    function removeMintModule(address mintModule) external;

    /// @notice Returns whether a mint module is approved.
    /// @param mintModule The contract address of the mint module.
    /// @return isApproved Whether the mint module is approved.
    function isMintModuleApproved(address mintModule) external view returns (bool isApproved);

    /// @notice Updates configuration located in an external contract.
    /// @dev Can only be executed by the owner of the contract.
    /// The cardinality of `configurables` and `configData` must be the same.
    /// @param configurables The contract addresses to configure.
    /// @param configData The configuration data for the contracts.
    function updateExternalConfiguration(address[] calldata configurables, bytes[] calldata configData) external;

    /// @notice Sets the metadata renderer.
    /// @dev This will not request a metadata refresh. If needed, call `refreshMetadata`.
    /// @param renderer The new metadata renderer.
    function setMetadataRenderer(IMetadataRenderer renderer) external;

    /// @notice Returns the metadata renderer for this contract.
    /// @return metadataRenderer The metadata renderer.
    function metadataRenderer() external returns (IMetadataRenderer metadataRenderer);

    /// @notice Triggers a batch metadata update.
    function refreshMetadata() external;

    /// @notice Updates the royalty for this contract.
    /// @dev Can only be called by the contract owner.
    /// Emits a `RoyaltyUpdated` event.
    /// @param bps The new royalty.
    function setRoyalty(uint256 bps) external;

    /// @notice Returns the royalty for this contract.
    /// @return bps The royalty.
    function royaltyBps() external returns (uint256 bps);
}

// File: src/Create/contracts/v1/Version.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;

abstract contract Version {
    /// @notice The version of the contract.
    uint32 public immutable contractVersion;

    constructor(uint32 _contractVersion) {
        contractVersion = _contractVersion;
    }
}

// File: src/Create/interfaces/v1/MintPayoutReasons.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;

bytes4 constant MINT_REFERRAL_REASON = bytes4(keccak256("MINT_REFERRAL"));
bytes4 constant PROTOCOL_FEE_REASON = bytes4(keccak256("PROTOCOL_FEE"));
bytes4 constant PURCHASE_AMOUNT_REASON = bytes4(keccak256("PURCHASE_AMOUNT"));

// File: src/Create/interfaces/v1/IMetadataRenderer.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;

interface IMetadataRenderer {
    /// @notice Retrieves the token URI for the specified token ID.
    /// @param tokenId The ID of the token.
    /// @return uri The URI of the token.
    function tokenURI(uint256 tokenId) external view returns (string memory uri);
}