Structured Data Clear Signing Format
Abstract
This specification defines a JSON format carrying additional information required to correctly display structured data for human verification on wallet screens or for machine consumption e.g. by transaction simulation engines.
The ERC-7730 specification enriches type data contained in the ABIs and schemas of structured messages (structures like the calldata of an EVM transaction or an EIP-712 message) with additional formatting information and dynamic value interpolation, enabling both human-readable display with contextual intent descriptions and machine-interpretable data processing. For instance, a solidity field containing an amount, encoded as an uint256, can be converted to the right magnitude and appended with the correct ticker for display, or parsed programmatically for transaction simulation.
Wallets and automated systems will use curated ERC-7730 files alongside the raw data to sign in order to construct appropriate interfaces for their respective use cases.
This enables significantly improved signing user experiences and lower end-user risk from frontend and phishing attacks.
Motivation
Properly validating a transaction on a hardware wallet’s screen (also known as Clear Signing) is a key element of good security practices for end users when interacting with any Blockchain. Unfortunately, most data to sign, even enriched with the data structure description (like ABIs or EIP-712 types) are not self-sufficient in terms of correctly displaying them to users for review. Among other things:
- Function name or main message type is often a developer oriented name and does not translate to a clear intent for the user
- Fields in the data to sign are tied to primitive types only, but those can be displayed in many different ways. For instance, integers can be displayed as percentages, dates, etc…
- Some fields require additional metadata to be displayed correctly, for instance token amounts require knowledge of the decimals and the ticker, as well as where to find the token address itself to be correctly formatted.
This specification intends to provide a simple, open standard format to provide wallets with the additional information required to properly format a structured data to sign for review by users.
Providing this additional formatting information requires deep knowledge of the way a smart contract or message is going to be used. It is expected that app developers will be the best placed to write such a file. The intent of an open standard is to only write this file once and have it work with most wallets supporting this standard.
Specification
The keywords “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “NOT RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119 and RFC 8174.
Simple example
The following is an example of how to clear sign a transfer function call on an ERC-20 contract.
{
"$schema": "https://eips.ethereum.org/assets/eip-7730/erc7730-v1.schema.json",
"context": {
"$id": "Example ERC-20",
"contract" : {
"deployments": [
{
"chainId": 1,
"address": "0xdAC17F958D2ee523a2206206994597C13D831ec7"
},
{
"chainId": 137,
"address": "0xc2132D05D31c914a87C6611C10748AEb04B58e8F"
},
{
"chainId": 42161,
"address": "0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9"
}
]
}
},
"metadata": {
"owner": "Example",
"contractName": "Example Token",
"info": {
"url": "https://example.io/",
"deploymentDate": "2017-11-28T12:41:21Z"
}
},
"display": {
"formats": {
"transfer(address to,uint256 value)": {
"intent": "Send",
"interpolatedIntent": "Send {value} to {to}",
"fields": [
{
"path": "value",
"label": "Amount",
"format": "tokenAmount",
"params": {
"tokenPath": "@.to"
}
},
{
"path": "to",
"label": "To",
"format": "addressName"
}
]
}
}
}
}
The $schema key refers to the latest version of this specification json schema (version 1 at the time of writing).
The context key is used to provide binding context information for this file. It can be seen as a set of constraints on the structured data being reviewed, indicating whether the ERC-7730 file is valid for this data. A wallet MUST ensure these constraints are met before ERC-7730 formatting information is applied to the data being signed.
In this example, the context section indicates that the ERC-7730 file should only be applied to the Example smart contract whose deployment addresses are provided. Once the contract is matched, wallets use the display.formats entry itself to derive the function selector, parameter layout, and user-facing labels.
The metadata section contains constants that can be trusted when the ERC-7730 file is applicable for the given context. This section is typically used to:
- Provide displayable information about the recipient of the contract call / message
- Provide displayable values of enumeration or encoded id parameters, typically smart contract / message specific
- Provide common constants used by the various formats defined in the file
In this example, the metadata section contains only the recipient information, in the form of a displayable name (owner key), contract displayable name (contractName key) and additional information (info key) that MAY be used by wallets to provide details about the recipient.
Finally, the display section contains the definitions of how to format each field of targeted contract calls under the formats key.
In this example, the function being described is identified by its human-readable ABI fragment transfer(address to,uint256 value). Wallets strip the parameter names to compute the type-only signature transfer(address,uint256), hash it, and match the resulting selector 0xa9059cbb against the calldata.
- The
intentkey contains a human-readable string that wallets SHOULD display to explain to the user the intent of the function call. - The
fieldskey contains all the parameters formatting information - The
interpolatedIntentkey is a suggested short string representation of intent and fields that wallet CAN use instead of having a dedicated layout.
In this example, the to parameter and the value parameter SHOULD both be displayed, one as an address replaceable by a trusted name (ENS or others), the other as an amount formatted using metadata of the target ERC-20 contract.
Versioning
The version of the specification used by the 7730 file is specified by linking to the reference schema file under the $schema key.
The current version of the specification is 1 at the time of writing. The reference schema for version 1 is here.
Common concepts
Key naming convention
In all the specification, key names starting with $ are internal and have no value beyond readability of the specification file itself. They should not be used in any function to build the UI to review structured data.
Structured Data
This specification intends to be extensible to describe the display formatting of any kind of structured data.
By Structured data, we target any data format that has:
- A well-defined type system; the data being described itself being of a specific top-level type
- A description of the type system, the schema, that should allow splitting the data into fields, each field clearly identified with a path that can be described as a string.
Displaying structured data is often done by wallets to review its content before authorizing an action in the form of a signature over some serialization of the structured data. As such, the structured data is contained in a container structure:
- Container structure has a well-defined signature scheme (a serialization scheme, a hashing scheme, and signature algorithm).
- The container structure does not necessarily follow the same type system as the structured data.
- Wallets receive the full container structure and uses the signature scheme to generate a signature on the overall structure.
Current specification covers EVM smart contract calldata:
- Defined in Solidity
- The schema is the function ABI (derived from the matched
display.formatsfunction fragment) - The container structure is an EVM Transaction serialized in RLP encoding
It also supports EIP-712 messages
- Defined in EIP-712
- The schema is extracted from the type string representation carried in the
display.formatsmessage fragments. - An EIP-712 message is self-contained, the signature is applied to the hashed message itself following EIP-712 specification.
The schema is defined by the binding context together with the selected display entry. For contracts, the matched display.formats key provides the full function signature (types and parameter names); for EIP-712 messages, the matched display.formats provides the type encoding of the message. Both form of type definition allows defining unique paths pointing to specific fields in the data.
Formats are dependent on and defined for the underlying types on the structured data. The Reference section covers formats and types supported by this current version of the specification.
It is sometime necessary for formatting of fields of the structured data to reference values of the container structure. These values are dependent on the container structure itself and are defined in the Reference section.
Path references
This specification uses a limited json path notation to reference values that can be found in multiple json documents.
Limitation to the json path specification are the following:
- Paths MUST use the dot notation, including for slice and array selectors (i.e. an element of an array should be selected through
array_name.[index]) - Only name, index and slices selectors are supported.
- Slices selectors MUST NOT contain the optional step. Start index is inclusive, end index is exclusive. Start or end index can be omitted to indicate the beginning or end of the array.
In addition, additional roots are introduced to support description of paths over multiple files in a single common notation. The root node identifier indicates which document or data location this path references, according to the following table:
| Root node identifier | Refers to | Value location |
|---|---|---|
| # | Path applies to the structured data schema (decoded function parameters for contracts, path in the message types itself for EIP-712) | Values can be found in the serialized representation of the structured data |
| $ | Path applies to the current file describing the structured data formatting, after merging with includes | Values can be found in the merged file itself |
| @ | Path applies to the container of the structured data to be signed | Values can be found in the serialized container to sign, and are uniquely defined per container in the Reference section |
Root nodes and following separator can be omitted, in which case the path is relative to the structure being described. In case there is no encapsulating structure, relative paths refer to the top-level structure described in the current file and are equivalent to #. root node.
For paths referring to structured data fields, if a field has a variable length primitive type (like bytes or string in solidity), a slice selector can be appended to the path, to refer to the specific set of bytes indicated by the slice.
Examples
References to values in the serialized structured data
#.params.amountInorparams.amountInrefers to parameteramountInin the top-level structureparamsas defined by the message schema.#.data.path.[0].path.[-1].toordata.path.[0].path.[-1].torefers to the fieldtotaken from last member ofpatharray, itself taken from first member of enclosingpatharray, itself part of top leveldatastructure.#.params.path.[:20]or#.params.path.[0:20]refers to the first 20 bytes of thepathbyte array#.params.path.[-20:]refers to the last 20 bytes of thepathbyte array#.details.[]refers to the array with the Permit Details of a PermitBatch message- For contract calls (functions only), use decoded argument paths such as
valueor#.order.price.
References to values in the format specification file
$.metadata.enums.interestRateModerefers to the values of an enum defined in the specification file (typically to convert an integer into a displayed string)$.display.definitions.minReceiveAmountrefers to a common definition reused across fields formatting definition
References to values in the container (here an EVM Tx container)
@.valuerefers to the enclosing transaction native currency amount@.torefers to the enclosing transaction destination address (usually, a smart contract bound to this ERC-7730 through thecontextsection)
Value Interpolation
The interpolatedIntent field supports embedding formatted field values directly within intent strings using interpolation syntax. This allows constructing dynamic, context-aware descriptions of transactions and messages as an alternative to using individual intent and fields.
The interpolatedIntent makes transaction intents significantly shorter and easier to read by presenting all relevant information in a single, natural language sentence rather than as separate labeled fields. This is particularly valuable for:
- Reducing the cognitive load on users reviewing transactions
- Displaying concise summaries on resource-constrained devices
- Enabling clear descriptions of batch transactions (e.g., EIP-5792), where multiple operations can be concatenated into a single readable sentence
Interpolation Syntax
Values are interpolated using curly braces containing a path reference: {path}. The path MUST follow the path reference rules and can reference:
- Structured data fields (e.g.,
{to},{params.amountIn}) - Container values (e.g.,
{@.value},{@.from}) - Metadata constants (e.g.,
{$.metadata.constants.nativeAssetAddress})
Interpolated intents MUST only refer to paths that are noted as always visible (i.e. visible key in the field formatter is always or not present).
Formatting Behavior
When a wallet processes an interpolatedIntent:
- The wallet MUST identify all interpolation expressions
{path}in the string - For each expression, the wallet MUST resolve the path and locate the corresponding field format specification in the
fieldsarray - The wallet MUST apply the field’s
formatandparamsto format the value - The wallet MUST replace the interpolation expression with the formatted value
- If any interpolation fails (path not found, formatting error, etc.), the wallet MUST fall back to displaying the regular
intentfield
Escaping
To include literal curly braces in the intent text, escape them by doubling: `` for }.
Examples
Simple token transfer:
{
"intent": "Send",
"interpolatedIntent": "Send {value} to {to}",
"fields": [
{"path": "to", "format": "addressName"},
{"path": "value", "format": "tokenAmount", "params": {"tokenPath": "@.to"}}
]
}
Displays as: “Send 100 USDT to cyberdrk.eth”
Swap with native currency:
{
"intent": "Swap",
"interpolatedIntent": "Swap {amountIn} for at least {amountOutMinimum}",
"fields": [
{"path": "amountIn", "format": "tokenAmount", "params": {"tokenPath": "tokenIn"}},
{"path": "amountOutMinimum", "format": "tokenAmount", "params": {"tokenPath": "tokenOut"}}
]
}
Displays as: “Swap 1000 USDC for at least 0.25 WETH”
Using container values:
{
"intent": "Wrap ETH",
"interpolatedIntent": "Wrap {@.value} ETH for WETH",
"fields": [
{"path": "@.value", "format": "amount"}
]
}
Displays as: “Wrap 0.5 ETH for WETH”
Escaping literal braces:
{
"interpolatedIntent": "Execute with {amount} tokens"
}
Displays as: “Execute {function} with 100 tokens”
Organizing files
Smart contracts and EIP-712 messages are often re-using common interfaces or types that share similar display formatting. This specification supports a basic inclusion mechanism that enables sharing files describing specific interfaces or types.
The includes top-level key is a URL pointing to an ERC-7730 file that MUST follow this specification.
A wallet using an ERC-7730 file including another file SHOULD merge those files into a single reference file. When merging, conflicts between common unique keys are resolved by prioritizing the including file.
Merging field format specifications
Special care must be taken when merging field format specifications. These objects are grouped in an array under the fields key, allowing ordering of field formatters. When merging the two arrays, a wallet SHOULD:
- Merge together objects sharing the same
pathvalue, overriding parameters of the included file with those of the including file. - Append objects with
pathvalues not part of the included file to the resulting array.
Example
This file defines a generic ERC-20 interface for a single approve function:
{
"display": {
"formats": {
"approve(address spender,uint256 value)": {
"intent": "Approve",
"fields": [
{
"path": "spender",
"label": "Spender",
"format": "addressName"
},
{
"path": "value",
"label": "Amount",
"format": "tokenAmount",
"params": {
"tokenPath": "@.to",
"threshold": "0x8000000000000000000000000000000000000000000000000000000000000000",
"thresholdLabel": "Unlimited"
}
}
]
}
}
}
}
Note that there are no keys for binding the contract to a specific address or owner, nor any contract specific metadata.
The following file would include this generic interface and bind it to the specific USDT contract, overriding the threshold value to one relative to USDT:
{
"context": {
"$id": "Example Contract",
"contract" : {
"deployments": [
{
"chainId": 1,
"address": "0xdAC17F958D2ee523a2206206994597C13D831ec7"
}
]
}
},
"includes": "./example-erc20.json",
"metadata": {
"owner": "Example",
"contractName": "Example Token",
"info": {
"url": "https://example.io/",
"deploymentDate": "2017-11-28T12:41:21Z"
},
"token": {
"ticker": "STABLE",
"name": "Example Stablecoin",
"decimals": 6
}
},
"display": {
"formats": {
"approve(address spender,uint256 value)": {
"fields": [
{
"path": "value",
"params" : {
"threshold": "0xFFFFFFFFFFFFFFFFFF"
}
}
]
}
}
}
}
Note that the keys under context.contract would be merged together to construct a full contract binding object. The field formatter value parameter threshold is overridden with value 0xFFFFFFFFFFFFFFFFFF.
Context section
The context section describes a set of constraints that must be verified by the structured data and container structure before formatting them using the ERC-7730 file. A wallet MUST verify that the structured data and container it is trying to sign matches the constraints of the context section.
The current version of this specification only supports two types of binding context, EVM smart contracts and EIP-712 domains.
All context support an $id sub-key as an internal identifier (only relevant to provide a human-readable name to the ERC-7730 file)
EVM smart contract binding context (denoted ‘calldata’)
The contract sub-key is used to introduce an EVM smart contract binding context, with the following constraints expressed as sub-keys of contract.
contract.deployments
An array of deployments options. Wallets MUST verify that the target chain and contract address of the containing transaction MUST match one of these deployment options.
A deployment option is an object with:
chainId: an EIP-155 identifier of the chain the described contract is deployed on.address: the address of the deployed contract on specifiedchainIdchain.
contract.abi (deprecated)
Legacy ABI attachment kept for backward compatibility. Authors SHOULD prefer display.formats and avoid relying on this key, as it will be removed in a future revision.
contract.factory
An object describing the factory used to deploy smart contracts that can be clear signed using the ERC-7730 file.
A factory is a json object with:
deployEventkey, specifying the solidity signature of the events emitted when deploying a clear-signable contract.deploymentskey: an array of deployment options as incontract.deployments. These deployments represent the addresses at which the factory is deployed.
To verify a factory constraints a wallet MUST check that:
- The current transaction destination address is included in an event of signature
factory.deployEvent - The emitter of the event is a factory contract deployed at an address matching one of the deployment option in
factory.deployments
EIP-712 messages binding context (denoted ‘messages’)
- The
eip712sub-key is used to introduce an EIP-712 message type to bind to:
eip712.schemas (deprecated)
Legacy Schema attachment kept for backward compatibility. Authors SHOULD prefer display.formats and avoid relying on this key, as it will be removed in a future revision.
eip712.domain
The domain constraint is a json object with simple key-value pairs, describing a set of values that the EIP-712 Domain of the message MUST match.
A wallet MUST verify that each key-value pair in this domain binding matches the values of the domain key-value pairs of the message. Note that the message can have more keys in its domain than those listed in the ERC-7730 file. An EIP-712 domain is a free-form list of keys, but those are very common to include:
name: the name of the message verifierversion: the version of the messagechainID: an EIP-155 identifier of the chain the message is bound toverifyingContract: the address the message is bound to
Note that chainId and verifyingContract can also be bound to their values thanks to the eip712.deployments constraint, in a more flexible way (supporting multiple deployment values).
eip712.deployments
An array of deployments options.
When an eip712.deployments constraint is set, the wallet MUST verify that:
- The message being displayed has both
domain.chainIdanddomain.verifyingContractkeys - The
chainIdandverifyingContractvalues in the domain match ONE of the deployment option specified ineip712.deployments
A deployment option is an object with:
chainId: an EIP-155 identifier of the chain the described contract is deployed on.address: the address of the deployed contract on specifiedchainIdchain.
eip712.domainSeparator
An hex string containing the value of the domainSeparator to check.
Wallet MUST verify that the message EIP-712 Domain hashes (as defined in EIP-712) to the value in eip712.domainSeparator.
When the exact construction of the EIP-712 domain is not known (for instance, when the smart contract code only contains the hash value of the domain separator), domainSeparator and domain.verifyingContract can still be used to target the right message recipients.
Examples
{
"context" : {
"eip712": {
"domain": {
"name": "Permit2"
},
"deployments": [
{
"chainId": 1,
"address": "0x000000000022D473030F116dDEE9F6B43aC78BA3"
},
{
"chainId": 42161,
"address": "0x000000000022D473030F116dDEE9F6B43aC78BA3"
}
]
}
}
}
The previous snippet defines a context for a Permit2 EIP-712 message (types have been omitted for readability).
The domain key indicates that the message signed domain MUST contain a name key of value Permit2. The deployments key means that the domain must contain both chainId and verifyingContract keys and they MUST match one of the deployment options (here, on ETH mainnet and Arbitrum).
Metadata section
The metadata section contains information about constant values relevant in the scope of the current contract / message (as matched by the context section).
In the context of wallets and clear signing, these constant values are either used to construct the UI when approving the signing operation, or to provide parameters / checks on the data being signed. But these constant values are relevant outside of the scope of wallets, and should be understood as reference values concerning the bound contract / message.
All keys but the metadata.owner key are optional.
metadata.owner
A key containing a displayable name of the owner of the contract or of the verifying contract for a message.
Wallet MAY use this value to display the target of the interaction being reviewed.
metadata.contractName
A key containing a displayable name of the contract or of the verifying contract for a message.
Wallet MAY use this value to display the target of the interaction being reviewed.
metadata.info
A key containing additional structured info about the owner of the contract:
deploymentDateis the date of deployment of the contract (or verifying contract)urlis the official URL of the owner
A wallet MAY use this information to display additional details about the targeted contract.
metadata.token
The token key is only relevant for contract ERC-7730 files and only for contracts supporting an ERC-20 interface.
It contains the ERC-20 metadata when the contract itself does not support the optional calls to retrieve it. It SHOULD NOT be present if the contract does support the name(), symbol() and decimals() smart contract calls.
The ERC-20 token metadata for the contract described is in the sub-keys name, ticker and decimals and contains the values that the correponding function calls would have returned.
metadata.constants
This key contains in a json object all the constants that can be re-used as parameters in the formatters, or that make sense in the context of this contract / message.
It is a list of key / value pairs, the key being used to reference this constant (as a path starting with a root node $. i.e. $.metadata.constants.KEY_NAME).
Example
{
"metadata": {
"constants": {
"nativeAssetAddress": "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"
}
}
}
This snippet introduces a constant nativeAssetAddress (an address typically used to represent the native network underlying currency, which is smart contract specific). This constant can be referenced using the path $.metadata.constants.nativeAssetAddress
metadata.maps
Sometimes constants are dependant on some part of the context of the transaction, for instance a token address used by a contract might depend on the deployment chain. maps key allow an efficient representation of those types of context-dependent constants.
Each key under the maps object is a map name. Map names can be referred to in the display section formatters by using a path starting with root node $. (i.e. $.metadata.maps.MAP_NAME). Maps can be used anywhere a constant would be used.
A map is a json object with two keys:
- The
keyPathkey contains a path to the context-dependent data used to select the right map value - The
valueskey contains a list of key / value pairs, each key being used to match the data pointed to in thekeyPath. The corresponding value is used as the context-dependent constant when referenced in thedisplaysection.
A wallet MUST replace a path to a map using the value matching the keyPath parameter. In case no values matches the wallet MUST consider the 7730 file invalid for the transaction.
Examples
{
"metadata": {
"maps": {
"underlyingToken": {
"keyPath": "@.chainId",
"values" : {
"1": "0xaabbccddeeff...",
"17000": "0x112233445566..."
}
}
}
},
"display": {
"formats": {
"deposit(amount)" : {
"intent": "Deposit",
"fields": [
{
"path": "#.amount",
"label": "Deposit Amount",
"format": "tokenAmount",
"params": {
"token": "$.metadata.maps.underlyingToken"
}
}
]
}
}
}
}
This example shows a deposit function that takes as only input the amount of a token hardcoded in the contract. The token address deposited changes based on the chain the contract is deployed to.
The underlyingToken map allows defining the hardcoded values of the underlying token based on the chainId of the transaction context (as specified in "keyPath": "@.chainId").
metadata.enums
The enums key contains displayable values for parameters that are enumerations (in a loose sense including parameters taking fixed number of well known values). These enums are used to replace specific parameter values with a human-readable one.
Each key of the enums object is an enumeration name. Enumeration names can be referred to in the display section formatters by using a path starting with root node $. (i.e. $.metadata.enums.ENUM_NAME).
An enum is a json object with a flat list of key / value pairs, each key being the enumeration value to replace, and the value being the display string to use instead of the enumeration value.
Examples
{
"metadata": {
"enums": {
"interestRateMode": {
"1": "stable",
"2": "variable"
}
}
}
}
This snippet introduces an enumeration describing the displayable values of an integer parameter used to represent multiple modes of interest rates. It can be referenced using the path $.metadata.enums.interestRateMode.
{
"display": {
"formats": {
"repay(address asset,uint256 amount,uint256 interestRateMode)": {
"$id": "repay",
"intent": "Repay loan",
"interpolatedIntent": "Repay {amount} of {interestRateMode} rate loan",
"fields": [
{
"path": "amount",
"format": "tokenAmount",
"label": "Amount to repay",
"params": { "tokenPath": "asset" }
},
{
"path": "interestRateMode",
"format": "enum",
"label": "Interest rate mode",
"params": { "$ref": "$.metadata.enums.interestRateMode" }
}
]
}
}
}
}
In this example, the interestRateMode field is formatted using the enumeration defined under $.metadata.enums.interestRateMode.
Display section
The display section contains the actual formatting instructions for each field of the bound structured data. It is split into two parts, a display.definitions key that contains common formats that can be re-used in the other parts and a display.formats key containing the actual format instructions for each function / message type bound to the specification file.
display.definitions
The definitions key is an object in which each sub-key is a field format specification. The sub-key name is the name of the common definition and is used to refer to this object in the form of a path starting with root node $. (i.e. $.display.definitions.DEF_NAME).
Definitions don’t usually include the path or value key of a field format specification, since they are intended for re-use in other fields specifications, that will specify locally what path they apply to.
Example
{
"display": {
"definitions": {
"sendAmount": {
"label": "Amount to Send",
"format": "tokenAmount",
"params": {
"tokenPath": "fromToken",
"nativeCurrencyAddress": "$.display.constants.addressAsEth"
}
}
}
}
}
This snippet defines a common formatter for an amount to send that can be used by a simple reference to the path $.display.definitions.sendAmount.
display.formats
The formats key is an object containing the actual information used to format the structured data. It is a json object in which each sub-key is a specific function call (for contracts) or a specific message type (for EIP-712) being described. The values are each a structured data format specification.
Contract keys
For contract calldata, this specification only covers functions. Each key MUST be a human-readable ABI function fragment that includes parameter names, for example transfer(address to,uint256 value) or submitOrder((address token,uint256 amount) order,bytes32 salt).
- Keys MUST use canonical Solidity type names:
uint256,bytes32,address,bool, tuple syntax(…), dynamic arraystype[], and fixed arraystype[N]. Aliases (e.g.,uint) are NOT permitted, commas MUST NOT be followed by spaces, and there MUST be exactly one space between each type and its parameter name. - Parameter names in the fragment MUST match the names used throughout the formatting specification; wallets derive all display paths from these names.
- Overloaded functions are distinguished solely by their type signatures. Parameter names do not affect selector matching.
Selector matching (contracts)
Wallets MUST match calldata to a display.formats entry using the following procedure:
- Parse the key and drop parameter names to obtain the canonical type-only signature (e.g.,
transfer(address,uint256)). - Compute
keccak256(<type-only signature>)[:4]. - Compare the resulting selector to the transaction calldata selector; a match selects the corresponding format specification.
If multiple keys share the same type-only signature, wallets MUST treat this as an invalid descriptor.
Decoding and parameter names
Once a format entry is selected, wallets MUST decode calldata arguments using the canonical type vector derived from the key. Parameter names, fields[].path entries, and {placeholders} in interpolatedIntent MUST all use the names from the key (e.g., value, order.amount, recipients[0]).
Paths are relative to the matched function parameters unless prefixed by another root node; for example, to, order.price, and recipients[0] point to decoded arguments, while @.value references the transaction container.
Placeholders in strings MUST use {<path>} with the same path semantics.
Unknown selectors
If no key matches the calldata selector, wallets SHOULD display a safe fallback (for example, “Unknown function” alongside raw arguments) and MUST NOT attempt to apply any unrelated format specifications.
Minimal contract examples
{
"display": {
"formats": {
"transfer(address to,uint256 value)": {
"intent": "Send",
"interpolatedIntent": "Send {value} to {to}",
"fields": [
{ "path": "value", "label": "Amount", "format": "tokenAmount" },
{ "path": "to", "label": "To", "format": "addressName" }
]
}
}
}
}
{
"display": {
"formats": {
"submitOrder((address token,uint256 amount,uint256 price) order,bytes32 salt)": {
"intent": "Place order",
"interpolatedIntent": "Buy {order.amount} @ {order.price}",
"fields": [
{ "path": "order.token", "label": "Token", "format": "addressName" },
{ "path": "order.amount", "label": "Amount", "format": "number" },
{ "path": "order.price", "label": "Price", "format": "number" },
{ "path": "salt", "label": "Salt", "format": "bytes32" }
]
}
}
}
}
{
"display": {
"formats": {
"airdrop(address[] recipients,uint256[3] values)": {
"intent": "Airdrop",
"interpolatedIntent": "Airdrop to {recipients[0]} (+{recipients.length} total)",
"fields": [
{ "path": "recipients[0]", "label": "First recipient", "format": "addressName" },
{ "path": "values[0]", "label": "Tier 1", "format": "tokenAmount" }
]
}
}
}
}
EIP-712 keys
For EIP-712, the key names MUST be the string returned by the encodeType function defined in EIP-712 specification applied to the primary type of the message.
During the EIP-712 signature process, wallets will compute the type hash from the message. This type hash MUST match the hash computed from the display.formats key in use: keccak256(encodeType(typeOf(s))) == keccak256(TYPE_KEY)
Example
In the sample EIP-712 message included in the specification here, the key used to descrie the message would be the string Mail(Person from,Person to,string contents)Person(string name,address wallet).
Structured data format specification
A Structured data format specification is used to describe how to format all the fields of a single function or EIP-712 message. It is contained in a single json object under each sub-keys of display.formats.
$id
This key is purely internal and used to specify a human-readable identifier for this specification.
intent
Use to specify the intent of the function call or message signature in a user-friendly way.
An intent can take two forms:
- A simple string with human-readable content
- A json object with a flat list of string key-value pairs, representing more complex intents. Both keys and values should be human-readable and user-friendly.
Wallets SHOULD use this intent value to display a clear intent when reviewing the structured data before signature. When displaying a complex json intent, it is expected that keys represent labels, and values should be displayed after their label.
{
"display": {
"formats": {
"withdraw(uint256)": {
"intent": {
"Native Staking": "Withdraw",
"Rewards": "Consensus & Exec"
}
}
}
}
}
This snippet defines an intent for a withdrawal function on a contract, with an expectation that the intent would be displayed in a structured way on the wallet screen.
interpolatedIntent
A string containing an intent description with embedded field values using interpolation syntax.
Wallets MUST display either interpolatedIntent strings or screens generated from intent and individual fields.
Wallets MAY display both fields, with interpolatedIntent as the primary description.
Interpolated paths MUST reference fields that have corresponding format specifications in the fields array. The formatting applied during interpolation MUST match the formatting that would be applied if the field were displayed separately.
Example with complex intent:
{
"intent": {
"Action": "Approve",
"Type": "Batch"
},
"interpolatedIntent": "Approve {spender} to spend up to {amount} on your behalf until {deadline}",
"fields": [
{"path": "spender", "label": "Spender", "format": "addressName"},
{"path": "amount", "label": "Amount", "format": "tokenAmount"},
{"path": "deadline", "label": "Deadline", "format": "date"}
]
}
fields
The fields key defines formatting information for individual fields of the structured data (function or message).
fields is an array of elements, each element being either:
- A single field format specification
- A reference to a format specification in the
definitionssection: by declaring an object with two keys, apathkey with the path to the field being formatted, and a$refkey with a path to the internal definition.- A reference object can override a field format specification
paramsby including its ownparamssub-key, whose values will take precedence over the common definition
- A reference object can override a field format specification
- A group of fields, defined in Group format specification
Grouping fields allows for both control of the way wallets should order the display of fields and recursivity of fields definitions, which can be a more readable form for the 7730 file itself.
Examples
Let’s assume the following solidity contract
pragma solidity ^0.8.0;
contract MyContract {
struct MyParamType {
string name;
uint256 value;
}
// Function declaration
function myFunction(address account, uint256 amount, MyParamType memory param) public {
// Function logic here
}
}
The following ERC-7730 shows examples for the three kinds of fields options
{
"display": {
"formats": {
"myFunction(address account,uint256 amount,(string name,uint256 value) param)" : {
"fields": [
{
"path": "account",
"$ref": "$.display.definitions.sendTo",
"params": { "type": "eoa" }
},
{
"path": "amount",
"label": "Number of items",
"format": "raw"
},
{
"path": "param",
"fields": [
{ "path": "name", "$ref": "$.display.definitions.itemName" },
{ "path": "value", "$ref": "$.display.definitions.itemReference" }
]
}
]
}
}
}
}
- The
accountfield is an example of an internal reference (reference not included in the example), overriding the referencetypeparameter with another value. - The
amountfield shows an example of a simple formatter, displaying an int in its natural representation with a label. - The
paramfield shows an example of defining formatters with a recursive structure, ending up defining two embedded formatters for paths#.param.nameand#.param.value
Note that the recursive definition is equivalent to the following definition, which is the preferred form:
{
"display": {
"formats": {
"myFunction(address account,uint256 amount,MyParamType param)" : {
"fields": [
{ "path":"param.name", "$ref": "$.display.definitions.itemName" },
{ "path":"param.value", "$ref": "$.display.definitions.itemReference" }
]
}
}
}
}
Field format specification
A field format specification is a json object defining how to format a single field of the structured data.
path / value
The path is the absolute or relative location of the field in the structured data, following the rules in the path section. Instead of path, a literal value may be provided to display a constant without looking up a field.
label
A displayable string shown before the formatted field value.
format
Indicates how the field value must be transformed for display. Supported format identifiers are listed in the Reference section.
params
Optional object containing formatter-specific parameters. Available parameters are described alongside each format in the Reference section.
Array references in parameters
When a formatter applies to an array, the parameters CAN also reference a path to an array. In that case, wallets should format each element of the array using the parameter value at the same index as the current element being formatted. Parameter arrays should be the same length as the formatted array else the wallet SHOULD raise an error.
See example-array-iteration.json for examples on array iteration.
$id
Optional internal identifier to help authors distinguish or reference the field definition; not intended for end-user display.
visible
Optional rule to apply before displaying a field. Rules allow defining under which conditions a field should be displayed. If not specified, the field is assumed to be always visible (ie, default value is "visible"="always"). Current supported rules are:
- A simple string with values among
[never,always,optional], with the following meaningnever": Never display this field to users (this field should be excluded from the UI).always: Always display this field to users (this field is required).optional: Wallets MAY display this field if possible or sensible.
- An object with keys representing complex rules, with the following complex rules supported:
ifNotInkey with an array of values to make the field visible only when the field value does NOT match any of the specified valuesmustMatchkey with an array of values to make the field always hidden AND check its value against the list. Wallet should raise an error if the value does not match the list.
separator
Optional separator string to display before each element of an array. Can only be used be used for formatters that are applied to a path pointing to an array. Each element of the array is formatted using the provided formatter, and the separator value is displayed before each element. separator is a string using the interpolated syntax with one specific replacement {index} that gets replaced with the index of current element being displayed.
Group format specification
A Group format specification is a json object defining how to format a group of fields of the structured data.
path
The absolute or relative path to the field location in the structured data, as described in the path section.
path is optional, in which case the group refer to the path of the embedding fields definition.
label
Optional displayable string that should be shown before displaying the fields described in this group.
iteration
Key controlling how arrays in the group should be iterated over when formatting the group. It can only be applied to groups containing arrays.
It supports two modes sequential and bundled:
- In
sequentialmode, arrays are displayed fully one after the other - array_0[0] array_0[1] .. array_0[N] array_1[0] array_1[1] .. array_1[M] - In
bundledmode, elements of the arrays gets bundled together - array_0[0] array_1[0] array_0[1] array_1[1] .. array_0[N] array_1[N]- In this mode, the arrays of the group MUST be the same size else the wallet SHOULD return an error
Defining groups of fields allows for controlling the order in which wallets should display fields. Wallets SHOULD display fields in the order in which they appear in the group defintion. For this grouping purpose, the top level Structured data format specification is also considered a group.
Recursive path references work by concatenating the paths of the parents structured data format specification, all the way to the leaf, to build a full reference path to the field being described. The leaf should be either a field format specification, or a reference to a field format specification in the definitions section.
This recursivity allows structuring the ERC-7730 file itself, but is NOT RECOMMENDED. It is expected that resource limited wallets will only support very limited recursivity in the ERC-7730 file itself, so the initial intent of the spec is to “flatten” the list of fields to display using the path mechanics.
See example-array-iteration.json for examples on grouping and group iteration control.
Slices in paths
A slice can be applied at the end of paths.
A slice on a primitive type like uint256, bytes and string means that the associated field format specification MUST only be applied to the corresponding slice of bytes of the underlying data.
A slice on an array type means that the associated field format specification or recursive structured data format specification MUST be applied to ALL the array elements part of the slice.
Example
{
"display": {
"formats": {
"exactOutput((bytes path, address recipient, uint256 deadline, uint256 amountOut, uint256 amountInMaximum) params)": {
"fields": [
{
"path": "params.amountInMaximum",
"label": "Maximum Amount to Send",
"format": "tokenAmount",
"params": {
"tokenPath": "params.path.[0:20]"
}
}
]
}
}
}
}
The tokenPath parameter uses a slice on a bytes value, indicating only the first 20 bytes should be taken as the address and used as the reference for the formatting of the corresponding token amount.
{
"display": {
"formats": {
"buyOnMySwap(address router,uint256 amountIn,uint256 amountOutMin,address tokenPathStart,uint256[] pools)": {
"fields": [
{
"path": "pools.[-1]",
"label": "Last pool",
"format": "raw"
}
]
}
}
}
}
This examples uses an array slice to indicate that only the last element of the pools array should be displayed using the raw format.
{
"display": {
"formats": {
"ermitBatch(PermitDetails[] details,address spender,uint256 sigDeadline) PermitDetails(address token,uint160 amount,uint48 expiration,uint48 nonce)": {
"intent": "Approve token spending",
"interpolatedIntent": "Approve {spender} to spend multiple tokens until {sigDeadline}",
"fields": [
{
"path": "details.[]",
"fields": [
{
"path": "amount",
"label": "Amount allowance",
"format": "tokenAmount",
"params": {
"tokenPath": "token"
}
},
{
"path": "expiration",
"label": "Approval expires",
"format": "date",
"params": {
"encoding": "timestamp"
}
}
]
},
{
"path": "spender",
"label": "Spender",
"format": "addressName"
},
{
"path": "sigDeadline",
"label": "Signature Deadline",
"format": "date",
"params": {
"encoding": "timestamp"
}
}
]
}
}
}
}
This example uses a full array selector details.[] to apply the list of the underlying two underlying formats to ALL the elements of the details array.
Embedded Calldata
Embedded calldata is used when a parameter of a smart contract function contains the calldata for another function call to a smart contract.
This pattern is common in contract interactions where one function call triggers another function call as part of its execution.
For example, the permitAndCall function verifies a permit and then executes another function within the same contract using the provided embedded calldata.
Here is an example of how to format embedded calldata:”
{
"display": {
"formats": {
"permitAndCall(bytes permit,bytes action)": {
"intent": "Execute with permit",
"fields": [
{
"path": "action",
"label": "Swap",
"format": "calldata",
"params": {
"calleePath": "@.to",
}
}
],
"required": ["action"],
"excluded": ["permit"]
}
}
}
}
In this example, the permitAndCall function accepts two parameters: permit and action.
The action parameter contains the calldata for a subsequent function call.
The format field is set to calldata, instructing the wallet to interpret the action parameter as embedded calldata.
The calleePath parameter defines the path to the address of the target contract, while the optional selector parameter specifies the function selector if it is not the first 4 bytes of the calldata.
When displaying the transaction, the wallet will attempt to resolve an ERC-7730 descriptor for the embedded calldata using the callee address and the selector.
In some cases, embedded calldata may require a transaction value and/or a spender to be properly clear-signed, as the underlying function might rely on a value passed from the parent transaction (e.g., for swaps or staking).
To handle this, the amountPath and/or spenderPath parameters can be used to specify these values explicitly.
{
"display": {
"formats": {
"permitAndCall(bytes permit,address target,bytes action)": {
"intent": "Execute with permit",
"fields": [
{
"path": "action",
"label": "Swap",
"format": "calldata",
"params": {
"calleePath": "target",
"amountPath": "@.value",
"spenderPath": "@.to"
}
}
],
"required": ["action", "target"],
"excluded": ["permit"]
}
}
}
}
Reference
Container structure values
This section describes all container structure supported by this specification and possible references path to relevant values.
EVM Transaction container
| Value reference | Value Description | Examples |
|---|---|---|
| @.from | The address of the sender of the transaction | |
| @.value | The native currency value of the transaction | |
| @.to | The destination address of the containing transaction, ie the target smart contract address | |
| @.chainId | The chainId of the transaction |
EIP-712 container
| Value reference | Value Description | Examples |
|---|---|---|
| @.from | The address of the signer of the message | |
| @.value | EIP-712 have no underlying currency value transferred, so a wallet MAY interpret it as 0 | |
| @.to | The verifying contract address, when known. If not known a wallet SHOULD reject using the ERC-7730 file to clear sign the message | |
| @.chainId | The chainId of the verifying contract, when known. . If not known a wallet SHOULD reject using the ERC-7730 file to clear sign the message |
Field formats
In the following references, the format title is the value to use under the format key of a field format specification.
Integer formats
Formats usable for uint/int solidity types.
raw |
|
|---|---|
| Description | Display the integer as a raw int in natural, localized representation |
| Parameters | None |
| Examples | Value 1000 displayed as 1000 |
amount |
|
|---|---|
| Description | Display as an amount in native currency, using best ticker / magnitude match |
| Parameters | None |
| Examples | Value 0x2c1c986f1c48000 is displayed as 0.19866144 ETH |
tokenAmount |
|
|---|---|
| Description | Convert value using token decimals, and append token ticker name. If value is above optional threshold, display instead message with ticker. |
| Parameters | — |
tokenPath or token |
path reference, or constant value for the address of the token contract. Used to associate correct ticker. If ticker is not found or tokenPath/token is not set, the wallet SHOULD display the raw value instead with an “Unknown token” warning |
nativeCurrencyAddress |
Either a string or an array of strings. If the address pointed to by tokenPath is equal to one of the addresses in nativeCurrencyAddress, the tokenAmount is interpreted as expressed in native currency |
threshold |
integer value, above which value is displayed as a special message. Optional |
message |
message to display above threshold. Optional, defaults to “Unlimited” |
| Examples | — |
1 DAI |
Field value = 1000000 tokenPath =0x6B17…1d0F (DAI, 6 decimals) |
Unlimited DAI |
Field value = 0xFFFFFFFF token =0x6B17…1d0F (DAI, 6 decimals) threshold “0xFFFFFFFF” |
Max DAI |
Field value = 0xFFFFFFFF tokenPath =0x6B17…1d0F (DAI, 6 decimals) threshold “0xFFFFFFFF” message = “Max” |
0.002 ETH |
Field value = 2000000000000000 tokenPath = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE nativeCurrencyAddress = [“0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE”] |
nftName |
|
|---|---|
| Description | Display value as a specific NFT in a collection, if found by wallet, or fallback to a raw int token ID if not |
| Parameters | — |
collectionPath or collection |
A path reference, or constant value for the collection address |
| Examples | — |
Collection Name: BoredApeYachtClub Token ID: 1036 |
Field Value = 1036 collectionPath = ““0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D |
date |
|
|---|---|
| Description | Display int as a date, using specified encoding. Date display RECOMMENDED use of RFC 3339 |
| Parameters | — |
"encoding": "timestamp" |
value is encoded as a unix timestamp |
"encoding": "blockheight" |
value is a blockheight and is converted to an approximate unix timestamp |
| Examples | — |
2024-02-29T08:27:12 |
Field Value = 1709191632 encoding = “timestamp” |
2024-02-29T09:00:35 |
Field Value = 19332140 encoding = “blockheight” |
duration |
|
|---|---|
| Description | Display int as a duration interpreted in seconds and represented as a duration HH:MM:ss |
| Parameters | None |
| Examples | — |
02:17:30 |
Field Value = 8250 |
unit |
|
|---|---|
| Description | Value is converted to a float using decimals (value / 10^decimals) and displayed appending the corresponding unit. If prefix is true, the value is further converted to scientific representation, minimizing the significand and converting the exponent to an SI prefix added in front of the unit symbol |
| Parameters | — |
base |
The symbol of the base unit, an SI unit or other acceptable symbols like “%”, “bps” |
decimals |
Number of decimals in integer representation, defaults to 0 |
prefix |
A boolean indicating whether an SI prefix should be appended, defaults to False |
| Examples | — |
10h |
Field Value = 10 base = “h” |
1.5d |
Field Value = 15 base = “d” decimals = 1 |
36ks |
Field Value = 36000 base = “s” prefix = True |
enum |
|
|---|---|
| Description | Value is converted using referenced constant enumeration values |
| Parameters | — |
$ref |
An internal path (starting with root node $.) to an enumerations in metadata.constants |
| Examples |
String formats
Formats usable for strings.
raw |
|
|---|---|
| Description | Display as an UTF-8 encoded string |
| Parameters | None |
| Examples | — |
Ledger |
Field Value = [‘4c’,’65’,’64’,’67’,’65’,’72’] |
Bytes formats
Formats usable for bytes
raw |
|
|---|---|
| Description | Display byte array as an hex-encoded string |
| Parameters | None |
| Examples | — |
123456789A |
Field Value = Value [‘12’,’34’,’56’,’78’,’9a’] |
calldata |
|
|---|---|
| Description | Contains a call to another smart contract or to another function within the same contract. To resolve an ERC-7730 descriptor for this embedded calldata, use the callee and selector parameters. If no matching ERC-7730 descriptor is found or if tha wallet does not support embedded calldata, it MAY display a hash of the embedded calldata instead, with target calleePath resolved to a trusted name if possible. |
| Parameters | — |
calleePath or callee |
A path reference or a constant value specifying the address of the contract being called. |
selectorPath or selector |
Optional. If omitted, the first 4 bytes of the calldata are used as the selector. |
chainIdPath or chainId |
Optional. Specifies the chain ID if it differs from the current contract’s chain. |
amountPath or amount |
Optional. Specifies the native currency amount associated with the calldata, if applicable. If provided, the ERC-7730 descriptor for this embedded calldata MAY reference this value using the @.value container path. |
spenderPath or spender |
Optional. Specifies the spender address associated with the calldata, if applicable. If provided, the ERC-7730 descriptor for this embedded calldata MAY reference this value using the @.from container path. |
| Examples |
Address
Formats usable for address
raw |
|
|---|---|
| Description | Display address as an EIP-55 formatted string. Truncation is device dependent |
| Parameters | None |
| Examples | — |
0x5aAe...eAed |
Field Value 0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed |
addressName |
|
|---|---|
| Description | Display address as a trusted name if a trusted source exists, an EIP-55 formatted address otherwise. See next section for a reference of trusted sources |
| Parameters | — |
types |
An array of expected types of the address (see next section). If set, the wallet SHOULD check that the address matches one of the types provided |
sources |
An array of acceptable sources for names (see next section). If set, the wallet SHOULD restrict name lookup to relevant sources |
senderAddress |
Either a string or an array of strings. If the address pointed to by addressName is equal to one of the addresses in senderAddress, the addressName is interpreted as the sender referenced by @.from |
| Examples | — |
vitalik.eth |
Field Value 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 types = [“eoa”] (Externally Owned Account) sources = [“ens”] (Ethereum Name Service) |
Uniswap V3: WBTC-USDC |
Field Value 0x99ac8cA7087fA4A2A1FB6357269965A2014ABc35 types = [“contract”] |
Sender |
Field Value 0x0000000000000000000000000000000000000000 senderAddress = [“0x0000000000000000000000000000000000000000”] types = [“eoa”] |
| tokenTicker | |
|——————-|———————————————————————————————-|
| Description | Display address as an ERC-20 token ticker |
| Parameters | None |
| Examples | — |
| MYTOKEN | Field Value 0xaabbccddeeff…. |
Address types and sources
Address names trusted sources specify which type and source of trusted names SHOULD be used to replace an address with a human-readable names.
When specified a wallet MUST only use specified sources to resolve address names. Wallet MUST verify the type of the address if able to.
When omitted, a wallet MAY use any source to resolve an address.
| Address type | Description |
|---|---|
| wallet | Address is an account controlled by the wallet |
| eoa | Address is an Externally Owned Account |
| contract | Address is a well known smart contract |
| token | Address is a well known ERC-20 token |
| collection | Address is a well known NFT collection |
A wallet MAY verify that a wallet address is in fact controlled by the wallet, and reject signing if not the case.
Sources values are wallet manufacturer specific. Some example values could be:
| Source type | Description |
|---|---|
| local | Address MAY be replaced with a local name trusted by user. Wallets MAY consider that local setting for sources is always valid |
| ens | Address MAY be replaced with an associated ENS domain |
Rationale
Human readability
It is expected that the main limitation to adoption of ERC-7730 will be the complexity of writing this interface description file compared to interest of writing it.
This drove a few choices when introducing this ERC specification:
- The form of an ERC itself will allow usage of these file by any wallets (Hardware or Software, without restrictions), and in turn drive up the benefits for app developers to provide their own ERC-7730 description
- The specification is intended to be directly readable by developers, in order to facilitate direct edition by developers.
- In addition, a set of edition tools will be created and open sourced to ease visualization of the results for end users
Wallet limitations
Wide support by wallets is key for adoption of this specification.
Hardware wallets tend to have more limited capabilities that will impact what they can display securely, especially since the intention of the specification is to create a strong security binding between the spec and the data being reviewed.
This consideration is driving a few choices done for ERC-7730:
- Complex UI constructs like layouts, grouping and re-ordering of fields have been left to a wallet specific section, yet unspecified. After a time, we may see patterns emerge between wallets in terms of minimal features.
- Representation of fields has been created to allow “flattening” the list of fields, when handling complex message structures. This flattened representation is expected to work better with Hardware wallets in particular, and is recommended at first.
- Some formatters that require recursive constructs, like
calldataare expected to work with restrictions at first, especially on Hardware wallets.
Internationalization
This specification intentionally does not address internationalization or localization of displayable strings.
All display strings in ERC-7730 files—including intents, labels, field names, and enumeration values—are expected to be authored in English.
Future versions of this specification may consider standardized internationalization support once the base standard achieves wider adoption and the ecosystem can better assess the need for multilingual support.
Batch Transactions (EIP-5792)
When displaying batch transactions as defined in EIP-5792, wallets SHOULD either:
- Concatenate the
interpolatedIntentstrings of individual operations using “ and “ as a separator to create a single, human-readable description of the entire batch. - Concatenate screens generated using
intentand requiredfields, clearly separating individual transactions
The interpolatedIntent approach provides users with a clear, natural language summary of complex multi-step operations without requiring them to mentally piece together separate transaction descriptions. The intent approach provide a more controllable wallet UI for space limited wallets.
Permit + Swap example:
[
{
"function": "permit",
"interpolatedIntent": "Approve {spender} to spend {value} USDC"
},
{
"function": "swapExactTokensForTokens",
"interpolatedIntent": "Swap {amountIn} for at least {amountOutMin}"
}
]
Combined display: “Approve Uniswap Router to spend 1000 USDC and Swap 1000 USDC for at least 0.25 WETH”
Approve + Swap example:
[
{
"function": "approve",
"interpolatedIntent": "Approve {_spender} to spend {_value}"
},
{
"function": "exactInputSingle",
"interpolatedIntent": "Swap {amountIn} for at least {amountOutMinimum}"
}
]
Combined display: “Approve Uniswap V3 Router to spend 5000 DAI and Swap 5000 DAI for at least 1.2 ETH”
Approve + Swap + Mint NFT example:
[
{
"function": "approve",
"interpolatedIntent": "Approve {_spender} to spend {_value}"
},
{
"function": "swapExactTokensForETH",
"interpolatedIntent": "Swap {amountIn} for at least {amountOutMin} ETH"
},
{
"function": "mint",
"interpolatedIntent": "Mint {quantity} NFT(s) from {collection}"
}
]
Combined display: “Approve DEX Router to spend 2000 USDC and Swap 2000 USDC for at least 0.5 ETH and Mint 2 NFT(s) from NftProject”
Implementation Guidance for Batch Transactions using interpolatedIntents
Wallets implementing batch transaction display SHOULD:
- Process each transaction in the batch to generate its
interpolatedIntent - Join the resulting intent strings with “ and “ (note the spaces)
- Display the combined string as a single transaction summary
- Provide a way for users to view individual transaction details if needed
- Fall back to displaying individual
intentfields if anyinterpolatedIntentprocessing fails
Wallets MAY:
- Apply additional formatting (e.g., capitalizing the first letter, adding a period at the end)
- Truncate very long combined intents and provide expansion UI
- Group related operations visually while still showing the combined intent
Extensibility to other Structured Data formats
In the future, this specification could be extended to structured data like Meta Transaction in EIP-2771, User Operations in EIP-4337, and batch transaction payloads in EIP-5792.
The interpolatedIntent field is particularly well-suited for EIP-5792 batch transactions, as it enables wallets to concatenate multiple operation descriptions into a single, natural language sentence. By joining individual interpolatedIntent strings with “ and “, wallets can provide users with clear, readable summaries of complex multi-step operations like “Approve Uniswap Router to spend 1000 USDC and Swap 1000 USDC for at least 0.25 WETH”. This significantly improves the user experience when reviewing batch transactions compared to displaying separate, disconnected operation descriptions. See the Value Interpolation section for detailed implementation guidance and examples.
Common Keywords
The DeFi ecosystem has matured and has some repeating usecases with well defined terms for different actions. While this ERC does not define specific standards for DEX or Lending protocol intents, for the sake of easier user readibility, teams with similar products should align on similar terminology to describe actions and actors. We encourage the creation of another layer of standartization on top of ERC-7730 to be recommeneded and even enforced in clear signing registries for improved user security.
Test Cases
More examples can be found in the asset folder.
| File name | Description |
|---|---|
| example-main.json | Simple example of a basic ERC-7730 file |
| example-erc-20.json | Simple example of defining an interface for ERC-20 contracts |
| example-include.json | Example of including an interface into another ERC-7730 file |
| example-eip-712.json | Clear signing EIP-712 example |
| example-array-iteration.json | Examples of various array iteration modes and field grouping features |
| example-maps.json | Example of maps of constants |
| example-visibility-rules.json | Example of controlling the visibility of fields |
Security Considerations
ERC-7730 creates a risk surface where malicious actors could abuse formatting to make unsafe transactions appear benign. In addition to misapplied or malformed files, implementers should anticipate phishing techniques such as (a) parameter injection using legitimate, high-profile contract formats, (b) registry poisoning with well-formed but misleading entries, and (c) front-end/CDN compromise that prompts signatures for unexpected contracts or parameters.
Binding context
The binding context mitigates misuse by specifying exactly which structured data a given ERC-7730 file may format. app developers MUST set restrictive constraints; wallets MUST verify all constraints and ensure formatting is cryptographically bound to the reviewed data and not tamperable in transit (including on resource-constrained hardware wallets).
Registry Poisoning
Curation and registry of ERC-7730 files are out of scope for this specification, but any external registry or wallet ecosystem MUST assume it will be targeted. A secure registry should (a) require cryptographically verifiable provenance and attestations for each ERC-7730 file and its maintainer, (b) keep a public, tamper-evident history of submissions, approvals, and revocations, (c) implement a mechanism to credibly link contract ownership or authority to the submitted file, and (d) adopt a clear governance model with multi-party sign-off and automated monitoring to detect anomalies or mass poisoning attempts. These measures mitigate attacks on ERC-7730 files in transit and make registry compromise significantly harder without constraining this ERC itself.
External Data Resolution
ERC-7730 formatting relies on resolving various external data sources to provide human-readable information. This resolution introduces trust assumptions that wallets must understand when showing ERC-7730 information:
Trust Assumptions:
The transition from transaction data to human readable data requires trust in external data sources to resolve some parts of the presentation layer. Without the resolution, it’s not readable and missed the whole point of the ERC, with the resolution it adds a risk factor:
- Token metadata (tickers, decimals) A token address translated to a token ticker and proper decimals applied to the
tokenAmount. - NFT collection names and token metadata for the
nftNameformat - Address Resolution when formatting
addressNameusing ENS or similar tools
Malicious or compromised data sources could:
- Display incorrect token names, leading users to believe they are approving transactions for legitimate tokens when they are not
- Display malicious addresses as if it is a trusted address name
- Present fake NFT collection names to obscure the true nature of transactions
- Provide misleading enumeration values that misrepresent transaction parameters
- Supply modified ABIs that change how transaction data is interpreted
Mitigations:
Wallets SHOULD:
- Maintain curated lists of well-known addresses (tokens, contracts, collections) and warn users when encountering unknown entities
- Display full addresses alongside resolved names in some form
- Clearly indicate the source and trust level of resolved names and metadata
Wallets MUST:
- Display clear warnings when external data cannot be resolved or verified (e.g., “Unknown token”)
- Never fallback to presenting untrusted information as if it was trusted and verified
Example Attack Scenarios:
-
Token Name Spoofing: A malicious ERC-7730 file references a fake token contract at address
0x1234...that returns “USDT” as its ticker and 6 decimals, making a transaction appear to transfer legitimate USDT when it actually transfers a worthless token. -
Address Name Substitution: A compromised ENS resolver returns a malicious address naming resolution causing a send to an address not anticipated
Interpolated Intent Security
The interpolatedIntent feature introduces additional attack surfaces that wallets MUST mitigate:
Injection Attacks
Malicious contracts could craft parameter values that, when interpolated, create misleading intent descriptions. For example:
- A token name containing “to vitalik.eth” could make “Send {amount} to {recipient}” appear as “Send 100 USDT to vitalik.eth to 0x123…”
- Addresses with misleading ENS names could obscure the true destination
- Very long formatted values could push critical information off-screen
Mitigations:
- Wallets MUST apply the same formatting and validation to interpolated values as they would for separately displayed fields
- Wallets MUST validate that ENS names and other address labels are verified before interpolation
- Wallets SHOULD display field values separately in addition to the interpolated intent, or provide easy access to detailed field view
Format Consistency
Wallets MUST ensure that the formatting applied during interpolation exactly matches the formatting that would be applied if the field were displayed separately. This prevents attackers from exploiting differences in formatting to hide malicious values.
Implementation requirement:
- The interpolation engine MUST use the same formatter functions and parameters specified in the corresponding field format specification
- If a path in
interpolatedIntentdoes not have a corresponding field format specification, interpolation MUST fail and fall back tointent
Path Validation
Wallets MUST validate that all paths in interpolation expressions:
- Follow the path reference rules
- Reference fields that exist in the structured data being signed
- Have corresponding field format specifications in the
fieldsarray - Do not create circular references or unbounded recursion
Fallback Behavior
If interpolation fails for any reason (invalid path, formatting error, missing field specification, validation failure), wallets MUST:
- Fall back to displaying the static
intentfield - Display all fields separately according to their field format specifications
- NOT attempt to partially process the interpolated intent
- Optionally warn the user that the dynamic description could not be generated
Resource Constraints
Hardware wallets and other resource-constrained devices MAY choose not to implement interpolatedIntent support. The intent field MUST always be present as a fallback for such devices.
Testing and Validation
ERC-7730 file authors SHOULD test interpolatedIntent strings with:
- Minimum and maximum expected values for each interpolated field
- Edge cases like zero amounts, maximum allowances, expired dates
- Long addresses, token names, and other string fields to ensure proper truncation
Future Work
Future improvements could bind frontends to verified contract manifests so that a compromised UI cannot silently substitute a different contract. Clear, auditable registry governance (multi-party signoff, monitoring, and revocation) can further raise the cost of phishing, parameter injection, and front-end compromise attacks.
Copyright
Copyright and related rights waived via CC0.