Implement NFT standards (ERC-721, ERC-1155) with proper metadata handling, minting strategies, and marketplace integration. Use when creating NFT contracts, building NFT marketplaces, or implementing digital asset systems.
Add this skill
npx mdskills install sickn33/nft-standardsComprehensive NFT implementation guide with multiple standards, metadata handling, and advanced patterns
1---2name: nft-standards3description: Implement NFT standards (ERC-721, ERC-1155) with proper metadata handling, minting strategies, and marketplace integration. Use when creating NFT contracts, building NFT marketplaces, or implementing digital asset systems.4---56# NFT Standards78Master ERC-721 and ERC-1155 NFT standards, metadata best practices, and advanced NFT features.910## Do not use this skill when1112- The task is unrelated to nft standards13- You need a different domain or tool outside this scope1415## Instructions1617- Clarify goals, constraints, and required inputs.18- Apply relevant best practices and validate outcomes.19- Provide actionable steps and verification.20- If detailed examples are required, open `resources/implementation-playbook.md`.2122## Use this skill when2324- Creating NFT collections (art, gaming, collectibles)25- Implementing marketplace functionality26- Building on-chain or off-chain metadata27- Creating soulbound tokens (non-transferable)28- Implementing royalties and revenue sharing29- Developing dynamic/evolving NFTs3031## ERC-721 (Non-Fungible Token Standard)3233```solidity34// SPDX-License-Identifier: MIT35pragma solidity ^0.8.0;3637import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";38import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";39import "@openzeppelin/contracts/access/Ownable.sol";40import "@openzeppelin/contracts/utils/Counters.sol";4142contract MyNFT is ERC721URIStorage, ERC721Enumerable, Ownable {43 using Counters for Counters.Counter;44 Counters.Counter private _tokenIds;4546 uint256 public constant MAX_SUPPLY = 10000;47 uint256 public constant MINT_PRICE = 0.08 ether;48 uint256 public constant MAX_PER_MINT = 20;4950 constructor() ERC721("MyNFT", "MNFT") {}5152 function mint(uint256 quantity) external payable {53 require(quantity > 0 && quantity <= MAX_PER_MINT, "Invalid quantity");54 require(_tokenIds.current() + quantity <= MAX_SUPPLY, "Exceeds max supply");55 require(msg.value >= MINT_PRICE * quantity, "Insufficient payment");5657 for (uint256 i = 0; i < quantity; i++) {58 _tokenIds.increment();59 uint256 newTokenId = _tokenIds.current();60 _safeMint(msg.sender, newTokenId);61 _setTokenURI(newTokenId, generateTokenURI(newTokenId));62 }63 }6465 function generateTokenURI(uint256 tokenId) internal pure returns (string memory) {66 // Return IPFS URI or on-chain metadata67 return string(abi.encodePacked("ipfs://QmHash/", Strings.toString(tokenId), ".json"));68 }6970 // Required overrides71 function _beforeTokenTransfer(72 address from,73 address to,74 uint256 tokenId,75 uint256 batchSize76 ) internal override(ERC721, ERC721Enumerable) {77 super._beforeTokenTransfer(from, to, tokenId, batchSize);78 }7980 function _burn(uint256 tokenId) internal override(ERC721, ERC721URIStorage) {81 super._burn(tokenId);82 }8384 function tokenURI(uint256 tokenId) public view override(ERC721, ERC721URIStorage) returns (string memory) {85 return super.tokenURI(tokenId);86 }8788 function supportsInterface(bytes4 interfaceId)89 public90 view91 override(ERC721, ERC721Enumerable)92 returns (bool)93 {94 return super.supportsInterface(interfaceId);95 }9697 function withdraw() external onlyOwner {98 payable(owner()).transfer(address(this).balance);99 }100}101```102103## ERC-1155 (Multi-Token Standard)104105```solidity106// SPDX-License-Identifier: MIT107pragma solidity ^0.8.0;108109import "@openzeppelin/contracts/token/ERC1155/ERC1155.sol";110import "@openzeppelin/contracts/access/Ownable.sol";111112contract GameItems is ERC1155, Ownable {113 uint256 public constant SWORD = 1;114 uint256 public constant SHIELD = 2;115 uint256 public constant POTION = 3;116117 mapping(uint256 => uint256) public tokenSupply;118 mapping(uint256 => uint256) public maxSupply;119120 constructor() ERC1155("ipfs://QmBaseHash/{id}.json") {121 maxSupply[SWORD] = 1000;122 maxSupply[SHIELD] = 500;123 maxSupply[POTION] = 10000;124 }125126 function mint(127 address to,128 uint256 id,129 uint256 amount130 ) external onlyOwner {131 require(tokenSupply[id] + amount <= maxSupply[id], "Exceeds max supply");132133 _mint(to, id, amount, "");134 tokenSupply[id] += amount;135 }136137 function mintBatch(138 address to,139 uint256[] memory ids,140 uint256[] memory amounts141 ) external onlyOwner {142 for (uint256 i = 0; i < ids.length; i++) {143 require(tokenSupply[ids[i]] + amounts[i] <= maxSupply[ids[i]], "Exceeds max supply");144 tokenSupply[ids[i]] += amounts[i];145 }146147 _mintBatch(to, ids, amounts, "");148 }149150 function burn(151 address from,152 uint256 id,153 uint256 amount154 ) external {155 require(from == msg.sender || isApprovedForAll(from, msg.sender), "Not authorized");156 _burn(from, id, amount);157 tokenSupply[id] -= amount;158 }159}160```161162## Metadata Standards163164### Off-Chain Metadata (IPFS)165166```json167{168 "name": "NFT #1",169 "description": "Description of the NFT",170 "image": "ipfs://QmImageHash",171 "attributes": [172 {173 "trait_type": "Background",174 "value": "Blue"175 },176 {177 "trait_type": "Rarity",178 "value": "Legendary"179 },180 {181 "trait_type": "Power",182 "value": 95,183 "display_type": "number",184 "max_value": 100185 }186 ]187}188```189190### On-Chain Metadata191192```solidity193contract OnChainNFT is ERC721 {194 struct Traits {195 uint8 background;196 uint8 body;197 uint8 head;198 uint8 rarity;199 }200201 mapping(uint256 => Traits) public tokenTraits;202203 function tokenURI(uint256 tokenId) public view override returns (string memory) {204 Traits memory traits = tokenTraits[tokenId];205206 string memory json = Base64.encode(207 bytes(208 string(209 abi.encodePacked(210 '{"name": "NFT #', Strings.toString(tokenId), '",',211 '"description": "On-chain NFT",',212 '"image": "data:image/svg+xml;base64,', generateSVG(traits), '",',213 '"attributes": [',214 '{"trait_type": "Background", "value": "', Strings.toString(traits.background), '"},',215 '{"trait_type": "Rarity", "value": "', getRarityName(traits.rarity), '"}',216 ']}'217 )218 )219 )220 );221222 return string(abi.encodePacked("data:application/json;base64,", json));223 }224225 function generateSVG(Traits memory traits) internal pure returns (string memory) {226 // Generate SVG based on traits227 return "...";228 }229}230```231232## Royalties (EIP-2981)233234```solidity235import "@openzeppelin/contracts/interfaces/IERC2981.sol";236237contract NFTWithRoyalties is ERC721, IERC2981 {238 address public royaltyRecipient;239 uint96 public royaltyFee = 500; // 5%240241 constructor() ERC721("Royalty NFT", "RNFT") {242 royaltyRecipient = msg.sender;243 }244245 function royaltyInfo(uint256 tokenId, uint256 salePrice)246 external247 view248 override249 returns (address receiver, uint256 royaltyAmount)250 {251 return (royaltyRecipient, (salePrice * royaltyFee) / 10000);252 }253254 function setRoyalty(address recipient, uint96 fee) external onlyOwner {255 require(fee <= 1000, "Royalty fee too high"); // Max 10%256 royaltyRecipient = recipient;257 royaltyFee = fee;258 }259260 function supportsInterface(bytes4 interfaceId)261 public262 view263 override(ERC721, IERC165)264 returns (bool)265 {266 return interfaceId == type(IERC2981).interfaceId ||267 super.supportsInterface(interfaceId);268 }269}270```271272## Soulbound Tokens (Non-Transferable)273274```solidity275contract SoulboundToken is ERC721 {276 constructor() ERC721("Soulbound", "SBT") {}277278 function _beforeTokenTransfer(279 address from,280 address to,281 uint256 tokenId,282 uint256 batchSize283 ) internal virtual override {284 require(from == address(0) || to == address(0), "Token is soulbound");285 super._beforeTokenTransfer(from, to, tokenId, batchSize);286 }287288 function mint(address to) external {289 uint256 tokenId = totalSupply() + 1;290 _safeMint(to, tokenId);291 }292293 // Burn is allowed (user can destroy their SBT)294 function burn(uint256 tokenId) external {295 require(ownerOf(tokenId) == msg.sender, "Not token owner");296 _burn(tokenId);297 }298}299```300301## Dynamic NFTs302303```solidity304contract DynamicNFT is ERC721 {305 struct TokenState {306 uint256 level;307 uint256 experience;308 uint256 lastUpdated;309 }310311 mapping(uint256 => TokenState) public tokenStates;312313 function gainExperience(uint256 tokenId, uint256 exp) external {314 require(ownerOf(tokenId) == msg.sender, "Not token owner");315316 TokenState storage state = tokenStates[tokenId];317 state.experience += exp;318319 // Level up logic320 if (state.experience >= state.level * 100) {321 state.level++;322 }323324 state.lastUpdated = block.timestamp;325 }326327 function tokenURI(uint256 tokenId) public view override returns (string memory) {328 TokenState memory state = tokenStates[tokenId];329330 // Generate metadata based on current state331 return generateMetadata(tokenId, state);332 }333334 function generateMetadata(uint256 tokenId, TokenState memory state)335 internal336 pure337 returns (string memory)338 {339 // Dynamic metadata generation340 return "";341 }342}343```344345## Gas-Optimized Minting (ERC721A)346347```solidity348import "erc721a/contracts/ERC721A.sol";349350contract OptimizedNFT is ERC721A {351 uint256 public constant MAX_SUPPLY = 10000;352 uint256 public constant MINT_PRICE = 0.05 ether;353354 constructor() ERC721A("Optimized NFT", "ONFT") {}355356 function mint(uint256 quantity) external payable {357 require(_totalMinted() + quantity <= MAX_SUPPLY, "Exceeds max supply");358 require(msg.value >= MINT_PRICE * quantity, "Insufficient payment");359360 _mint(msg.sender, quantity);361 }362363 function _baseURI() internal pure override returns (string memory) {364 return "ipfs://QmBaseHash/";365 }366}367```368369## Resources370371- **references/erc721.md**: ERC-721 specification details372- **references/erc1155.md**: ERC-1155 multi-token standard373- **references/metadata-standards.md**: Metadata best practices374- **references/enumeration.md**: Token enumeration patterns375- **assets/erc721-contract.sol**: Production ERC-721 template376- **assets/erc1155-contract.sol**: Production ERC-1155 template377- **assets/metadata-schema.json**: Standard metadata format378- **assets/metadata-uploader.py**: IPFS upload utility379380## Best Practices3813821. **Use OpenZeppelin**: Battle-tested implementations3832. **Pin Metadata**: Use IPFS with pinning service3843. **Implement Royalties**: EIP-2981 for marketplace compatibility3854. **Gas Optimization**: Use ERC721A for batch minting3865. **Reveal Mechanism**: Placeholder → reveal pattern3876. **Enumeration**: Support walletOfOwner for marketplaces3887. **Whitelist**: Merkle trees for efficient whitelisting389390## Marketplace Integration391392- OpenSea: ERC-721/1155, metadata standards393- LooksRare: Royalty enforcement394- Rarible: Protocol fees, lazy minting395- Blur: Gas-optimized trading396
Full transparency — inspect the skill content before installing.