Metadata renderer
MetadataRenderer builds the JSON that DeedNFT.tokenURI returns. It combines the deed's on-chain
traits with details it stores itself: features, condition, legal information, documents, a gallery,
an animation URL and an external link. It serves one DeedNFT, set with setDeedNFT.
On this page
Source: MetadataRenderer.sol. Tests:
MetadataRenderer.spec.ts. The function and role
summary is in smart-contracts.md.
How tokenURI is served
DeedNFT.tokenURI asks the renderer first. If the renderer is set and returns a non-empty
string, that is the token URI. If it is unset, reverts or returns empty, DeedNFT returns the URI
stored for the token at mint or by updateMetadata.
The renderer returns data:application/json;base64, followed by the encoded JSON.
The JSON
{
"name": "123 Main St, California 90210, USA - Land",
"description": "<the deed's definition trait>",
"image": "ipfs://...",
"background_color": "",
"animation_url": "",
"external_link": "",
"configuration": "<the deed's configuration trait>",
"gallery_images": ["ipfs://..."],
"documents": [{ "docType": "survey", "documentURI": "ipfs://..." }],
"features": ["..."],
"asset_condition": {
"general_condition": "Good",
"last_inspection_date": "2025-01-15",
"known_issues": [],
"improvements": [],
"additional_notes": ""
},
"legal_info": {
"jurisdiction": "California",
"registration_number": "...",
"registration_date": "...",
"documents": [],
"restrictions": [],
"additional_info": ""
},
"attributes": [
{ "trait_type": "Asset Type", "value": "Land" },
{ "trait_type": "Validation Status", "value": "Invalid" }
]
}namedescriptiondefinition traitimagebackground_color, animation_url, external_linkconfigurationconfiguration traitgallery_imagesdocumentsfeaturessetTokenFeaturesasset_conditionsetAssetConditiongeneral_condition is setlegal_infosetTokenLegalInfojurisdiction is setattributesdefinition and configurationAttributes. Each attribute's trait_type is the trait's name in DeedNFT
(getTraitName). The seven mint traits have display names (Asset Type, Validation Status,
Operating Agreement, Validator, Beneficiary, …). A trait set with a string key through
setTrait is named after the key itself, such as streetNumber, unless an admin renames it with
setTraitName. Values are rendered as follows:
assetType:Land,Vehicle,Equipment, orLandfor an Estate.isValidated:ValidorInvalid.validator,beneficiary: a hex address, or left out when zero.- Every other trait: decoded as a string.
The stored name, description and customMetadata are kept but not written to the JSON, and the
per-asset-type default background colours are not used.
Names
name is built from the deed's traits, by asset type. Trait values are read as strings.
{streetNumber} {streetName}, {state} {zipCode}, {country} - Land (or - Estate)streetNumber, streetName, state, zipCode, countryParcel #{parcelNumber}, {state} {zipCode}, {country} - Land (or - Estate)parcelNumber, then as above{year} {make} {model}year, make, model{make} {model}, then {make} Vehicle{manufacturer} {model} (S/N: {serialNumber})manufacturer, model, serialNumber{equipmentType} - {manufacturer} {model}equipmentType{manufacturer} {model}, then {manufacturer} Equipment{Land, Vehicle, Estate or Equipment} #{tokenId}For Land and Estate, the location part is built only when state is set; zipCode and country
are added after it when present.
Images
An unvalidated deed always shows the invalidated image. Otherwise the first of these that is set:
- 1.The stored
image, mirrored from the deed'simagetrait. - 2.The first gallery image.
- 3.The default image for the asset type.
Defaults live in defaultImageURIs: one per asset type, and key 255 for the invalidated image. The
owner sets them with setAssetTypeImageURI and setInvalidatedImageURI; the deploy script sets all
of them, with Estate using the Land image.
Trait mirroring
When a trait changes in DeedNFT, it calls syncTraitUpdate on the renderer. Only the
DeedNFT may call it. The renderer copies these traits into its own storage:
image, background_color, animation_urlname, descriptiongallerystring[]customMetadataRemoving a gallery or customMetadata trait clears the stored copy. If a sync call reverts,
DeedNFT ignores the failure and keeps the trait. Event: MetadataSynced.
Stored details
The deed's owner, any holder of VALIDATOR_ROLE on DeedNFT, and the renderer's owner can set
a deed's details.
setTokenFeatures(tokenId, features)setAssetCondition(tokenId, generalCondition, lastInspectionDate, knownIssues, improvements, additionalNotes)generalCondition must not be emptysetTokenLegalInfo(tokenId, jurisdiction, registrationNumber, registrationDate, documents, restrictions, additionalInfo)jurisdiction must not be emptymanageTokenDocument(tokenId, docType, documentURI, isRemove)docType. Adding an existing type replaces its URI; isRemove deletes itsetTokenAnimationURL(tokenId, url), setTokenExternalLink(tokenId, url)setTokenCustomMetadata(tokenId, metadata)DeedNFT as the customMetadata traitsetTokenGallery(tokenId, imageUrls) needs DEFAULT_ADMIN_ROLE on the renderer. It drops empty
entries and also writes the list to DeedNFT as the gallery trait.
Reads, open to anyone: getTokenFeatures, getAssetCondition, getTokenLegalInfo,
getTokenDocument(tokenId, docType), getTokenDocuments, getTokenGallery,
getTokenAnimationURL, getTokenExternalLink. contractURI returns DeedNFT.contractURI.
Writes emit MetadataUpdate(tokenId) (ERC-4906). Errors: Unauthorized, Invalid (a bad
argument, or no DeedNFT set), Empty.
Roles
DEFAULT_ADMIN_ROLEsetDeedNFT, setTokenGalleryDeedNFT VALIDATOR_ROLE holder, or renderer ownerThe renderer itself holds VALIDATOR_ROLE on DeedNFT, granted by DeedNFT.setMetadataRenderer,
so it can write the customMetadata and gallery traits.
Known limitations
- Estates render as Land in
attributes. TheAsset Typeattribute for an Estate readsLand. The generated name uses- Estatecorrectly. - Non-string traits can break the JSON. Traits outside the special cases are decoded as strings, as are the traits read for the name. A trait stored as a number or boolean (
setTraitvalueType2 or 3), such as a numericyear, can make the renderer revert;DeedNFT.tokenURIthen returns the stored URI instead. Store descriptive traits as strings (valueType1). - Field names differ from common marketplace metadata. The renderer writes
external_linkandgallery_images; the example metadata intest/templatesusesexternal_urlandgallery. - Stored details outlive the token. Burning a deed clears its traits in
DeedNFTbut not the renderer's stored details for that token id.