The ERC721 Metadata contract is an enhanced NFT implementation that provides on-chain metadata storage and management capabilities. It extends the standard ERC721 functionality with advanced metadata features for real-world asset tokenization.
The ERC721 Metadata contract provides:
- On-chain Metadata Storage: Store key-value pairs directly on-chain
- Metadata Filtering: Efficient filtering and searching capabilities
- Collection Metadata: Store collection-level information
- Metadata Freezing: Immutable metadata for certain tokens
- Gas-Optimized Indexing: Efficient metadata-based queries
- Standard ERC721: Full ERC721 compliance with extensions
- On-chain Metadata: Key-value storage for token attributes
- Collection Metadata: Collection-level information storage
- Metadata Indexing: Gas-optimized filtering and searching
- Freeze Mechanism: Immutable metadata for verified tokens
contract ERC721Metadata is IERC721Metadata, ERC721, ERC721URIStorage, Ownable {
// Metadata storage
mapping(bytes32 => KeyValue) internal metadata;
mapping(bytes32 => uint256[]) private metadataIndex;
mapping(uint256 => string[]) internal metadataKeys;
// Collection metadata
mapping(string => KeyValue) internal collectionMetadata;
string[] internal collectionMetadataKeys;
// Freeze mechanism
mapping(uint256 => bool) internal isFrozen;
}function setMetadata(uint256 _tokenId, string memory _key, string memory _newValue) externalParameters:
_tokenId: Token ID to set metadata for_key: Metadata key_newValue: Metadata value
Requirements:
- Caller must be token owner
- Token metadata must not be frozen
function setMetadata(uint256 _tokenId, string[] memory _keys, string[] memory _values) externalParameters:
_tokenId: Token ID to set metadata for_keys: Array of metadata keys_values: Array of metadata values
Requirements:
- Arrays must have same length
- Caller must be token owner
- Token metadata must not be frozen
function setCollectionMetadata(string[] memory _keys, string[] memory _values) external onlyOwnerParameters:
_keys: Array of collection metadata keys_values: Array of collection metadata values
Requirements:
- Caller must be contract owner
- Arrays must have same length
function mint(address _to, string memory _uri, string[] memory _keys, string[] memory _values) external onlyOwner returns (uint256 tokenId)Parameters:
_to: Address to mint token to_uri: Token URI_keys: Initial metadata keys_values: Initial metadata values
Returns: Minted token ID
function freezeMetadata(uint256 _tokenId) external onlyOwnerParameters:
_tokenId: Token ID to freeze metadata for
Requirements:
- Caller must be contract owner
- Token metadata must not already be frozen
struct KeyValue {
string key;
string value;
bool exists;
}struct TokenDetails {
uint256 id;
string uri;
address owner;
KeyValue[] metadata;
}function getMetadata(uint256 _tokenId) public view returns (KeyValue[] memory)Returns all metadata key-value pairs for a specific token.
function getMetadata(uint256 _tokenId, string memory _key) public view returns (KeyValue memory)Returns specific metadata for a token by key.
function getCollectionMetadata() external view returns (KeyValue[] memory)Returns all collection-level metadata.
function getCollectionMetadata(string memory _key) external view returns (KeyValue memory)Returns specific collection metadata by key.
function filterTokens(string memory _key, string memory _value) external view returns (TokenDetails[] memory)Returns all tokens that have the specified key-value pair.
function filterTokens(string[] memory _keys, string[] memory _values) external view returns (TokenDetails[] memory)Returns all tokens that have all specified key-value pairs.
constructor(string memory _name, string memory _symbol)Parameters:
_name: NFT collection name_symbol: NFT collection symbol
// Deploy ERC721 Metadata contract
const erc721Metadata = await ethers.deployContract("ERC721Metadata", ["Real Estate NFTs", "RENFT"]);
console.log("ERC721 Metadata deployed to:", erc721Metadata.target);- Owner Only: Collection metadata and freezing functions
- Token Owner: Individual token metadata management
- Public: Read-only query functions
- Freeze Mechanism: Prevents metadata tampering
- Owner Control: Collection metadata managed by owner
- Validation: Input validation for all functions
- Indexing: Efficient metadata-based filtering
- Storage: Optimized data structures
- Batch Operations: Multiple metadata updates in single transaction
The ERC721 Metadata includes comprehensive tests:
- Metadata setting and retrieval
- Collection metadata management
- Token filtering and search
- Freeze mechanism
- Access control
- Edge cases and error conditions
# Run ERC721 tests
yarn hardhat test test/erc721/erc721.test.ts
# Run with gas reporting
yarn hardhat test test/erc721/erc721.test.ts --gas-report// Connect to contract
const erc721Metadata = await ethers.getContractAt("ERC721Metadata", contractAddress);
// Mint token with metadata
const tokenId = await erc721Metadata.mint(
userAddress,
"https://example.com/token/1",
["location", "size", "year"],
["New York", "1000", "2023"],
);
// Set additional metadata
await erc721Metadata.setMetadata(tokenId, "price", "500000");// Set collection metadata
await erc721Metadata.setCollectionMetadata(
["collection_name", "description", "website"],
["Real Estate Collection", "Tokenized real estate assets", "https://example.com"],
);
// Get collection metadata
const collectionMetadata = await erc721Metadata.getCollectionMetadata();// Filter tokens by location
const nyTokens = await erc721Metadata.filterTokens("location", "New York");
// Filter tokens by multiple criteria
const filteredTokens = await erc721Metadata.filterTokens(["location", "size"], ["New York", "1000"]);
// Get token details
for (const token of filteredTokens) {
console.log("Token ID:", token.id);
console.log("Owner:", token.owner);
console.log("URI:", token.uri);
console.log("Metadata:", token.metadata);
}// Get all metadata for a token
const tokenMetadata = await erc721Metadata.getMetadata(tokenId);
// Get specific metadata
const location = await erc721Metadata.getMetadata(tokenId, "location");
// Freeze metadata (owner only)
await erc721Metadata.freezeMetadata(tokenId);The ERC721 Metadata integrates with building contracts to provide:
- Property Information: Building details and specifications
- Ownership History: Transfer and ownership records
- Compliance Data: Regulatory and compliance information
- Audit Records: Building audit and inspection data
Frontend applications can use the metadata for:
- Property Display: Show building information
- Search and Filter: Find properties by criteria
- Portfolio Management: Organize and manage properties
- Analytics: Track property performance
- Packed Structs: Optimized data structures
- Indexing: Gas-efficient filtering mechanisms
- Batch Operations: Multiple updates in single transaction
- Cached Values: Frequently accessed data
- Indexed Filtering: Pre-computed indexes for common queries
- Intersection Logic: Efficient multi-criteria filtering
- Memory Management: Optimized array operations
error ERC721Metadata: not token owner
error ERC721Metadata: token metadata can no longer be modified
error ERC721Metadata: array length mismatch
error ERC721Metadata: invalid array length- Unauthorized access attempts
- Frozen metadata modification
- Array length mismatches
- Invalid token operations
The ERC721 Metadata contract is not upgradeable by design to maintain metadata integrity. For updates:
- Deploy new version
- Migrate existing tokens
- Update integration points
- Maintain backward compatibility
- Token Count: Total tokens minted
- Metadata Entries: Total metadata key-value pairs
- Filter Usage: Query frequency and patterns
- Freeze Events: Metadata freezing activity
// Token events
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
// Metadata events
event MetadataSet(uint256 indexed tokenId, string key, string value);
event MetadataFrozen(uint256 indexed tokenId);
// Collection events
event CollectionMetadataSet(string key, string value);For questions or issues related to the ERC721 Metadata:
- Check the test files for usage examples
- Review the contract source code
- Open an issue in the repository
- Contact the development team
Next Steps: