// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts/utils/Counters.sol"; import "@openzeppelin/contracts/utils/Strings.sol"; contract QuantumLynxCollection is ERC721Enumerable, Ownable { using Counters for Counters.Counter; using Strings for uint256; Counters.Counter private _tokenIdCounter; struct LynxAttributes { string furColor; string eyeColor; string accessory; string background; bool isRare; } mapping(uint256 => LynxAttributes) public lynxTraits; string private _baseTokenURI; constructor(string memory baseURI) ERC721("Quantum Lynx Collection", "QLYNX") { _baseTokenURI = baseURI; } function mintLynx( address to, string memory furColor, string memory eyeColor, string memory accessory, string memory background, bool isRare ) public onlyOwner { _tokenIdCounter.increment(); uint256 tokenId = _tokenIdCounter.current(); lynxTraits[tokenId] = LynxAttributes(furColor, eyeColor, accessory, background, isRare); _safeMint(to, tokenId); } function tokenURI(uint256 tokenId) public view virtual override returns (string memory) { require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token"); LynxAttributes memory traits = lynxTraits[tokenId]; string memory rarity = traits.isRare ? "Rare" : "Common"; string memory json = Base64.encode(bytes(string(abi.encodePacked( '{"name": "Quantum Lynx #', tokenId.toString(), '",', '"description": "A unique Quantum Lynx NFT with exclusive traits.",', '"image": "', _baseTokenURI, tokenId.toString(), '",', '"attributes": [', '{"trait_type": "Fur Color", "value": "', traits.furColor, '"},', '{"trait_type": "Eye Color", "value": "', traits.eyeColor, '"},', '{"trait_type": "Accessory", "value": "', traits.accessory, '"},', '{"trait_type": "Background", "value": "', traits.background, '"},', '{"trait_type": "Rarity", "value": "', rarity, '"}', ']}' )))); return string(abi.encodePacked("data:application/json;base64,", json)); } function setBaseURI(string memory baseURI) public onlyOwner { _baseTokenURI = baseURI; } } /// @title Base64 - Utility library for encoding/decoding base64 library Base64 { bytes internal constant TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; function encode(bytes memory data) internal pure returns (string memory) { if (data.length == 0) return ""; string memory table = string(TABLE); uint256 encodedLen = 4 * ((data.length + 2) / 3); string memory result = new string(encodedLen + 32); assembly { let tablePtr := add(table, 1) let resultPtr := add(result, 32) for { let i := 0 } lt(i, mload(data)) { i := add(i, 3) } { data := add(data, 3) let input := mload(data) mstore(resultPtr, shl(224, mload(add(tablePtr, and(shr(18, input), 0x3F))))) resultPtr := add(resultPtr, 1) mstore(resultPtr, shl(224, mload(add(tablePtr, and(shr(12, input), 0x3F))))) resultPtr := add(resultPtr, 1) mstore(resultPtr, shl(224, mload(add(tablePtr, and(shr(6, input), 0x3F))))) resultPtr := add(resultPtr, 1) mstore(resultPtr, shl(224, mload(add(tablePtr, and(input, 0x3F))))) resultPtr := add(resultPtr, 1) } mstore(result, encodedLen) } return result; } }