What changed, and why it matters
This commit adds a back button (swipe-down gesture) to the transaction summary screen on Trezor's Delizia and Eckhart UI layouts. Previously, the summary screen only allowed swiping up to proceed to the hold-to-confirm step. Now, when the caller requests it, users can swipe down to go back. This is a user-interface feature change, not a security fix or vulnerability.
No security action required. Review as normal UI/UX feature code. Ensure downstream callers that pass `back_button=True` handle the new BACK outcome correctly to avoid unexpected user flows.
Security signals we found
No security-relevant signals in the commit message or diff
UI flow change only; no cryptographic, memory-safety, or authentication changes
Return type broadened from None to UiResult to allow BACK outcome
Evidence from the diff
The change threads a back_button boolean parameter through confirm_total() Python wrappers and the Rust confirm_summary flow. When back_button is true, the summary page enables a downward swipe that emits FlowMsg::Back, and the Python wrapper switches from raise_if_not_confirmed() (which only accepts CONFIRM) to interact() (which can return BACK or CONFIRM). The default remains back_button=False, preserving existing behavior unless callers opt in.
Changed components
core/embed/rust/src/ui/layout_delizia/flow/confirm_summary.rscore/embed/rust/src/ui/layout_delizia/ui_firmware.rscore/src/trezor/ui/layouts/delizia/__init__.pycore/src/trezor/ui/layouts/eckhart/__init__.pyInspect captured patch +17 / −7
diff --git a/core/embed/rust/src/ui/layout_delizia/flow/confirm_summary.rs b/core/embed/rust/src/ui/layout_delizia/flow/confirm_summary.rs
index 30f728c0..6beee00d 100644
--- a/core/embed/rust/src/ui/layout_delizia/flow/confirm_summary.rs
+++ b/core/embed/rust/src/ui/layout_delizia/flow/confirm_summary.rs
@@ -44,6 +44,7 @@ impl FlowController for ConfirmSummary {
fn handle_swipe(&'static self, direction: Direction) -> Decision {
match (self, direction) {
+ (Self::Summary, Direction::Down) => self.return_msg(FlowMsg::Back),
(Self::Summary, Direction::Up) => Self::Hold.swipe(direction),
(Self::Hold, Direction::Down) => Self::Summary.swipe(direction),
_ => self.do_nothing(),
@@ -72,10 +73,14 @@ pub fn new_confirm_summary(
extra_params: Option<ShowInfoParams>,
extra_title: Option<TString<'static>>,
verb_cancel: Option<TString<'static>>,
+ can_go_back: bool,
) -> Result<SwipeFlow, error::Error> {
// Summary
- let content_summary = summary_params
- .with_flow_menu(true)
+ let mut content_summary = summary_params.with_flow_menu(true);
+ if can_go_back {
+ content_summary = content_summary.with_swipe_down();
+ }
+ let content_summary = content_summary
.into_layout()?
// Summary(1) + Hold(1)
.with_pages(|summary_pages| summary_pages + 1);
diff --git a/core/embed/rust/src/ui/layout_delizia/ui_firmware.rs b/core/embed/rust/src/ui/layout_delizia/ui_firmware.rs
index 66aa180f..202d188f 100644
--- a/core/embed/rust/src/ui/layout_delizia/ui_firmware.rs
+++ b/core/embed/rust/src/ui/layout_delizia/ui_firmware.rs
@@ -393,7 +393,7 @@ impl FirmwareUI for UIDelizia {
extra_items: Option<Obj>,
extra_title: Option<TString<'static>>,
verb_cancel: Option<TString<'static>>,
- _back_button: bool,
+ back_button: bool,
_external_menu: bool, // TODO: will eventually replace the internal menu
) -> Result<impl LayoutMaybeTrace, Error> {
let mut summary_params = ShowInfoParams::new(title.unwrap_or(TString::empty()))
@@ -437,6 +437,7 @@ impl FirmwareUI for UIDelizia {
extra_params,
extra_title,
verb_cancel,
+ back_button,
)?;
Ok(flow)
}
diff --git a/core/src/trezor/ui/layouts/delizia/__init__.py b/core/src/trezor/ui/layouts/delizia/__init__.py
index 1bcd3e12..6305223d 100644
--- a/core/src/trezor/ui/layouts/delizia/__init__.py
+++ b/core/src/trezor/ui/layouts/delizia/__init__.py
@@ -916,14 +916,15 @@ def confirm_total(
account_title: str | None = None,
account_items: Iterable[StrPropertyType] | None = None,
fee_items: Iterable[StrPropertyType] | None = None,
+ back_button: bool = False,
br_name: str = "confirm_total",
br_code: ButtonRequestType = ButtonRequestType.SignTx,
-) -> Awaitable[None]:
+) -> Awaitable[ui.UiResult]:
title = title or TR.words__title_summary # def_arg
total_label = total_label or TR.send__total_amount # def_arg
fee_label = fee_label or TR.send__incl_transaction_fee # def_arg
- return raise_if_not_confirmed(
+ return interact(
trezorui_api.confirm_summary(
amount=total_amount,
amount_label=total_label,
@@ -934,6 +935,7 @@ def confirm_total(
account_items=account_items,
extra_items=fee_items,
extra_title=TR.confirm_total__title_fee,
+ back_button=back_button,
),
br_name,
br_code,
diff --git a/core/src/trezor/ui/layouts/eckhart/__init__.py b/core/src/trezor/ui/layouts/eckhart/__init__.py
index 115f861e..d122ac14 100644
--- a/core/src/trezor/ui/layouts/eckhart/__init__.py
+++ b/core/src/trezor/ui/layouts/eckhart/__init__.py
@@ -925,14 +925,15 @@ def confirm_total(
account_title: str | None = None,
account_items: Iterable[StrPropertyType] | None = None,
fee_items: Iterable[StrPropertyType] | None = None,
+ back_button: bool = False,
br_name: str = "confirm_total",
br_code: ButtonRequestType = ButtonRequestType.SignTx,
-) -> Awaitable[None]:
+) -> Awaitable[ui.UiResult]:
title = title or TR.words__send # def_arg
total_label = total_label or TR.send__total_amount # def_arg
fee_label = fee_label or TR.send__incl_transaction_fee # def_arg
- return raise_if_not_confirmed(
+ return interact(
trezorui_api.confirm_summary(
amount=total_amount,
amount_label=total_label,
@@ -943,6 +944,7 @@ def confirm_total(
account_items=account_items,
extra_items=fee_items,
extra_title=TR.confirm_total__title_fee,
+ back_button=back_button,
),
br_name,
br_code,
Why this scored 19/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.