refactor(core): enforce layout scoping for more Rust layouts
What changed, and why it matters
This commit is a code cleanup that changes how user-interface layouts are managed in the Trezor firmware. It wraps more layout objects in explicit context managers (the `with ... as layout:` pattern) so their lifetimes are clearly scoped. There is no direct evidence in the commit that this fixes an exploitable security bug; it appears to be a follow-up refactoring for safer resource handling.
No immediate security action is required. Treat this as a normal refactoring commit and continue routine review and testing.
Security signals we found
Refactoring of UI layout lifetime management
Type annotation change from LayoutObj to LayoutContext
Use of context managers (with statements) for layout objects
Follow-up to prior pull request #6812
Evidence from the diff
The patch continues the work from PR #6812 by enforcing ‘layout scoping’ for additional Rust-backed UI layouts. It replaces direct use of LayoutObj with LayoutContext in type annotations and wraps layout creation in with statements across the Bolt, Caesar, Delizia, and Eckhart layout implementations. Several functions are also converted from sync functions returning Awaitable to async functions. The changes are structural and resource-lifetime oriented rather than logic changes.
Changed components
core/embed/rust/src/ui/api/firmware_micropython.rscore/mocks/generated/trezorui_api.pyicore/src/trezor/ui/layouts/bolt/__init__.pycore/src/trezor/ui/layouts/caesar/__init__.pycore/src/trezor/ui/layouts/delizia/__init__.pycore/src/trezor/ui/layouts/eckhart/__init__.pycore/src/trezor/ui/layouts/menu.pyInspect captured patch +785 / −701
diff --git a/core/embed/rust/src/ui/api/firmware_micropython.rs b/core/embed/rust/src/ui/api/firmware_micropython.rs
index 6c30bc26..c203317d 100644
--- a/core/embed/rust/src/ui/api/firmware_micropython.rs
+++ b/core/embed/rust/src/ui/api/firmware_micropython.rs
@@ -1572,7 +1572,7 @@ pub static mp_module_trezorui_api: Module = obj_module! {
/// back_button: bool = False,
/// footer: tuple[str, bool] | None = None,
/// external_menu: bool = False,
- /// ) -> LayoutObj[UiResult]:
+ /// ) -> LayoutContext[UiResult]:
/// """Confirm a generic piece of information on the screen.
/// The value can either be human readable text (`is_data=False`)
/// or something else - like an address or a blob of data.
@@ -1589,7 +1589,7 @@ pub static mp_module_trezorui_api: Module = obj_module! {
/// verb_cancel: str | None = None,
/// hold: bool = False,
/// chunkify: bool = False,
- /// ) -> LayoutObj[UiResult]:
+ /// ) -> LayoutContext[UiResult]:
/// """Similar to `confirm_value`, but only the first page is shown.
/// This function is intended as a building block for a higher level `confirm_blob`
/// abstraction which can paginate the blob, show just the first page
@@ -2064,7 +2064,7 @@ pub static mp_module_trezorui_api: Module = obj_module! {
/// """Homescreen for locked device."""
Qstr::MP_QSTR_show_lockscreen => obj_fn_kw!(0, new_show_lockscreen).as_obj(),
- /// def show_mismatch(*, title: str) -> LayoutObj[UiResult]:
+ /// def show_mismatch(*, title: str) -> LayoutContext[UiResult]:
/// """Warning of receiving address mismatch."""
Qstr::MP_QSTR_show_mismatch => obj_fn_kw!(0, new_show_mismatch).as_obj(),
@@ -2096,7 +2096,7 @@ pub static mp_module_trezorui_api: Module = obj_module! {
/// title: str,
/// value: Sequence[PropertyType] | str,
/// subtitle: str | None = None,
- /// ) -> LayoutObj[None]:
+ /// ) -> LayoutContext[None]:
/// """Show a list of key-value pairs, or a monospace string."""
Qstr::MP_QSTR_show_properties => obj_fn_kw!(0, new_show_properties).as_obj(),
@@ -2165,7 +2165,7 @@ pub static mp_module_trezorui_api: Module = obj_module! {
/// """Warning modal. Bolt: No buttons shown when `button` is empty string. Caesar: middle button and centered text."""
Qstr::MP_QSTR_show_warning => obj_fn_kw!(0, new_show_warning).as_obj(),
- /// def confirm_cancel() -> LayoutObj[UiResult]:
+ /// def confirm_cancel() -> LayoutContext[UiResult]:
/// """Ask the user to confirm the cancellation (or cancel the cancellation and go back to
/// the previous flow)"""
Qstr::MP_QSTR_confirm_cancel => obj_fn_kw!(0, new_confirm_cancel).as_obj(),
diff --git a/core/mocks/generated/trezorui_api.pyi b/core/mocks/generated/trezorui_api.pyi
index 90f2ae54..adc6099c 100644
--- a/core/mocks/generated/trezorui_api.pyi
+++ b/core/mocks/generated/trezorui_api.pyi
@@ -198,7 +198,7 @@ def confirm_value(
back_button: bool = False,
footer: tuple[str, bool] | None = None,
external_menu: bool = False,
-) -> LayoutObj[UiResult]:
+) -> LayoutContext[UiResult]:
"""Confirm a generic piece of information on the screen.
The value can either be human readable text (`is_data=False`)
or something else - like an address or a blob of data.
@@ -216,7 +216,7 @@ def confirm_value_intro(
verb_cancel: str | None = None,
hold: bool = False,
chunkify: bool = False,
-) -> LayoutObj[UiResult]:
+) -> LayoutContext[UiResult]:
"""Similar to `confirm_value`, but only the first page is shown.
This function is intended as a building block for a higher level `confirm_blob`
abstraction which can paginate the blob, show just the first page
@@ -733,7 +733,7 @@ def show_lockscreen(
# rust/src/ui/api/firmware_micropython.rs
-def show_mismatch(*, title: str) -> LayoutObj[UiResult]:
+def show_mismatch(*, title: str) -> LayoutContext[UiResult]:
"""Warning of receiving address mismatch."""
@@ -768,7 +768,7 @@ def show_properties(
title: str,
value: Sequence[PropertyType] | str,
subtitle: str | None = None,
-) -> LayoutObj[None]:
+) -> LayoutContext[None]:
"""Show a list of key-value pairs, or a monospace string."""
@@ -845,7 +845,7 @@ def show_warning(
# rust/src/ui/api/firmware_micropython.rs
-def confirm_cancel() -> LayoutObj[UiResult]:
+def confirm_cancel() -> LayoutContext[UiResult]:
"""Ask the user to confirm the cancellation (or cancel the cancellation and go back to
the previous flow)"""
diff --git a/core/src/trezor/ui/layouts/bolt/__init__.py b/core/src/trezor/ui/layouts/bolt/__init__.py
index af84372b..859637df 100644
--- a/core/src/trezor/ui/layouts/bolt/__init__.py
+++ b/core/src/trezor/ui/layouts/bolt/__init__.py
@@ -371,15 +371,16 @@ async def show_address(
assert result is CANCELLED
else:
- result = await interact(
- trezorui_api.show_mismatch(title=mismatch_title),
- None,
- raise_on_cancel=None,
- )
- assert result in (CONFIRMED, CANCELLED)
- # Right button aborts action, left goes back to showing address.
- if result is CONFIRMED:
- raise ActionCancelled
+ with trezorui_api.show_mismatch(title=mismatch_title) as layout:
+ result = await interact(
+ layout,
+ None,
+ raise_on_cancel=None,
+ )
+ assert result in (CONFIRMED, CANCELLED)
+ # Right button aborts action, left goes back to showing address.
+ if result is CONFIRMED:
+ raise ActionCancelled
async def show_pubkey(
@@ -524,25 +525,28 @@ async def confirm_payment_request(
(TR.address_details__derivation_path, refund.account_path, None)
)
- await with_info(
- trezorui_api.confirm_value(
- title=title,
- subtitle=TR.words__provider,
- value=recipient_name,
- description=None,
- verb=TR.words__confirm,
- verb_cancel=None,
- chunkify=False,
- info=True,
- ),
- trezorui_api.confirm_properties(
- title="",
- items=menu_items,
- verb=TR.buttons__close,
- ),
- "confirm_payment_request",
- ButtonRequestType.SignTx,
+ main_ctx = trezorui_api.confirm_value(
+ title=title,
+ subtitle=TR.words__provider,
+ value=recipient_name,
+ description=None,
+ verb=TR.words__confirm,
+ verb_cancel=None,
+ chunkify=False,
+ info=True,
)
+ menu_ctx = trezorui_api.confirm_properties(
+ title="",
+ items=menu_items,
+ verb=TR.buttons__close,
+ )
+ with main_ctx as main_layout, menu_ctx as menu_layout:
+ await with_info(
+ main_layout,
+ menu_layout,
+ "confirm_payment_request",
+ ButtonRequestType.SignTx,
+ )
for trade in trades:
await confirm_trade(
@@ -609,20 +613,21 @@ async def confirm_output(
)
try:
- await interact(
- trezorui_api.confirm_value(
- title=amount_title,
- value=amount,
- description=None,
- subtitle=None,
- verb=None if hold else TR.buttons__confirm,
- verb_cancel="^",
- info=False,
- hold=hold,
- ),
- "confirm_output",
- br_code,
- )
+ with trezorui_api.confirm_value(
+ title=amount_title,
+ value=amount,
+ description=None,
+ subtitle=None,
+ verb=None if hold else TR.buttons__confirm,
+ verb_cancel="^",
+ info=False,
+ hold=hold,
+ ) as layout:
+ await interact(
+ layout,
+ "confirm_output",
+ br_code,
+ )
except ActionCancelled:
# if the user cancels here, go back to confirm_value
continue
@@ -727,7 +732,7 @@ async def confirm_blob_intro(
return False
-def confirm_blob(
+async def confirm_blob(
br_name: str,
title: str,
data: StrOrBytes,
@@ -741,12 +746,12 @@ def confirm_blob(
extra_confirmation_if_not_read: bool = False,
chunkify: bool = False,
prompt_screen: bool = True,
-) -> Awaitable[None]:
+) -> None:
if description and ":" not in description:
description += ":"
verb = verb or TR.buttons__confirm # def_arg
- layout = trezorui_api.confirm_value(
+ with trezorui_api.confirm_value(
title=title,
subtitle=subtitle,
description=description,
@@ -755,25 +760,25 @@ def confirm_blob(
verb=verb,
verb_cancel=None,
chunkify=chunkify,
- )
+ ) as layout:
- if ask_pagination and layout.page_count() > 1:
- return _confirm_ask_pagination(
- br_name,
- title,
- data,
- description or "",
- br_code,
- extra_confirmation_if_not_read,
- hold,
- )
- else:
- assert not extra_confirmation_if_not_read
- return raise_if_not_confirmed(
- layout,
- br_name,
- br_code,
- )
+ if ask_pagination and layout.page_count() > 1:
+ return await _confirm_ask_pagination(
+ br_name,
+ title,
+ data,
+ description or "",
+ br_code,
+ extra_confirmation_if_not_read,
+ hold,
+ )
+ else:
+ assert not extra_confirmation_if_not_read
+ return await raise_if_not_confirmed(
+ layout,
+ br_name,
+ br_code,
+ )
def confirm_address(
@@ -838,7 +843,7 @@ def confirm_amount(
)
-def confirm_value(
+async def confirm_value(
title: str,
value: str,
description: str | None,
@@ -855,37 +860,40 @@ def confirm_value(
chunkify: bool = False,
chunkify_info: bool = False,
cancel: bool = False,
-) -> Awaitable[None]:
+) -> None:
"""General confirmation dialog, used by many other confirm_* functions."""
if description and value:
description += ":"
- info_layout = trezorui_api.show_info_with_cancel(
+ info_ctx = trezorui_api.show_info_with_cancel(
title=info_title if info_title else TR.words__title_information,
items=list(info_items) if info_items else [],
chunkify=chunkify_info,
)
- return with_info(
- trezorui_api.confirm_value(
- title=title,
- value=value,
- description=description,
- is_data=is_data,
- chunkify=chunkify,
- subtitle=subtitle,
- verb=verb,
- verb_cancel=verb_cancel,
- info=bool(info_items),
- hold=hold,
- cancel=cancel,
- ),
- info_layout,
- br_name,
- br_code,
+ main_ctx = trezorui_api.confirm_value(
+ title=title,
+ value=value,
+ description=description,
+ is_data=is_data,
+ chunkify=chunkify,
+ subtitle=subtitle,
+ verb=verb,
+ verb_cancel=verb_cancel,
+ info=bool(info_items),
+ hold=hold,
+ cancel=cancel,
)
+ with main_ctx as main_layout, info_ctx as info_layout:
+ return await with_info(
+ main_layout,
+ info_layout,
+ br_name,
+ br_code,
+ )
+
def confirm_properties(
br_name: str,
@@ -1092,7 +1100,7 @@ if not utils.BITCOIN_ONLY:
else:
description = f"{TR.ethereum__interaction_contract}:" if recipient else None
- address_layout = trezorui_api.confirm_value(
+ address_ctx = trezorui_api.confirm_value(
title=TR.words__address,
description=description,
value=recipient or TR.ethereum__new_contract,
@@ -1108,14 +1116,14 @@ if not utils.BITCOIN_ONLY:
(TR.address_details__derivation_path, account_path, None),
)
)
- account_info_layout = trezorui_api.show_info_with_cancel(
+ account_info_ctx = trezorui_api.show_info_with_cancel(
title=TR.send__send_from,
items=items,
)
extra_items = with_colon(fee_info_items)
- total_layout = trezorui_api.confirm_summary(
+ total_ctx = trezorui_api.confirm_summary(
amount=total_amount,
amount_label=f"{TR.words__amount}:",
fee=maximum_fee,
@@ -1126,21 +1134,25 @@ if not utils.BITCOIN_ONLY:
verb_cancel="^",
)
- fee_info_layout = trezorui_api.show_info_with_cancel(
+ fee_info_ctx = trezorui_api.show_info_with_cancel(
title=TR.confirm_total__title_fee,
items=extra_items,
)
- while True:
- await with_info(address_layout, account_info_layout, br_name, br_code)
+ with address_ctx as address_layout, account_info_ctx as account_info_layout:
+ with total_ctx as total_layout, fee_info_ctx as fee_info_layout:
+ while True:
+ await with_info(
+ address_layout, account_info_layout, br_name, br_code
+ )
- try:
- await with_info(total_layout, fee_info_layout, br_name, br_code)
- except ActionCancelled:
- # Allowing going back and forth between recipient and summary
- continue
- else:
- break
+ try:
+ await with_info(total_layout, fee_info_layout, br_name, br_code)
+ except ActionCancelled:
+ # Allowing going back and forth between recipient and summary
+ continue
+ else:
+ break
def ethereum_address_title() -> str:
"""Return the title for the Ethereum address confirmation."""
@@ -1536,7 +1548,7 @@ if not utils.BITCOIN_ONLY:
amount_label, amount, _is_data = amount_item or ("", "", None)
fee_label, fee, _is_data = fee_item
- confirm_layout = trezorui_api.confirm_value(
+ confirm_ctx = trezorui_api.confirm_value(
title=title,
description=description,
extra=f"{TR.words__provider}:" if vote_account else None,
@@ -1559,13 +1571,14 @@ if not utils.BITCOIN_ONLY:
)
items.append(blockhash_property)
- info_layout = trezorui_api.show_info_with_cancel(
+ info_ctx = trezorui_api.show_info_with_cancel(
title=title,
items=with_colon(items),
horizontal=True,
)
- await with_info(confirm_layout, info_layout, br_name, br_code)
+ with confirm_ctx as confirm_layout, info_ctx as info_layout:
+ await with_info(confirm_layout, info_layout, br_name, br_code)
await _confirm_summary(
amount=amount or "",
@@ -1921,28 +1934,30 @@ async def confirm_modify_output(
send_button_request = True
while True:
# if the user cancels here, raise ActionCancelled (by default)
- await interact(
- trezorui_api.confirm_value(
- title=TR.modify_amount__title,
- value=address,
- verb=TR.buttons__continue,
- verb_cancel=None,
- description=TR.words__address + ":",
- ),
- "modify_output" if send_button_request else None,
- ButtonRequestType.ConfirmOutput,
- )
-
- try:
+ with trezorui_api.confirm_value(
+ title=TR.modify_amount__title,
+ value=address,
+ verb=TR.buttons__continue,
+ verb_cancel=None,
+ description=TR.words__address + ":",
+ ) as layout:
await interact(
- trezorui_api.confirm_modify_output(
- sign=sign,
- amount_change=amount_change,
- amount_new=amount_new,
- ),
+ layout,
"modify_output" if send_button_request else None,
ButtonRequestType.ConfirmOutput,
)
+
+ try:
+ with trezorui_api.confirm_modify_output(
+ sign=sign,
+ amount_change=amount_change,
+ amount_new=amount_new,
+ ) as layout:
+ await interact(
+ layout,
+ "modify_output" if send_button_request else None,
+ ButtonRequestType.ConfirmOutput,
+ )
except ActionCancelled:
# if the user cancels here, go back to confirm_blob
send_button_request = False
@@ -2054,44 +2069,45 @@ async def confirm_signverify(
await with_info(address_layout, info_layout, br_name, br_code=BR_CODE_OTHER)
break
except ActionCancelled:
- result = await interact(
- trezorui_api.show_mismatch(title=TR.addr_mismatch__mismatch),
- None,
- raise_on_cancel=None,
- )
- assert result in (CONFIRMED, CANCELLED)
- # Right button aborts action, left goes back to showing address.
- if result is CONFIRMED:
- raise ActionCancelled
- else:
- continue
+ with trezorui_api.show_mismatch(title=TR.addr_mismatch__mismatch) as layout:
+ result = await interact(
+ layout,
+ None,
+ raise_on_cancel=None,
+ )
+ assert result in (CONFIRMED, CANCELLED)
+ # Right button aborts action, left goes back to showing address.
+ if result is CONFIRMED:
+ raise ActionCancelled
+ else:
+ continue
- message_layout = trezorui_api.confirm_value(
+ with trezorui_api.confirm_value(
title=TR.sign_message__confirm_message,
description=None,
value=message,
hold=not verify,
verb=TR.buttons__confirm,
- )
+ ) as message_layout:
- while True:
- if message_layout.page_count() <= LONG_MSG_PAGE_THRESHOLD:
- result = await interact(message_layout, br_name, BR_CODE_OTHER)
- if result is CONFIRMED:
- break
- else:
- await confirm_blob(
- br_name,
- TR.sign_message__confirm_message,
- message,
- verb=TR.buttons__confirm,
- hold=not verify,
- br_code=BR_CODE_OTHER,
- ask_pagination=True,
- extra_confirmation_if_not_read=not verify,
- )
+ while True:
+ if message_layout.page_count() <= LONG_MSG_PAGE_THRESHOLD:
+ result = await interact(message_layout, br_name, BR_CODE_OTHER)
+ if result is CONFIRMED:
+ break
+ else:
+ await confirm_blob(
+ br_name,
+ TR.sign_message__confirm_message,
+ message,
+ verb=TR.buttons__confirm,
+ hold=not verify,
+ br_code=BR_CODE_OTHER,
+ ask_pagination=True,
+ extra_confirmation_if_not_read=not verify,
+ )
- break
+ break
def error_popup(
@@ -2298,24 +2314,25 @@ async def success_pin_change(curpin: str | None, newpin: str | None) -> None:
async def confirm_firmware_update(description: str, fingerprint: str) -> None:
- main = trezorui_api.confirm_value(
+ main_ctx = trezorui_api.confirm_value(
title=TR.firmware_update__title,
description=description,
value="",
verb=TR.buttons__install,
info=True,
)
- info = trezorui_api.show_info_with_cancel(
+ info_ctx = trezorui_api.show_info_with_cancel(
title=TR.firmware_update__title_fingerprint,
items=[("", fingerprint, None)],
chunkify=True,
)
- await with_info(
- main,
- info,
- br_name="firmware_update",
- br_code=BR_CODE_OTHER,
- )
+ with main_ctx as main_layout, info_ctx as info_layout:
+ await with_info(
+ main_layout,
+ info_layout,
+ br_name="firmware_update",
+ br_code=BR_CODE_OTHER,
+ )
async def set_brightness(current: int | None = None) -> None:
diff --git a/core/src/trezor/ui/layouts/caesar/__init__.py b/core/src/trezor/ui/layouts/caesar/__init__.py
index 6e02a439..a2882128 100644
--- a/core/src/trezor/ui/layouts/caesar/__init__.py
+++ b/core/src/trezor/ui/layouts/caesar/__init__.py
@@ -387,15 +387,16 @@ async def show_address(
# User pressed left cancel button, show mismatch dialogue.
else:
- result = await interact(
- trezorui_api.show_mismatch(title=mismatch_title),
- None,
- raise_on_cancel=None,
- )
- assert result in (CONFIRMED, CANCELLED)
- # Right button aborts action, left goes back to showing address.
- if result is CONFIRMED:
- raise ActionCancelled
+ with trezorui_api.show_mismatch(title=mismatch_title) as layout:
+ result = await interact(
+ layout,
+ None,
+ raise_on_cancel=None,
+ )
+ assert result in (CONFIRMED, CANCELLED)
+ # Right button aborts action, left goes back to showing address.
+ if result is CONFIRMED:
+ raise ActionCancelled
async def show_pubkey(
@@ -683,17 +684,18 @@ async def confirm_output(
)
try:
- await interact(
- trezorui_api.confirm_value(
- title=amount_title,
- value=amount,
- description=None,
- verb_cancel="^",
- verb=TR.buttons__confirm,
- ),
- "confirm_output",
- br_code,
- )
+ with trezorui_api.confirm_value(
+ title=amount_title,
+ value=amount,
+ description=None,
+ verb_cancel="^",
+ verb=TR.buttons__confirm,
+ ) as layout:
+ await interact(
+ layout,
+ "confirm_output",
+ br_code,
+ )
except ActionCancelled:
# if the user cancels here, go back to confirm_value
continue
@@ -754,7 +756,7 @@ async def confirm_blob_intro(
return False
-def confirm_blob(
+async def confirm_blob(
br_name: str,
title: str,
data: StrOrBytes,
@@ -768,11 +770,11 @@ def confirm_blob(
extra_confirmation_if_not_read: bool = False,
chunkify: bool = False,
prompt_screen: bool = True,
-) -> Awaitable[None]:
+) -> None:
if description and ":" not in description:
description += ":"
- layout = trezorui_api.confirm_value(
+ with trezorui_api.confirm_value(
title=title,
description=description,
value=data,
@@ -780,20 +782,20 @@ def confirm_blob(
verb_cancel=verb_cancel or "",
hold=hold,
chunkify=chunkify,
- )
+ ) as layout:
- if ask_pagination and layout.page_count() > 1:
- assert not hold
- return _confirm_ask_pagination(
- br_name,
- title,
- data,
- description or "",
- br_code,
- extra_confirmation_if_not_read,
- )
- else:
- return raise_if_not_confirmed(layout, br_name, br_code)
+ if ask_pagination and layout.page_count() > 1:
+ assert not hold
+ return await _confirm_ask_pagination(
+ br_name,
+ title,
+ data,
+ description or "",
+ br_code,
+ extra_confirmation_if_not_read,
+ )
+ else:
+ return await raise_if_not_confirmed(layout, br_name, br_code)
async def _confirm_ask_pagination(
@@ -937,7 +939,7 @@ def confirm_properties(
)
-def confirm_value(
+async def confirm_value(
title: str,
value: str,
description: str | None,
@@ -952,29 +954,30 @@ def confirm_value(
chunkify: bool = False,
chunkify_info: bool = False,
cancel: bool = False,
-) -> Awaitable[None]:
+) -> None:
"""General confirmation dialog, used by many other confirm_* functions."""
if description and value:
description += ":"
if not info_items:
- return raise_if_not_confirmed(
- trezorui_api.confirm_value(
- title=title,
- value=value,
- description=description,
- verb=verb or TR.buttons__hold_to_confirm,
- verb_cancel=verb_cancel or "",
- info=False,
- hold=hold,
- is_data=is_data,
- chunkify=chunkify,
- cancel=cancel,
- ),
- br_name,
- br_code,
- )
+ with trezorui_api.confirm_value(
+ title=title,
+ value=value,
+ description=description,
+ verb=verb or TR.buttons__hold_to_confirm,
+ verb_cancel=verb_cancel or "",
+ info=False,
+ hold=hold,
+ is_data=is_data,
+ chunkify=chunkify,
+ cancel=cancel,
+ ) as layout:
+ return await raise_if_not_confirmed(
+ layout,
+ br_name,
+ br_code,
+ )
from trezor.ui.layouts.menu import Details, Menu, confirm_with_menu
@@ -988,7 +991,7 @@ def confirm_value(
def item_factory(
info_title: str, info_value: str
- ) -> Callable[[], trezorui_api.LayoutObj]:
+ ) -> Callable[[], trezorui_api.LayoutContext]:
return lambda: trezorui_api.confirm_value(
title=info_title,
value=info_value,
@@ -1003,7 +1006,7 @@ def confirm_value(
Details.from_layout(name or "", item_factory(name or "", value or ""))
for name, value, _is_data in info_items
)
- return confirm_with_menu(main, menu, br_name, br_code)
+ return await confirm_with_menu(main, menu, br_name, br_code)
def confirm_total(
@@ -1996,37 +1999,39 @@ async def confirm_modify_output(
amount_change: str,
amount_new: str,
) -> None:
- address_layout = trezorui_api.confirm_value(
+ address_ctx = trezorui_api.confirm_value(
title=TR.modify_amount__title,
value=address,
verb=TR.buttons__continue,
description=f"{TR.words__address}:",
)
- modify_layout = trezorui_api.confirm_modify_output(
+ modify_ctx = trezorui_api.confirm_modify_output(
sign=sign,
amount_change=amount_change,
amount_new=amount_new,
)
- send_button_request = True
- while True:
- await raise_if_not_confirmed(
- address_layout,
- "modify_output" if send_button_request else None,
- ButtonRequestType.ConfirmOutput,
- )
- try:
+ with address_ctx as address_layout, modify_ctx as modify_layout:
+
+ send_button_request = True
+ while True:
await raise_if_not_confirmed(
- modify_layout,
+ address_layout,
"modify_output" if send_button_request else None,
ButtonRequestType.ConfirmOutput,
)
- except ActionCancelled:
- send_button_request = False
- continue
- else:
- break
+ try:
+ await raise_if_not_confirmed(
+ modify_layout,
+ "modify_output" if send_button_request else None,
+ ButtonRequestType.ConfirmOutput,
+ )
+ except ActionCancelled:
+ send_button_request = False
+ continue
+ else:
+ break
def confirm_modify_fee(
@@ -2090,7 +2095,7 @@ async def confirm_signverify(
) -> None:
br_name = "verify_message" if verify else "sign_message"
- message_layout = trezorui_api.confirm_value(
+ with trezorui_api.confirm_value(
title=TR.sign_message__confirm_message,
description=None,
value=message,
@@ -2098,38 +2103,38 @@ async def confirm_signverify(
verb_cancel="^",
hold=not verify,
chunkify=chunkify,
- )
+ ) as message_layout:
- # Allowing to go back from the second screen
- while True:
- await confirm_blob(
- br_name,
- TR.sign_message__confirm_address,
- address,
- verb=TR.buttons__continue,
- br_code=BR_CODE_OTHER,
- )
- try:
- if message_layout.page_count() <= LONG_MSG_PAGE_THRESHOLD:
- await raise_if_not_confirmed(
- message_layout,
- br_name,
- BR_CODE_OTHER,
- )
+ # Allowing to go back from the second screen
+ while True:
+ await confirm_blob(
+ br_name,
+ TR.sign_message__confirm_address,
+ address,
+ verb=TR.buttons__continue,
+ br_code=BR_CODE_OTHER,
+ )
+ try:
+ if message_layout.page_count() <= LONG_MSG_PAGE_THRESHOLD:
+ await raise_if_not_confirmed(
+ message_layout,
+ br_name,
+ BR_CODE_OTHER,
+ )
+ else:
+ await confirm_blob(
+ br_name,
+ TR.sign_message__confirm_message,
+ message,
+ verb=None,
+ verb_cancel="^",
+ ask_pagination=True,
+ extra_confirmation_if_not_read=not verify,
+ )
+ except ActionCancelled:
+ continue
else:
- await confirm_blob(
- br_name,
- TR.sign_message__confirm_message,
- message,
- verb=None,
- verb_cancel="^",
- ask_pagination=True,
- extra_confirmation_if_not_read=not verify,
- )
- except ActionCancelled:
- continue
- else:
- break
+ break
def error_popup(
diff --git a/core/src/trezor/ui/layouts/delizia/__init__.py b/core/src/trezor/ui/layouts/delizia/__init__.py
index f15c4c00..506cbe5b 100644
--- a/core/src/trezor/ui/layouts/delizia/__init__.py
+++ b/core/src/trezor/ui/layouts/delizia/__init__.py
@@ -516,7 +516,7 @@ async def confirm_payment_request(
) as obj:
await raise_if_not_confirmed(obj, "confirm_payment_request")
- main_layout = trezorui_api.confirm_value(
+ main_ctx = trezorui_api.confirm_value(
title=title,
subtitle=TR.words__provider,
value=recipient_name,
@@ -548,7 +548,8 @@ async def confirm_payment_request(
)
menu = Menu.root(menu_items, TR.send__cancel_sign)
- await confirm_with_menu(main_layout, menu, "confirm_payment_request")
+ with main_ctx as main_layout:
+ await confirm_with_menu(main_layout, menu, "confirm_payment_request")
for trade in trades:
await confirm_trade(
@@ -696,21 +697,22 @@ async def confirm_blob_intro(
- cancel (raises `ActionCancelled`)
"""
- res = await interact(
- trezorui_api.confirm_value_intro(
- title=title,
- value=value,
- subtitle=subtitle,
- verb=verb,
- verb_cancel=verb_cancel,
- ),
- br_name=br_name,
- br_code=br_code,
- )
- return res is CONFIRMED
+ with trezorui_api.confirm_value_intro(
+ title=title,
+ value=value,
+ subtitle=subtitle,
+ verb=verb,
+ verb_cancel=verb_cancel,
+ ) as layout:
+ res = await interact(
+ layout,
+ br_name=br_name,
+ br_code=br_code,
+ )
+ return res is CONFIRMED
-def confirm_blob(
+async def confirm_blob(
br_name: str,
title: str,
data: StrOrBytes,
@@ -724,9 +726,9 @@ def confirm_blob(
verb_skip_pagination: str | None = None,
chunkify: bool = False,
prompt_screen: bool = True,
-) -> Awaitable[None]:
+) -> None:
if ask_pagination:
- main_layout = trezorui_api.confirm_value_intro(
+ main_ctx = trezorui_api.confirm_value_intro(
title=title,
value=data,
subtitle=description,
@@ -735,7 +737,7 @@ def confirm_blob(
hold=hold,
chunkify=chunkify,
)
- info_layout = trezorui_api.confirm_value(
+ info_ctx = trezorui_api.confirm_value(
title=title,
value=data,
subtitle=description,
@@ -750,16 +752,17 @@ def confirm_blob(
cancel=True,
)
- return with_info(
- main_layout,
- info_layout,
- br_name,
- br_code,
- repeat_button_request=True,
- info_layout_can_confirm=True,
- )
+ with main_ctx as main_layout, info_ctx as info_layout:
+ return await with_info(
+ main_layout,
+ info_layout,
+ br_name,
+ br_code,
+ repeat_button_request=True,
+ info_layout_can_confirm=True,
+ )
else:
- layout = trezorui_api.confirm_value(
+ with trezorui_api.confirm_value(
title=title,
value=data,
description=description,
@@ -769,12 +772,12 @@ def confirm_blob(
hold=hold,
chunkify=chunkify,
prompt_screen=prompt_screen,
- )
- return raise_if_not_confirmed(
- layout,
- br_name,
- br_code,
- )
+ ) as layout:
+ return await raise_if_not_confirmed(
+ layout,
+ br_name,
+ br_code,
+ )
def confirm_address(
@@ -844,7 +847,7 @@ def confirm_amount(
)
-def confirm_value(
+async def confirm_value(
title: str,
value: str,
description: str,
@@ -862,12 +865,12 @@ def confirm_value(
) = None,
cancel: bool = False,
cancel_text: str | None = None,
-) -> Awaitable[ui.UiResult]:
+) -> ui.UiResult:
"""General confirmation dialog, used by many other confirm_* functions."""
from trezor.ui.layouts.menu import Cancel, Menu, interact_with_menu
- main = trezorui_api.confirm_value(
+ main_ctx = trezorui_api.confirm_value(
title=title,
value=value,
is_data=is_data,
@@ -891,7 +894,8 @@ def confirm_value(
layout_factory=trezorui_api.confirm_cancel,
),
)
- return interact_with_menu(main, menu, br_name, br_code)
+ with main_ctx as main_layout:
+ return await interact_with_menu(main_layout, menu, br_name, br_code)
def confirm_properties(
@@ -1358,77 +1362,93 @@ if not utils.BITCOIN_ONLY:
)
)
- steps = [
- lambda: interact_with_menu(
- trezorui_api.confirm_value(
- title=title,
- value=intro_question,
- is_data=False,
- description=None,
- chunkify=False,
- external_menu=True,
- ),
- Menu.root(menu_items, TR.send__cancel_sign),
- f"{br_name}/intro",
- ButtonRequestType.SignTx,
- ),
- lambda: interact_with_menu(
- trezorui_api.confirm_value(
- title=title,
- value=vault_str,
- is_data=False,
- description=verb,
- verb="",
- ),
- Menu.root(menu_items, TR.send__cancel_sign),
- f"{br_name}/vault_name",
- ),
- lambda: interact_with_menu(
- trezorui_api.confirm_properties(
- title=title,
- items=[
- (amount_label, amount, False),
- (TR.words__chain, chain, False),
- ],
- hold=False,
- verb=TR.buttons__continue,
- ),
- Menu.root(menu_items, TR.send__cancel_sign),
- f"{br_name}/amount",
- br_code,
- ),
- ]
- if extra_data is not None:
- steps.append(
- lambda: interact_with_menu(
- trezorui_api.confirm_value(
- title=title,
- value=extra_data,
- description=TR.ethereum__calldata_suffix,
- is_data=True,
- ),
+ async def _step1() -> trezorui_api.UiResult:
+ with trezorui_api.confirm_value(
+ title=title,
+ value=intro_question,
+ is_data=False,
+ description=None,
+ chunkify=False,
+ external_menu=True,
+ ) as layout:
+ return await interact_with_menu(
+ layout,
+ Menu.root(menu_items, TR.send__cancel_sign),
+ f"{br_name}/intro",
+ ButtonRequestType.SignTx,
+ )
+
+ async def _step2() -> trezorui_api.UiResult:
+ with trezorui_api.confirm_value(
+ title=title,
+ value=vault_str,
+ is_data=False,
+ description=verb,
+ verb="",
+ ) as layout:
+ return await interact_with_menu(
+ layout,
+ Menu.root(menu_items, TR.send__cancel_sign),
+ f"{br_name}/vault_name",
+ )
+
+ async def _step3() -> trezorui_api.UiResult:
+ with trezorui_api.confirm_properties(
+ title=title,
+ items=[
+ (amount_label, amount, False),
+ (TR.words__chain, chain, False),
+ ],
+ hold=False,
+ verb=TR.buttons__continue,
+ ) as layout:
+ return await interact_with_menu(
+ layout,
Menu.root(menu_items, TR.send__cancel_sign),
- f"{br_name}/extra_data",
+ f"{br_name}/amount",
br_code,
)
- )
- steps.append(
- lambda: interact_with_menu(
- trezorui_api.confirm_summary(
- amount=None,
- amount_label=None,
- fee=maximum_fee,
- fee_label=TR.send__maximum_fee,
- extra_title=TR.confirm_total__title_fee,
- extra_items=list(info_items),
+
+ steps = [_step1, _step2, _step3]
+
+ if extra_data is not None:
+
+ async def _step4() -> trezorui_api.UiResult:
+ with trezorui_api.confirm_value(
title=title,
- back_button=False,
- ),
- Menu.root(menu_items, TR.send__cancel_sign),
- f"{br_name}/summary",
- br_code,
- )
- )
+ value=extra_data,
+ description=TR.ethereum__calldata_suffix,
+ is_data=True,
+ ) as layout:
+ return await interact_with_menu(
+ layout,
+ Menu.root(menu_items, TR.send__cancel_sign),
+ f"{br_name}/extra_data",
+ br_code,
+ )
+
+ steps.append(_step4)
+
+ async def _step5() -> trezorui_api.UiResult:
+ with trezorui_api.confirm_summary(
+ amount=None,
+ amount_label=None,
+ fee=maximum_fee,
+ fee_label=TR.send__maximum_fee,
+ extra_title=TR.confirm_total__title_fee,
+ extra_items=list(info_items),
+ title=title,
+ back_button=False,
+ ) as layout:
+ return await interact_with_menu(
+ layout,
+ Menu.root(menu_items, TR.send__cancel_sign),
+ f"{br_name}/summary",
+ br_code,
+ )
+
+ steps.append(_step5)
+
await confirm_linear_flow(*steps)
async def confirm_ethereum_vault_claim(
@@ -1455,49 +1475,57 @@ if not utils.BITCOIN_ONLY:
)
)
- await confirm_linear_flow(
- lambda: interact_with_menu(
- trezorui_api.confirm_value(
- title=title,
- value=intro_question,
- is_data=False,
- description=None,
- chunkify=False,
- external_menu=True,
- ),
- Menu.root(menu_items, TR.send__cancel_sign),
- f"{br_name}/intro",
- br_code,
- ),
- lambda: interact_with_menu(
- trezorui_api.confirm_properties(
- title=title,
- items=[
- (TR.ethereum__reward_tokens, token_list, False),
- ],
- hold=False,
- verb=TR.buttons__continue,
- ),
- Menu.root(menu_items, TR.send__cancel_sign),
- f"{br_name}/tokens",
- br_code,
- ),
- lambda: interact_with_menu(
- trezorui_api.confirm_summary(
- amount=None,
- amount_label=None,
- fee=maximum_fee,
- fee_label=TR.send__maximum_fee,
- extra_title=TR.confirm_total__title_fee,
- extra_items=list(info_items),
- title=title,
- back_button=False,
- ),
- Menu.root(menu_items, TR.send__cancel_sign),
- f"{br_name}/summary",
- br_code,
- ),
- )
+ async def _step1() -> trezorui_api.UiResult:
+ with trezorui_api.confirm_value(
+ title=title,
+ value=intro_question,
+ is_data=False,
+ description=None,
+ chunkify=False,
+ external_menu=True,
+ ) as layout:
+ return await interact_with_menu(
+ layout,
+ Menu.root(menu_items, TR.send__cancel_sign),
+ f"{br_name}/intro",
+ br_code,
+ )
+
+ async def _step2() -> trezorui_api.UiResult:
+ with trezorui_api.confirm_properties(
+ title=title,
+ items=[
+ (TR.ethereum__reward_tokens, token_list, False),
+ ],
+ hold=False,
+ verb=TR.buttons__continue,
+ ) as layout:
+ return await interact_with_menu(
+ layout,
+ Menu.root(menu_items, TR.send__cancel_sign),
+ f"{br_name}/tokens",
+ br_code,
+ )
+
+ async def _step3() -> trezorui_api.UiResult:
+ with trezorui_api.confirm_summary(
+ amount=None,
+ amount_label=None,
+ fee=maximum_fee,
+ fee_label=TR.send__maximum_fee,
+ extra_title=TR.confirm_total__title_fee,
+ extra_items=list(info_items),
+ title=title,
+ back_button=False,
+ ) as layout:
+ return await interact_with_menu(
+ layout,
+ Menu.root(menu_items, TR.send__cancel_sign),
+ f"{br_name}/summary",
+ br_code,
+ )
+
+ await confirm_linear_flow(_step1, _step2, _step3)
def confirm_solana_unknown_token_warning() -> Awaitable[None]:
return show_danger(
@@ -1930,36 +1958,37 @@ async def confirm_modify_output(
amount_change: str,
amount_new: str,
) -> None:
- address_layout = trezorui_api.confirm_value(
+ address_ctx = trezorui_api.confirm_value(
title=TR.modify_amount__title,
value=address,
verb=TR.buttons__continue,
verb_cancel=None,
description=f"{TR.words__address}:",
)
- modify_layout = trezorui_api.confirm_modify_output(
+ modify_ctx = trezorui_api.confirm_modify_output(
sign=sign,
amount_change=amount_change,
amount_new=amount_new,
)
- send_button_request = True
- while True:
- await raise_if_not_confirmed(
- address_layout,
- "modify_output" if send_button_request else None,
- ButtonRequestType.ConfirmOutput,
- )
- result = await interact(
- modify_layout,
- "modify_output" if send_button_request else None,
- ButtonRequestType.ConfirmOutput,
- raise_on_cancel=None,
- )
- send_button_request = False
+ with address_ctx as address_layout, modify_ctx as modify_layout:
+ send_button_request = True
+ while True:
+ await raise_if_not_confirmed(
+ address_layout,
+ "modify_output" if send_button_request else None,
+ ButtonRequestType.ConfirmOutput,
+ )
+ result = await interact(
+ modify_layout,
+ "modify_output" if send_button_request else None,
+ ButtonRequestType.ConfirmOutput,
+ raise_on_cancel=None,
+ )
+ send_button_request = False
- if result is CONFIRMED:
- break
+ if result is CONFIRMED:
+ break
def confirm_modify_fee(
@@ -2030,7 +2059,7 @@ async def confirm_signverify(
address_title = TR.sign_message__confirm_address
br_name = "sign_message"
- address_layout = trezorui_api.confirm_value(
+ address_ctx = trezorui_api.confirm_value(
title=address_title,
value=address,
description="",
@@ -2061,9 +2090,10 @@ async def confirm_signverify(
),
)
- await confirm_with_menu(address_layout, menu, br_name, br_code=BR_CODE_OTHER)
+ with address_ctx as address_layout:
+ await confirm_with_menu(address_layout, menu, br_name, br_code=BR_CODE_OTHER)
- message_layout = trezorui_api.confirm_value(
+ with trezorui_api.confirm_value(
title=TR.sign_message__confirm_message,
description=None,
value=message,
@@ -2072,21 +2102,21 @@ async def confirm_signverify(
hold=not verify,
info=False,
verb=TR.buttons__confirm,
- )
+ ) as message_layout:
- if message_layout.page_count() <= LONG_MSG_PAGE_THRESHOLD:
- await interact(message_layout, br_name, BR_CODE_OTHER)
- else:
- await confirm_blob(
- br_name,
- TR.sign_message__confirm_message,
- message,
- verb="",
- br_code=BR_CODE_OTHER,
- hold=not verify,
- ask_pagination=True,
- verb_skip_pagination=TR.sign_message__confirm_without_review,
- )
+ if message_layout.page_count() <= LONG_MSG_PAGE_THRESHOLD:
+ await interact(message_layout, br_name, BR_CODE_OTHER)
+ else:
+ await confirm_blob(
+ br_name,
+ TR.sign_message__confirm_message,
+ message,
+ verb="",
+ br_code=BR_CODE_OTHER,
+ hold=not verify,
+ ask_pagination=True,
+ verb_skip_pagination=TR.sign_message__confirm_without_review,
+ )
def error_popup(
diff --git a/core/src/trezor/ui/layouts/eckhart/__init__.py b/core/src/trezor/ui/layouts/eckhart/__init__.py
index aaf4d021..4bae744b 100644
--- a/core/src/trezor/ui/layouts/eckhart/__init__.py
+++ b/core/src/trezor/ui/layouts/eckhart/__init__.py
@@ -479,7 +479,7 @@ async def confirm_payment_request(
) as obj:
await raise_if_not_confirmed(obj, "confirm_payment_request")
- main_layout = trezorui_api.confirm_value(
+ main_ctx = trezorui_api.confirm_value(
title=title,
subtitle=TR.words__provider,
value=recipient_name,
@@ -511,57 +511,58 @@ async def confirm_payment_request(
)
menu = Menu.root(menu_items, TR.send__cancel_sign)
- while True:
- back_from_confirm_trade = False
- await confirm_with_menu(main_layout, menu, "confirm_payment_request")
-
+ with main_ctx as main_layout:
while True:
back_from_confirm_trade = False
+ await confirm_with_menu(main_layout, menu, "confirm_payment_request")
+
+ while True:
+ back_from_confirm_trade = False
+
+ # HACK: if we have multiple trades to confirm, we disable the back button
+ # to simplify the mechanism we use here to handle it.
+ # In practice, this should never happen, since normally there is only one trade in a transaction,
+ # but in theory it can (since SLIP-24 supports multiple CoinPurchaseMemos...)
+ can_go_back_from_trade = len(trades) == 1
+
+ for trade in trades:
+ res = await confirm_trade(
+ title,
+ TR.words__assets,
+ trade,
+ extra_menu_items or [],
+ can_go_back_from_trade,
+ )
+ if res is BACK:
+ back_from_confirm_trade = True
+ break
- # HACK: if we have multiple trades to confirm, we disable the back button
- # to simplify the mechanism we use here to handle it.
- # In practice, this should never happen, since normally there is only one trade in a transaction,
- # but in theory it can (since SLIP-24 supports multiple CoinPurchaseMemos...)
- can_go_back_from_trade = len(trades) == 1
-
- for trade in trades:
- res = await confirm_trade(
- title,
- TR.words__assets,
- trade,
- extra_menu_items or [],
- can_go_back_from_trade,
- )
- if res is BACK:
- back_from_confirm_trade = True
+ if back_from_confirm_trade:
break
- if back_from_confirm_trade:
- break
-
- if transaction_fee is not None:
- res = await _confirm_summary(
- amount=None,
- amount_label=None,
- fee=transaction_fee,
- fee_label=TR.words__transaction_fee,
- title=summary_title,
- account_items=account_items,
- extra_items=fee_info_items,
- extra_title=TR.confirm_total__title_fee,
- back_button=True,
- br_name="confirm_payment_request",
- )
- if res is BACK:
- continue
+ if transaction_fee is not None:
+ res = await _confirm_summary(
+ amount=None,
+ amount_label=None,
+ fee=transaction_fee,
+ fee_label=TR.words__transaction_fee,
+ title=summary_title,
+ account_items=account_items,
+ extra_items=fee_info_items,
+ extra_title=TR.confirm_total__title_fee,
+ back_button=True,
+ br_name="confirm_payment_request",
+ )
+ if res is BACK:
+ continue
+ else:
+ break
else:
break
+ if back_from_confirm_trade:
+ continue
else:
break
- if back_from_confirm_trade:
- continue
- else:
- break
async def confirm_output(
@@ -622,7 +623,7 @@ async def confirm_output(
),
)
- address_layout = trezorui_api.confirm_value(
+ address_ctx = trezorui_api.confirm_value(
title=TR.words__send,
value=address,
description=description,
@@ -633,7 +634,7 @@ async def confirm_output(
external_menu=True,
)
- amount_layout = trezorui_api.confirm_value(
+ amount_ctx = trezorui_api.confirm_value(
title=TR.words__send,
value=amount,
description=TR.words__amount,
@@ -643,14 +644,19 @@ async def confirm_output(
back_button=True,
)
- try:
- await confirm_linear_flow(
- lambda: interact_with_menu(address_layout, menu, "confirm_output", br_code),
- lambda: interact_with_menu(amount_layout, menu, "confirm_output", br_code),
- )
- except ActionCancelled:
- show_continue_in_app(TR.send__sign_cancelled)
- raise
+ with address_ctx as address_layout, amount_ctx as amount_layout:
+ try:
+ await confirm_linear_flow(
+ lambda: interact_with_menu(
+ address_layout, menu, "confirm_output", br_code
+ ),
+ lambda: interact_with_menu(
+ amount_layout, menu, "confirm_output", br_code
+ ),
+ )
+ except ActionCancelled:
+ show_continue_in_app(TR.send__sign_cancelled)
+ raise
async def should_show_more(
@@ -704,21 +710,22 @@ async def confirm_blob_intro(
- cancel (raises `ActionCancelled`)
"""
- res = await interact(
- trezorui_api.confirm_value_intro(
- title=title,
- value=value,
- subtitle=subtitle,
- verb=verb,
- verb_cancel=verb_cancel,
- ),
- br_name=br_name,
- br_code=br_code,
- )
- return res is CONFIRMED
+ with trezorui_api.confirm_value_intro(
+ title=title,
+ value=value,
+ subtitle=subtitle,
+ verb=verb,
+ verb_cancel=verb_cancel,
+ ) as layout:
+ res = await interact(
+ layout,
+ br_name=br_name,
+ br_code=br_code,
+ )
+ return res is CONFIRMED
-def confirm_blob(
+async def confirm_blob(
br_name: str,
title: str,
data: StrOrBytes,
@@ -731,10 +738,10 @@ def confirm_blob(
ask_pagination: bool = False,
verb_skip_pagination: str | None = None,
chunkify: bool = False,
-) -> Awaitable[None]:
+) -> None:
if ask_pagination:
- main_layout = trezorui_api.confirm_value_intro(
+ main_ctx = trezorui_api.confirm_value_intro(
title=title,
value=data,
subtitle=description,
@@ -743,7 +750,7 @@ def confirm_blob(
hold=hold,
chunkify=chunkify,
)
- info_layout = trezorui_api.confirm_value(
+ info_ctx = trezorui_api.confirm_value(
title=subtitle or title,
description=None,
value=data,
@@ -754,16 +761,17 @@ def confirm_blob(
cancel=True,
)
- return with_info(
- main_layout,
- info_layout,
- br_name,
- br_code,
- repeat_button_request=True,
- info_layout_can_confirm=True,
- )
+ with main_ctx as main_layout, info_ctx as info_layout:
+ return await with_info(
+ main_layout,
+ info_layout,
+ br_name,
+ br_code,
+ repeat_button_request=True,
+ info_layout_can_confirm=True,
+ )
else:
- return confirm_value(
+ return await confirm_value(
br_name=br_name,
title=title,
value=data,
@@ -837,7 +845,7 @@ def confirm_amount(
)
-def confirm_value(
+async def confirm_value(
title: str,
value: StrOrBytes,
description: str,
@@ -852,7 +860,7 @@ def confirm_value(
info_items: Iterable[StrPropertyType] | None = None,
info_title: str | None = None,
footer: tuple[str, bool] | None = None,
-) -> Awaitable[None]:
+) -> None:
"""General confirmation dialog, used by many other confirm_* functions."""
from trezor.ui.layouts.menu import Menu, confirm_with_menu
@@ -863,24 +871,25 @@ def confirm_value(
)
menu = Menu.root(menu_items, TR.buttons__cancel)
- return confirm_with_menu(
- trezorui_api.confirm_value(
- title=title,
- value=value,
- is_data=is_data,
- description=description,
- subtitle=subtitle,
- verb=verb,
- info=False,
- hold=hold,
- chunkify=chunkify,
- footer=footer,
- external_menu=True,
- ),
- menu,
- br_name,
- br_code,
- )
+ with trezorui_api.confirm_value(
+ title=title,
+ value=value,
+ is_data=is_data,
+ description=description,
+ subtitle=subtitle,
+ verb=verb,
+ info=False,
+ hold=hold,
+ chunkify=chunkify,
+ footer=footer,
+ external_menu=True,
+ ) as layout:
+ return await confirm_with_menu(
+ layout,
+ menu,
+ br_name,
+ br_code,
+ )
def confirm_properties(
@@ -1105,39 +1114,44 @@ if not utils.BITCOIN_ONLY:
else:
menu_items = []
- await confirm_linear_flow(
- lambda: interact_with_menu(
- trezorui_api.confirm_value(
- title=title,
- value=recipient or TR.ethereum__new_contract,
- is_data=bool(recipient),
- description="",
- subtitle=subtitle,
- verb=TR.buttons__continue,
- info=False,
- hold=False,
- chunkify=chunkify if recipient else False,
- external_menu=True,
- ),
- Menu.root(menu_items, TR.send__cancel_sign),
- "confirm_output",
- br_code,
- ),
- lambda: interact(
- trezorui_api.confirm_summary(
- amount=total_amount,
- amount_label=TR.words__amount,
- fee=maximum_fee,
- fee_label=TR.send__maximum_fee,
- extra_title=TR.confirm_total__title_fee,
- extra_items=list(fee_info_items),
- title=title,
- back_button=True,
- ),
- br_name,
- br_code,
- ),
- )
+ async def _step1() -> trezorui_api.UiResult:
+ with trezorui_api.confirm_value(
+ title=title,
+ value=recipient or TR.ethereum__new_contract,
+ is_data=bool(recipient),
+ description="",
+ subtitle=subtitle,
+ verb=TR.buttons__continue,
+ info=False,
+ hold=False,
+ chunkify=chunkify if recipient else False,
+ external_menu=True,
+ ) as layout:
+ return await interact_with_menu(
+ layout,
+ Menu.root(menu_items, TR.send__cancel_sign),
+ "confirm_output",
+ br_code,
+ )
+
+ async def _step2() -> trezorui_api.UiResult:
+ with trezorui_api.confirm_summary(
+ amount=total_amount,
+ amount_label=TR.words__amount,
+ fee=maximum_fee,
+ fee_label=TR.send__maximum_fee,
+ extra_title=TR.confirm_total__title_fee,
+ extra_items=list(fee_info_items),
+ title=title,
+ back_button=True,
+ ) as layout:
+ return await interact(
+ layout,
+ br_name,
+ br_code,
+ )
+
+ await confirm_linear_flow(_step1, _step2)
def ethereum_address_title() -> str:
"""Return the title for the Ethereum address confirmation."""
@@ -1517,36 +1531,41 @@ if not utils.BITCOIN_ONLY:
TR.words__amount if not verb == TR.ethereum__staking_claim else None,
)
- await confirm_linear_flow(
- lambda: interact_with_menu(
- trezorui_api.confirm_value(
- title=verb,
- value=intro_question,
- is_data=False,
- description="",
- subtitle=None,
- chunkify=False,
- external_menu=True,
- ),
- Menu.root(menu_items, TR.send__cancel_sign),
- br_name,
- ButtonRequestType.SignTx,
- ),
- lambda: interact(
- trezorui_api.confirm_summary(
- amount=amount,
- amount_label=amount_label,
- fee=maximum_fee,
- fee_label=TR.send__maximum_fee,
- extra_title=TR.confirm_total__title_fee,
- extra_items=list(info_items),
- title=title,
- back_button=True,
- ),
- "confirm_total",
- br_code,
- ),
- )
+ async def _step1() -> trezorui_api.UiResult:
+ with trezorui_api.confirm_value(
+ title=verb,
+ value=intro_question,
+ is_data=False,
+ description="",
+ subtitle=None,
+ chunkify=False,
+ external_menu=True,
+ ) as layout:
+ return await interact_with_menu(
+ layout,
+ Menu.root(menu_items, TR.send__cancel_sign),
+ br_name,
+ ButtonRequestType.SignTx,
+ )
+
+ async def _step2() -> trezorui_api.UiResult:
+ with trezorui_api.confirm_summary(
+ amount=amount,
+ amount_label=amount_label,
+ fee=maximum_fee,
+ fee_label=TR.send__maximum_fee,
+ extra_title=TR.confirm_total__title_fee,
+ extra_items=list(info_items),
+ title=title,
+ back_button=True,
+ ) as layout:
+ return await interact(
+ layout,
+ "confirm_total",
+ br_code,
+ )
+
+ await confirm_linear_flow(_step1, _step2)
def confirm_solana_recipient(
recipient: str,
@@ -1626,48 +1645,54 @@ if not utils.BITCOIN_ONLY:
extra = TR.words__provider if vote_account else ""
- await confirm_linear_flow(
- lambda: interact_with_menu(
- (
- trezorui_api.confirm_value(
- title=title,
- value=vote_account,
- extra=extra,
- description=description,
- is_data=False,
- chunkify=True,
- external_menu=True,
- verb=TR.buttons__continue,
- )
- if extra
- else trezorui_api.confirm_action(
- title=title,
- action=vote_account,
- description=description,
- verb=TR.buttons__continue,
- cancel=False,
- external_menu=True,
- )
- ),
- Menu.root(menu_items, TR.send__cancel_sign),
- br_name,
- br_code,
- ),
- lambda: interact_with_menu(
- trezorui_api.confirm_summary(
- amount=amount_item[1] if amount_item else None,
- amount_label=amount_item[0] if amount_item else None,
- fee=fee_item[1] or "",
- fee_label=fee_item[0] or "",
+ async def _step1() -> trezorui_api.UiResult:
+ ctx = (
+ trezorui_api.confirm_value(
title=title,
- back_button=True,
+ value=vote_account,
+ extra=extra,
+ description=description,
+ is_data=False,
+ chunkify=True,
external_menu=True,
- ),
- Menu.root(summary_menu_items, TR.buttons__cancel),
- br_name,
- br_code,
- ),
- )
+ verb=TR.buttons__continue,
+ )
+ if extra
+ else trezorui_api.confirm_action(
+ title=title,
+ action=vote_account,
+ description=description,
+ verb=TR.buttons__continue,
+ cancel=False,
+ external_menu=True,
+ )
+ )
+ with ctx as layout:
+ return await interact_with_menu(
+ layout,
+ Menu.root(menu_items, TR.send__cancel_sign),
+ br_name,
+ br_code,
+ )
+
+ async def _step2() -> trezorui_api.UiResult:
+ with trezorui_api.confirm_summary(
+ amount=amount_item[1] if amount_item else None,
+ amount_label=amount_item[0] if amount_item else None,
+ fee=fee_item[1] or "",
+ fee_label=fee_item[0] or "",
+ title=title,
+ back_button=True,
+ external_menu=True,
+ ) as layout:
+ return await interact_with_menu(
+ layout,
+ Menu.root(summary_menu_items, TR.buttons__cancel),
+ br_name,
+ br_code,
+ )
+
+ await confirm_linear_flow(_step1, _step2)
def confirm_cardano_tx(
amount: str,
@@ -2010,7 +2035,7 @@ async def confirm_modify_output(
amount_change: str,
amount_new: str,
) -> None:
- address_layout = trezorui_api.confirm_value(
+ address_ctx = trezorui_api.confirm_value(
title=TR.modify_amount__title,
subtitle=TR.words__address,
value=address,
@@ -2019,29 +2044,30 @@ async def confirm_modify_output(
is_data=True,
description=None,
)
- modify_layout = trezorui_api.confirm_modify_output(
+ modify_ctx = trezorui_api.confirm_modify_output(
sign=sign,
amount_change=amount_change,
amount_new=amount_new,
)
- send_button_request = True
- while True:
- await raise_if_not_confirmed(
- address_layout,
- "modify_output" if send_button_request else None,
- ButtonRequestType.ConfirmOutput,
- )
- result = await interact(
- modify_layout,
- "modify_output" if send_button_request else None,
- ButtonRequestType.ConfirmOutput,
- raise_on_cancel=None,
- )
- send_button_request = False
+ with address_ctx as address_layout, modify_ctx as modify_layout:
+ send_button_request = True
+ while True:
+ await raise_if_not_confirmed(
+ address_layout,
+ "modify_output" if send_button_request else None,
+ ButtonRequestType.ConfirmOutput,
+ )
+ result = await interact(
+ modify_layout,
+ "modify_output" if send_button_request else None,
+ ButtonRequestType.ConfirmOutput,
+ raise_on_cancel=None,
+ )
+ send_button_request = False
- if result is CONFIRMED:
- break
+ if result is CONFIRMED:
+ break
def confirm_modify_fee(
@@ -2110,7 +2136,7 @@ async def confirm_signverify(
address_title = TR.sign_message__confirm_address
br_name = "sign_message"
- address_layout = trezorui_api.confirm_value(
+ address_ctx = trezorui_api.confirm_value(
title=address_title,
value=address,
description=None,
@@ -2134,30 +2160,36 @@ async def confirm_signverify(
)
)
- info_layout = trezorui_api.show_info_with_cancel(
+ info_ctx = trezorui_api.show_info_with_cancel(
title=TR.words__title_information,
items=items,
horizontal=True,
)
- while True:
- try:
- await with_info(address_layout, info_layout, br_name, br_code=BR_CODE_OTHER)
- except ActionCancelled:
- result = await interact(
- trezorui_api.show_mismatch(title=TR.addr_mismatch__mismatch),
- None,
- raise_on_cancel=None,
- )
- assert result in (CONFIRMED, CANCELLED)
- # Right button aborts action, left goes back to showing address.
- if result is CONFIRMED:
- raise
- continue
- else:
- break
+ with address_ctx as address_layout, info_ctx as info_layout:
+ while True:
+ try:
+ await with_info(
+ address_layout, info_layout, br_name, br_code=BR_CODE_OTHER
+ )
+ except ActionCancelled:
+ with trezorui_api.show_mismatch(
+ title=TR.addr_mismatch__mismatch
+ ) as layout:
+ result = await interact(
+ layout,
+ None,
+ raise_on_cancel=None,
+ )
+ assert result in (CONFIRMED, CANCELLED)
+ # Right button aborts action, left goes back to showing address.
+ if result is CONFIRMED:
+ raise
+ continue
+ else:
+ break
- message_layout = trezorui_api.confirm_value(
+ with trezorui_api.confirm_value(
title=TR.sign_message__confirm_message,
description=None,
value=message,
@@ -2166,20 +2198,20 @@ async def confirm_signverify(
hold=not verify,
info=False,
cancel=True,
- )
+ ) as message_layout:
- if message_layout.page_count() <= LONG_MSG_PAGE_THRESHOLD:
- await interact(message_layout, br_name, BR_CODE_OTHER)
- else:
- await confirm_blob(
- br_name,
- TR.sign_message__confirm_message,
- message,
- br_code=BR_CODE_OTHER,
- hold=not verify,
- ask_pagination=True,
- verb_skip_pagination=TR.buttons__confirm,
- )
+ if message_layout.page_count() <= LONG_MSG_PAGE_THRESHOLD:
+ await interact(message_layout, br_name, BR_CODE_OTHER)
+ else:
+ await confirm_blob(
+ br_name,
+ TR.sign_message__confirm_message,
+ message,
+ br_code=BR_CODE_OTHER,
+ hold=not verify,
+ ask_pagination=True,
+ verb_skip_pagination=TR.buttons__confirm,
+ )
def error_popup(
diff --git a/core/src/trezor/ui/layouts/menu.py b/core/src/trezor/ui/layouts/menu.py
index eebf4447..5ccaecbb 100644
--- a/core/src/trezor/ui/layouts/menu.py
+++ b/core/src/trezor/ui/layouts/menu.py
@@ -47,7 +47,7 @@ class Details:
@classmethod
def from_layout(
- cls, name: str, layout_factory: Callable[[], trezorui_api.LayoutObj[T]]
+ cls, name: str, layout_factory: Callable[[], trezorui_api.LayoutContext[T]]
) -> Self:
"""IMPORTANT: `layout_factory()` MUST create a new layout on each invocation."""
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.