Skip to content

Anatomy of a descriptor

A descriptor is one JSON file. A file for a contract’s functions is named calldata-<ContractName>.json; a file for an EIP-712 typed-data message is named eip712-<MessageName>.json. Both have the same three sections.

This descriptor tells a wallet how to show the two calls of WETH9, the Wrapped Ether contract on Ethereum: wrapping ETH and unwrapping it.

calldata-WETH9.json
{
"$schema": "../../specs/erc7730-v2.schema.json",
"context": {
"$id": "WETH9",
"contract": {
"deployments": [
{
"chainId": 1,
"address": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
}
]
}
},
"metadata": {
"owner": "Wrapped Ether",
"contractName": "WETH9",
"info": { "url": "https://ethereum.org" }
},
"display": {
"formats": {
"deposit()": {
"intent": "Wrap ETH",
"fields": [
{ "path": "@.value", "label": "Amount", "format": "amount" }
]
},
"withdraw(uint256 wad)": {
"intent": "Unwrap WETH",
"interpolatedIntent": "Unwrap {wad}",
"fields": [
{
"path": "#.wad",
"label": "Amount",
"format": "tokenAmount",
"params": {
"token": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
}
}
]
}
}
}
}

context binds the file to deployments: a list of chainId and address pairs. A wallet uses the descriptor only for a transaction sent to one of them. For an upgradeable contract the address is the proxy’s, the one users send to, while the function signatures come from the implementation’s ABI.

metadata names the owner and the contract, with info.url pointing at more about them. It can also hold constants and enums that the display section refers to. Keep the strings short: hardware wallets have small screens, and the linter warns, for instance, about an owner URL longer than 26 characters.

display.formats has one entry per function, keyed by its full signature with parameter names, such as withdraw(uint256 wad). Each entry has:

  • intent: a short label for the action, such as “Unwrap WETH”.
  • interpolatedIntent (optional): a sentence with formatted values in it. Placeholders are parameter names in braces, such as {wad}.
  • fields: the values to show, in order, each with a path, a label, a format, and params when the format needs them.
  • #.name is a decoded calldata parameter, by its name in the ABI.
  • @.value is the ETH sent with the transaction.
  • An array element takes a dot before its brackets: #.path.[0] is the first element and #.path.[-1] the last. The linter rejects #.path[0].
Format Shows
raw The value as its type would naturally appear
amount An amount of the chain’s own currency, such as ETH
tokenAmount A token amount with its ticker and decimals, from token or tokenPath
tokenTicker A token’s ticker
addressName A trusted name for an address, or the address itself
date A date, from a timestamp or a block height
duration A number of seconds as hours, minutes and seconds
unit A number with a unit symbol after it, such as % or bps, after applying its decimals
enum A human-readable name from an enum in metadata
nftName An NFT’s name, or its token ID
calldata A call embedded in the call, shown with its own descriptor

The schema has the parameters each format takes.

Next: How a descriptor is checked.