Binding evaluator for ERC-7730 descriptors.
Given a parsed descriptor and a concrete signing request — calldata, an
EIP-712 typed message, or an ERC-4337 UserOperation — resolve/3 decides
which display format applies and decodes the bound data into a name →
value map ready for the display-rule engine.
Binding rules
| Request | Match on |
|---|---|
{:calldata, address, chain_id, hex_data} | contract deployment {chain_id, address} + 4-byte selector |
{:eip712, payload} | EIP-712 domain fields + primaryType |
{:user_op, address, chain_id, user_op} | unwraps callData, then binds as calldata |
Proxy detection
Reference implementations (Ledger's python-erc7730, Uniswap's descriptors)
bind by matching the transaction's target against the deployments list, and
delegate proxy/implementation resolution to an optional addressMatcher URI.
Resolving a matcher (or an EIP-1967 implementation slot behind a proxy)
requires on-chain reads and is registry/runtime-side — out of scope here. This
evaluator binds against the literal deployments list; when a descriptor
carries an addressMatcher and no deployment matches, the error reason notes
that an unresolved matcher was present.
The resolution result is a map consumed by Onchain.ERC7730.Formatter:
%{
format: format(), # the matched display format
signature: %ABI.FunctionSelector{} | nil,
message: %{name => value}, # decoded bound data (path "#." root)
types: %{name => abi_type}, # ABI type per message field
envelope: %{to: _, value: _, from: _} # transaction envelope (path "@." root)
}API Functions
| Function | Arity | Description | Param Kinds |
|---|---|---|---|
resolve | 3 | Resolve which display format applies to a signing request and decode the bound data. | descriptor: value, request: value, opts: value |
Summary
Functions
Resolve which display format applies to a signing request and decode the bound data.
Types
@type request() :: {:calldata, String.t() | binary(), non_neg_integer(), String.t()} | {:eip712, map()} | {:user_op, String.t() | binary(), non_neg_integer(), map()}
@type resolution() :: %{ format: Onchain.ERC7730.Descriptor.format(), signature: ABI.FunctionSelector.t() | nil, message: %{optional(String.t()) => term()}, types: %{optional(String.t()) => term()}, envelope: map() }
Functions
@spec resolve(Onchain.ERC7730.Descriptor.t(), request(), keyword()) :: {:ok, resolution()} | {:error, {atom(), term()}}
Resolve which display format applies to a signing request and decode the bound data.
Parameters
descriptor- Parsed %Onchain.ERC7730.Descriptor{} (value)request- {:calldata, address, chain_id, hex_data} | {:eip712, payload} | {:user_op, address, chain_id, user_op} (value)opts- Envelope overrides for the @. root: :value (native value), :from (sender) (default:[], value)
Returns
Resolution map (format/signature/message/types/envelope). Errors: :context_mismatch, :no_deployment_match, :no_format_match, :decode_error, :invalid_request, :missing_calldata ({:ok, resolution} | {:error, {tag, reason}})
# descripex:contract
%{
params: %{
request: %{
description: "{:calldata, address, chain_id, hex_data} | {:eip712, payload} | {:user_op, address, chain_id, user_op}",
kind: :value
},
opts: %{
default: [],
description: "Envelope overrides for the @. root: :value (native value), :from (sender)",
kind: :value
},
descriptor: %{
description: "Parsed %Onchain.ERC7730.Descriptor{}",
kind: :value
}
},
returns: %{
type: "{:ok, resolution} | {:error, {tag, reason}}",
description: "Resolution map (format/signature/message/types/envelope). Errors: :context_mismatch, :no_deployment_match, :no_format_match, :decode_error, :invalid_request, :missing_calldata"
}
}