Claude Agent Skill · by Wshobson

Nft Standards

Implements complete ERC-721 and ERC-1155 contracts with OpenZeppelin imports, proper enumeration, and URI handling. Generates both off-chain IPFS metadata struc

Install
Terminal · npx
$npx skills add https://github.com/wshobson/agents --skill nft-standards
Works with Paperclip

How Nft Standards fits into a Paperclip company.

Nft Standards drops into any Paperclip agent that handles this kind of work. Assign it to a specialist inside a pre-configured PaperclipOrg company and the skill becomes available on every heartbeat — no prompt engineering, no tool wiring.

S
SaaS FactoryPaired

Pre-configured AI company — 18 agents, 18 skills, one-time purchase.

$27$59
Explore pack
Source file
SKILL.md355 lines
Expand
---name: nft-standardsdescription: 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.--- # NFT Standards Master ERC-721 and ERC-1155 NFT standards, metadata best practices, and advanced NFT features. ## When to Use This Skill - Creating NFT collections (art, gaming, collectibles)- Implementing marketplace functionality- Building on-chain or off-chain metadata- Creating soulbound tokens (non-transferable)- Implementing royalties and revenue sharing- Developing dynamic/evolving NFTs ## ERC-721 (Non-Fungible Token Standard) ```solidity// SPDX-License-Identifier: MITpragma solidity ^0.8.0; import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";import "@openzeppelin/contracts/access/Ownable.sol";import "@openzeppelin/contracts/utils/Counters.sol"; contract MyNFT is ERC721URIStorage, ERC721Enumerable, Ownable {    using Counters for Counters.Counter;    Counters.Counter private _tokenIds;     uint256 public constant MAX_SUPPLY = 10000;    uint256 public constant MINT_PRICE = 0.08 ether;    uint256 public constant MAX_PER_MINT = 20;     constructor() ERC721("MyNFT", "MNFT") {}     function mint(uint256 quantity) external payable {        require(quantity > 0 && quantity <= MAX_PER_MINT, "Invalid quantity");        require(_tokenIds.current() + quantity <= MAX_SUPPLY, "Exceeds max supply");        require(msg.value >= MINT_PRICE * quantity, "Insufficient payment");         for (uint256 i = 0; i < quantity; i++) {            _tokenIds.increment();            uint256 newTokenId = _tokenIds.current();            _safeMint(msg.sender, newTokenId);            _setTokenURI(newTokenId, generateTokenURI(newTokenId));        }    }     function generateTokenURI(uint256 tokenId) internal pure returns (string memory) {        // Return IPFS URI or on-chain metadata        return string(abi.encodePacked("ipfs://QmHash/", Strings.toString(tokenId), ".json"));    }     // Required overrides    function _beforeTokenTransfer(        address from,        address to,        uint256 tokenId,        uint256 batchSize    ) internal override(ERC721, ERC721Enumerable) {        super._beforeTokenTransfer(from, to, tokenId, batchSize);    }     function _burn(uint256 tokenId) internal override(ERC721, ERC721URIStorage) {        super._burn(tokenId);    }     function tokenURI(uint256 tokenId) public view override(ERC721, ERC721URIStorage) returns (string memory) {        return super.tokenURI(tokenId);    }     function supportsInterface(bytes4 interfaceId)        public        view        override(ERC721, ERC721Enumerable)        returns (bool)    {        return super.supportsInterface(interfaceId);    }     function withdraw() external onlyOwner {        payable(owner()).transfer(address(this).balance);    }}``` ## ERC-1155 (Multi-Token Standard) ```solidity// SPDX-License-Identifier: MITpragma solidity ^0.8.0; import "@openzeppelin/contracts/token/ERC1155/ERC1155.sol";import "@openzeppelin/contracts/access/Ownable.sol"; contract GameItems is ERC1155, Ownable {    uint256 public constant SWORD = 1;    uint256 public constant SHIELD = 2;    uint256 public constant POTION = 3;     mapping(uint256 => uint256) public tokenSupply;    mapping(uint256 => uint256) public maxSupply;     constructor() ERC1155("ipfs://QmBaseHash/{id}.json") {        maxSupply[SWORD] = 1000;        maxSupply[SHIELD] = 500;        maxSupply[POTION] = 10000;    }     function mint(        address to,        uint256 id,        uint256 amount    ) external onlyOwner {        require(tokenSupply[id] + amount <= maxSupply[id], "Exceeds max supply");         _mint(to, id, amount, "");        tokenSupply[id] += amount;    }     function mintBatch(        address to,        uint256[] memory ids,        uint256[] memory amounts    ) external onlyOwner {        for (uint256 i = 0; i < ids.length; i++) {            require(tokenSupply[ids[i]] + amounts[i] <= maxSupply[ids[i]], "Exceeds max supply");            tokenSupply[ids[i]] += amounts[i];        }         _mintBatch(to, ids, amounts, "");    }     function burn(        address from,        uint256 id,        uint256 amount    ) external {        require(from == msg.sender || isApprovedForAll(from, msg.sender), "Not authorized");        _burn(from, id, amount);        tokenSupply[id] -= amount;    }}``` ## Metadata Standards ### Off-Chain Metadata (IPFS) ```json{  "name": "NFT #1",  "description": "Description of the NFT",  "image": "ipfs://QmImageHash",  "attributes": [    {      "trait_type": "Background",      "value": "Blue"    },    {      "trait_type": "Rarity",      "value": "Legendary"    },    {      "trait_type": "Power",      "value": 95,      "display_type": "number",      "max_value": 100    }  ]}``` ### On-Chain Metadata ```soliditycontract OnChainNFT is ERC721 {    struct Traits {        uint8 background;        uint8 body;        uint8 head;        uint8 rarity;    }     mapping(uint256 => Traits) public tokenTraits;     function tokenURI(uint256 tokenId) public view override returns (string memory) {        Traits memory traits = tokenTraits[tokenId];         string memory json = Base64.encode(            bytes(                string(                    abi.encodePacked(                        '{"name": "NFT #', Strings.toString(tokenId), '",',                        '"description": "On-chain NFT",',                        '"image": "data:image/svg+xml;base64,', generateSVG(traits), '",',                        '"attributes": [',                        '{"trait_type": "Background", "value": "', Strings.toString(traits.background), '"},',                        '{"trait_type": "Rarity", "value": "', getRarityName(traits.rarity), '"}',                        ']}'                    )                )            )        );         return string(abi.encodePacked("data:application/json;base64,", json));    }     function generateSVG(Traits memory traits) internal pure returns (string memory) {        // Generate SVG based on traits        return "...";    }}``` ## Royalties (EIP-2981) ```solidityimport "@openzeppelin/contracts/interfaces/IERC2981.sol"; contract NFTWithRoyalties is ERC721, IERC2981 {    address public royaltyRecipient;    uint96 public royaltyFee = 500; // 5%     constructor() ERC721("Royalty NFT", "RNFT") {        royaltyRecipient = msg.sender;    }     function royaltyInfo(uint256 tokenId, uint256 salePrice)        external        view        override        returns (address receiver, uint256 royaltyAmount)    {        return (royaltyRecipient, (salePrice * royaltyFee) / 10000);    }     function setRoyalty(address recipient, uint96 fee) external onlyOwner {        require(fee <= 1000, "Royalty fee too high"); // Max 10%        royaltyRecipient = recipient;        royaltyFee = fee;    }     function supportsInterface(bytes4 interfaceId)        public        view        override(ERC721, IERC165)        returns (bool)    {        return interfaceId == type(IERC2981).interfaceId ||               super.supportsInterface(interfaceId);    }}``` ## Soulbound Tokens (Non-Transferable) ```soliditycontract SoulboundToken is ERC721 {    constructor() ERC721("Soulbound", "SBT") {}     function _beforeTokenTransfer(        address from,        address to,        uint256 tokenId,        uint256 batchSize    ) internal virtual override {        require(from == address(0) || to == address(0), "Token is soulbound");        super._beforeTokenTransfer(from, to, tokenId, batchSize);    }     function mint(address to) external {        uint256 tokenId = totalSupply() + 1;        _safeMint(to, tokenId);    }     // Burn is allowed (user can destroy their SBT)    function burn(uint256 tokenId) external {        require(ownerOf(tokenId) == msg.sender, "Not token owner");        _burn(tokenId);    }}``` ## Dynamic NFTs ```soliditycontract DynamicNFT is ERC721 {    struct TokenState {        uint256 level;        uint256 experience;        uint256 lastUpdated;    }     mapping(uint256 => TokenState) public tokenStates;     function gainExperience(uint256 tokenId, uint256 exp) external {        require(ownerOf(tokenId) == msg.sender, "Not token owner");         TokenState storage state = tokenStates[tokenId];        state.experience += exp;         // Level up logic        if (state.experience >= state.level * 100) {            state.level++;        }         state.lastUpdated = block.timestamp;    }     function tokenURI(uint256 tokenId) public view override returns (string memory) {        TokenState memory state = tokenStates[tokenId];         // Generate metadata based on current state        return generateMetadata(tokenId, state);    }     function generateMetadata(uint256 tokenId, TokenState memory state)        internal        pure        returns (string memory)    {        // Dynamic metadata generation        return "";    }}``` ## Gas-Optimized Minting (ERC721A) ```solidityimport "erc721a/contracts/ERC721A.sol"; contract OptimizedNFT is ERC721A {    uint256 public constant MAX_SUPPLY = 10000;    uint256 public constant MINT_PRICE = 0.05 ether;     constructor() ERC721A("Optimized NFT", "ONFT") {}     function mint(uint256 quantity) external payable {        require(_totalMinted() + quantity <= MAX_SUPPLY, "Exceeds max supply");        require(msg.value >= MINT_PRICE * quantity, "Insufficient payment");         _mint(msg.sender, quantity);    }     function _baseURI() internal pure override returns (string memory) {        return "ipfs://QmBaseHash/";    }}```