DreamWispNFTV2.sol

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

import "./DreamWisps.sol";

/**
 * @title DreamWispNFTV2
 * @dev Enhanced version of DreamWispNFT with premium features
 * This contract demonstrates how to upgrade the existing DreamWispNFT contract
 * while maintaining all of its functionality and adding new features
 */
contract DreamWispNFTV2 is DreamWispNFT {
    // Mapping from token ID to premium status
    mapping(uint256 => bool) private _premiumTokens;
    
    // Additional royalty percentage for premium tokens (on top of base royalty)
    uint256 private _premiumRoyaltyBps;
    
    // Event emitted when premium status changes
    event PremiumStatusChanged(uint256 indexed tokenId, bool isPremium);
    
    // Event emitted when premium royalty percentage changes
    event PremiumRoyaltyChanged(uint256 previousBps, uint256 newBps);
    
    /**
     * @dev Returns the version of the contract
     * This is a new function added in V2
     */
    function version() public pure returns (string memory) {
        return "2.0.0";
    }
    
    /**
     * @dev Set premium status for a token
     * @param tokenId ID of the token
     * @param status Premium status to set
     * 
     * Requirements:
     * - Caller must be the owner or approved for all tokens
     * - The token must exist
     */
    function setPremiumStatus(uint256 tokenId, bool status) public {
        // Check if token exists by trying to access owner (will revert if doesn't exist)
        address owner = ownerOf(tokenId);
        require(
            owner == _msgSender() || 
            getApproved(tokenId) == _msgSender() || 
            isApprovedForAll(owner, _msgSender()),
            "DreamWispNFTV2: caller is not owner nor approved"
        );
        
        _premiumTokens[tokenId] = status;
        emit PremiumStatusChanged(tokenId, status);
    }
    
    /**
     * @dev Check if a token is premium
     * @param tokenId ID of the token
     * @return bool True if the token is premium
     * 
     * Requirements:
     * - The token must exist
     */
    function isPremium(uint256 tokenId) public view returns (bool) {
        // This will revert if token doesn't exist
        ownerOf(tokenId);
        return _premiumTokens[tokenId];
    }
    
    /**
     * @dev Set premium royalty percentage
     * @param bps Basis points for premium royalty (100 = 1%)
     * 
     * Requirements:
     * - Caller must be the creator or contract owner
     * - Premium royalty cannot exceed 5000 bps (50%)
     */
    function setPremiumRoyaltyBps(uint256 bps) public {
        require(_msgSender() == creator || _msgSender() == owner(), "DreamWispNFTV2: caller is not creator or owner");
        require(bps <= 5000, "DreamWispNFTV2: premium royalty cannot exceed 50%");
        
        uint256 previousBps = _premiumRoyaltyBps;
        _premiumRoyaltyBps = bps;
        emit PremiumRoyaltyChanged(previousBps, bps);
    }
    
    /**
     * @dev Get premium royalty percentage
     * @return uint256 Premium royalty in basis points
     */
    function getPremiumRoyaltyBps() public view returns (uint256) {
        return _premiumRoyaltyBps;
    }
    
    /**
     * @dev Override the royaltyInfo function to include premium royalty
     * @param tokenId ID of the token
     * @param salePrice Sale price of the token
     * @return receiver Address of the royalty receiver
     * @return royaltyAmount Amount of royalty to be paid
     */
    function royaltyInfo(uint256 tokenId, uint256 salePrice) public view returns (address receiver, uint256 royaltyAmount) {
        // This will revert if token doesn't exist
        ownerOf(tokenId);
        
        // Get creator as royalty receiver
        address baseReceiver = creator;
        
        // Calculate base royalty (e.g., 5%)
        uint256 baseRoyalty = (salePrice * 500) / 10000;
        
        // If the token is premium, add premium royalty
        if (_premiumTokens[tokenId]) {
            uint256 premiumRoyalty = (salePrice * _premiumRoyaltyBps) / 10000;
            return (baseReceiver, baseRoyalty + premiumRoyalty);
        }
        
        return (baseReceiver, baseRoyalty);
    }
    
    /**
     * @dev Override the token URI to include premium status
     * @param tokenId ID of the token
     * @return string URI for the token metadata
     */
    function tokenURI(uint256 tokenId) public view override returns (string memory) {
        string memory baseURI = super.tokenURI(tokenId);
        
        // If the token is premium, add premium indicator to the URI
        if (_premiumTokens[tokenId]) {
            return string(abi.encodePacked(baseURI, "?premium=true"));
        }
        
        return baseURI;
    }
    
    /**
     * @dev Function to mint a premium token in one transaction
     * @param to Address to mint the token to
     * @return uint256 ID of the minted token
     */
    function mintPremium(address to) public returns (uint256) {
        require(_msgSender() == creator || _msgSender() == owner(), "DreamWispNFTV2: caller is not creator or owner");
        
        uint256 tokenId = mint(to);
        _premiumTokens[tokenId] = true;
        emit PremiumStatusChanged(tokenId, true);
        
        return tokenId;
    }
    
    /**
     * @dev Get count of premium tokens
     * @return uint256 Number of premium tokens
     */
    function premiumTokenCount() public view returns (uint256) {
        uint256 count = 0;
        
        for (uint256 i = 0; i < tokenCounter; i++) {
            if (_premiumTokens[i]) {
                count++;
            }
        }
        
        return count;
    }
    
    /**
     * @dev Function to authorize an upgrade to this contract
     * @param newImplementation Address of the new implementation
     * 
     * Requirements:
     * - Can only be called by the current owner
     */
    function _authorizeUpgrade(address newImplementation) internal override onlyOwner {
        // This function is required by the UUPS pattern
        // No additional checks needed as we're enforcing onlyOwner
    }
}