refactor(clear_signing): improve types - add FormattedValue type
What changed, and why it matters
This is a pure code cleanup (refactoring) change in the Ethereum 'clear signing' feature of the Trezor firmware. It renames a type alias, introduces a new helper type alias to make function signatures shorter, removes an unused list initialization, and makes a private helper function public. No behavior of the device or how transactions are shown to the user is changed.
No security action needed. This is a non-functional refactor; routine review/merge is sufficient.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The commit refactors type aliases in core/src/apps/ethereum/clear_signing.py. It adds FormattedValue as an alias for tuple[str | AboveThreshold | None, EthereumTokenInfo | None, AnyBytes | None], renames DisplayedField to RenderedField, and updates return type annotations across multiple format() methods and helpers. It also removes self.parameters = [] from DisplayFormat.init (the attribute is unused) and renames _find_display_format to find_display_format (removing the leading underscore). No logic changes are present in the diff.
Changed components
core/src/apps/ethereum/clear_signing.pyInspect captured patch +34 / −25
### core/src/apps/ethereum/clear_signing.py
@@ -21,8 +21,8 @@
EthereumERC7730FieldInfo,
EthereumTokenInfo,
)
- from trezorui_api import StrPropertyType
from trezor.ui.layouts.properties import AboveThreshold
+ from trezorui_api import StrPropertyType
from typing_extensions import Self
from apps.common.payment_request import PaymentRequestVerifier
@@ -48,8 +48,17 @@
# Assumes that the memoryview contains just that value.
Parser = Callable[[memoryview], Value]
- # One displayed row: ((label, formatted value, is_mono), token, token_address)
- DisplayedField = tuple[
+ # Output of a `FieldFormatter.format`
+ # (formatted value, token, token_address)
+ FormattedValue = tuple[
+ str | AboveThreshold | None,
+ EthereumTokenInfo | None,
+ AnyBytes | None,
+ ]
+
+ # Rendered value. 1st member is essentially StrPropertyType
+ # ((label, formatted value, is_mono), token, token_address)
+ RenderedField = tuple[
tuple[str, str | AboveThreshold | None, bool | None],
EthereumTokenInfo | None,
AnyBytes | None,
@@ -276,7 +285,7 @@ async def format(
msg: MsgInSignTx,
defs: Definitions,
path_walker: PathWalker,
- ) -> tuple[str | AboveThreshold | None, EthereumTokenInfo | None, AnyBytes | None]:
+ ) -> FormattedValue:
"""
Format a field using the current formatter.
Return the formatted value and optionally a token and a token address,
@@ -293,7 +302,7 @@ async def format(
_msg: MsgInSignTx,
defs: Definitions,
_path_walker: PathWalker,
- ) -> tuple[str | AboveThreshold | None, EthereumTokenInfo | None, AnyBytes | None]:
+ ) -> FormattedValue:
if address is None:
return None, None, None
elif isinstance(address, str):
@@ -311,7 +320,7 @@ async def format(
_msg: MsgInSignTx,
defs: Definitions,
_path_walker: PathWalker,
- ) -> tuple[str | AboveThreshold | None, EthereumTokenInfo | None, AnyBytes | None]:
+ ) -> FormattedValue:
if amount is None:
return None, None, None
else:
@@ -345,7 +354,7 @@ async def format(
msg: MsgInSignTx,
defs: Definitions,
path_walker: PathWalker,
- ) -> tuple[str | AboveThreshold | None, EthereumTokenInfo | None, AnyBytes | None]:
+ ) -> FormattedValue:
"""Returns (formatted_value, token, token_address)"""
from trezor.ui.layouts.properties import AboveThreshold
@@ -421,7 +430,7 @@ async def format(
_msg: MsgInSignTx,
_definitions: Definitions,
_path_walker: PathWalker,
- ) -> tuple[str | AboveThreshold | None, EthereumTokenInfo | None, AnyBytes | None]:
+ ) -> FormattedValue:
if value is None:
return None, None, None
else:
@@ -467,7 +476,7 @@ async def format(
_msg: MsgInSignTx,
_definitions: Definitions,
_path_walker: PathWalker,
- ) -> tuple[str | AboveThreshold | None, EthereumTokenInfo | None, AnyBytes | None]:
+ ) -> FormattedValue:
if value is None:
return None, None, None
elif isinstance(value, str):
@@ -493,7 +502,7 @@ async def format(
_msg: MsgInSignTx,
_definitions: Definitions,
_path_walker: PathWalker,
- ) -> tuple[str | AboveThreshold | None, EthereumTokenInfo | None, AnyBytes | None]:
+ ) -> FormattedValue:
from trezor.strings import format_timestamp
if value is None:
@@ -515,7 +524,7 @@ async def format(
_msg: MsgInSignTx,
_definitions: Definitions,
_path_walker: PathWalker,
- ) -> tuple[str | AboveThreshold | None, EthereumTokenInfo | None, AnyBytes | None]:
+ ) -> FormattedValue:
if value is None:
return None, None, None
if isinstance(value, bytes):
@@ -560,7 +569,7 @@ async def format(
_msg: MsgInSignTx,
_definitions: Definitions,
_path_walker: PathWalker,
- ) -> tuple[str | AboveThreshold | None, EthereumTokenInfo | None, AnyBytes | None]:
+ ) -> FormattedValue:
if value is None:
return None, None, None
if isinstance(value, (bytes, bytearray)):
@@ -580,7 +589,7 @@ async def _format_field_value(
msg: MsgInSignTx,
defs: Definitions,
path_walker: PathWalker,
-) -> tuple[str | AboveThreshold | None, EthereumTokenInfo | None, AnyBytes | None]:
+) -> FormattedValue:
"""Format a field value.
When the field's path resolves to an array (a `list`), the formatter is
@@ -937,8 +946,6 @@ def __init__(
self.field_definitions = field_definitions
self.provider_name = provider_name
- self.parameters = []
-
def matches_context(self, chain_id: int, address: bytes) -> bool:
if self.binding_context is None:
# applies to anything without context verification
@@ -958,9 +965,11 @@ async def parse_calldata(
defs: Definitions,
nested: bool = False,
override_callee: bytes | None = None,
- ) -> tuple[list[AnyValue], list[DisplayedField]]:
+ ) -> tuple[list[AnyValue], list[RenderedField]]:
"""Parse `calldata` (without the selector) and format the display fields.
+ Loosely speaking, it returns ([parameter = parsed value], [formatted value])
+
`nested` marks the parse of an embedded subcall's calldata (see
`_expand_calldata_field`): container paths other than `@.to` are
unresolvable there, and any further calldata fields render as raw
@@ -1053,7 +1062,7 @@ def get_value_for_path(path: Path) -> AnyValue:
raise InvalidFormatDefinition
return p
- fields: list[DisplayedField] = []
+ fields: list[RenderedField] = []
for field_definition in self.field_definitions:
try:
formatter = field_definition.get_formatter()
@@ -1149,7 +1158,7 @@ async def request_definitions(
return definitions, display_format
-async def _find_display_format(
+async def find_display_format(
func_sig: bytes, contract_address: bytes, msg: MsgInSignTx, nested: bool = False
) -> DisplayFormat | None:
"""Find a display format for calling `func_sig` on the `contract_address`
@@ -1188,7 +1197,7 @@ async def _expand_calldata_field(
msg: MsgInSignTx,
defs: Definitions,
nested: bool,
-) -> list[DisplayedField]:
+) -> list[RenderedField]:
"""Expand one `calldata` field into display rows.
The field's path resolves either to one `bytes` blob of embedded calldata
@@ -1212,7 +1221,7 @@ async def _expand_calldata_field(
# Same callee for all subcalls
callees = [callees] * len(blobs)
- rows: list[DisplayedField] = []
+ rows: list[RenderedField] = []
for i, (blob, callee) in enumerate(zip(blobs, callees)):
rows.extend(
await _expand_one_subcall(
@@ -1238,7 +1247,7 @@ async def _expand_one_subcall(
defs: Definitions,
nested: bool,
index: int | None = None,
-) -> list[DisplayedField]:
+) -> list[RenderedField]:
"""Expand one embedded subcall into display rows.
On success the rows are the subcall's provider and intent, followed by
@@ -1266,7 +1275,7 @@ def callee_str() -> str:
callee, defs.network
)
- def raw_rows() -> list[DisplayedField]:
+ def raw_rows() -> list[RenderedField]:
"""No subparsing. Show the callee and the raw hex blob."""
to_label = TR.ethereum__subcall_to
blob_label = field_definition.label
@@ -1293,7 +1302,7 @@ def raw_rows() -> list[DisplayedField]:
return raw_rows()
try:
- inner_format = await _find_display_format(func_sig, callee, msg, nested=True)
+ inner_format = await find_display_format(func_sig, callee, msg, nested=True)
if inner_format is None:
return raw_rows()
_, inner_fields = await inner_format.parse_calldata(
@@ -1311,7 +1320,7 @@ def raw_rows() -> list[DisplayedField]:
)
return raw_rows()
- rows: list[DisplayedField] = [
+ rows: list[RenderedField] = [
(
(
f"({subcall}) {TR.words__provider}",
@@ -1352,7 +1361,7 @@ async def try_confirm(
func_sig = bytes(data[:SC_FUNC_SIG_BYTES])
- display_format = await _find_display_format(func_sig, contract_address, msg)
+ display_format = await find_display_format(func_sig, contract_address, msg)
if display_format is None:
return False
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.