nft-standards
wshobson/agents
Implement ERC-721 and ERC-1155 NFT standards with metadata, minting, royalties, and marketplace integration.
What is nft-standards?
This skill provides production-ready implementations of ERC-721 (single-token) and ERC-1155 (multi-token) NFT standards, along with metadata handling, royalty support (EIP-2981), and minting strategies. Use it when building NFT collections, marketplaces, gaming assets, or any on-chain digital asset system.
- Implement ERC-721 contracts with enumerable and URI storage extensions
- Implement ERC-1155 multi-token contracts with batch minting and supply caps
- Handle off-chain metadata via IPFS with trait definitions and display types
- Generate on-chain metadata with SVG rendering and Base64 encoding
- Implement EIP-2981 royalty standards for secondary sales
- Support soulbound tokens, dynamic NFTs, and revenue sharing mechanisms
How to install nft-standards
npx skills add https://github.com/wshobson/agents --skill nft-standards- Solidity ^0.8.0 compiler
- OpenZeppelin contracts library (@openzeppelin/contracts)
- Understanding of ERC token standards and Solidity basics
- IPFS node or pinning service for off-chain metadata (optional)
How to use nft-standards
- 1.Choose between ERC-721 (single unique tokens) or ERC-1155 (multiple token types)
- 2.Customize the contract with your collection name, symbol, supply limits, and mint price
- 3.Define metadata structure: use IPFS URIs for off-chain or implement on-chain generation
- 4.Add royalty recipient and fee using EIP-2981 if secondary sales matter
- 5.Deploy to your target network and test minting and metadata retrieval
- 6.Integrate with marketplace contracts or frontends for trading functionality
Use cases
- Creating limited-edition NFT art collections with IPFS metadata
- Building gaming item systems with multiple token types and supply limits
- Implementing NFT marketplaces with royalty enforcement
- Developing soulbound or non-transferable token systems
- Creating evolving NFTs with on-chain trait generation
- Smart contract developers building NFT projects
- Blockchain game developers
- NFT marketplace creators
- Digital asset platform builders
- Web3 product teams implementing token standards
nft-standards FAQ
ERC-721 is for unique, non-fungible tokens (one per ID). ERC-1155 supports multiple token types in a single contract, useful for games or collections with different item categories. ERC-1155 is more gas-efficient for batch operations.
IPFS is cheaper and standard for large collections; use it for images and bulk data. On-chain metadata is immutable and doesn't depend on external services; use it for critical data or fully generative NFTs.
EIP-2981 defines royaltyInfo() which returns the recipient address and royalty percentage. Marketplaces query this to automatically send a cut to creators on secondary sales, though enforcement depends on marketplace implementation.
ERC-1155 is more efficient for batch minting and transfers. Single mints are similar, but batch operations save significant gas with ERC-1155.
Yes, override the transfer functions to restrict movement. The skill examples show the patterns; you can disable transfers entirely or allow only specific conditions.
Full instructions (SKILL.md)
Source of truth, from wshobson/agents.
name: nft-standards description: 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)
// SPDX-License-Identifier: MIT
pragma 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)
// SPDX-License-Identifier: MIT
pragma 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)
{
"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
contract 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)
import "@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);
}
}
Additional patterns and templates
More detailed templates and worked examples live in references/details.md. Read that file for the full pattern library.
Related skills
More from wshobson/agents and the wider catalog.

nodejs-backend-patterns
Build production-ready Node.js backends with Express/Fastify, middleware patterns, authentication, and database integration.

nx-workspace-patterns
Configure and optimize Nx monorepo workspaces with production patterns for project boundaries, caching, and affected commands.

on-call-handoff-patterns
Master on-call shift handoffs with structured context transfer and escalation procedures.

openapi-spec-generation
Generate and maintain OpenAPI 3.1 specifications from code, design-first specs, and validation patterns.

parallel-debugging
Debug complex issues systematically using competing hypotheses and parallel investigation.

parallel-feature-development
Coordinate parallel feature development with file ownership strategies and conflict avoidance rules for multi-agent teams.