chore(layout): make `verb_info` optional in `confirm_with_info`
What changed, and why it matters
This is a routine user-interface cleanup change. It makes one button label parameter optional across several Trezor device layouts and updates the Python helper that calls it. There is no security-relevant behavior change: the same screens are shown, only the internal plumbing for deciding whether to display an extra 'info' button is simplified.
No security action required. Treat as normal refactoring.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The commit changes confirm_with_info so that verb_info is an Option<TString> (or str | None in the mock) instead of a required string. Layout implementations are updated to treat None the same way they previously treated an empty string: the info button is hidden. In core/src/trezor/ui/layouts/bolt/__init__.py, callers now pass None instead of an empty string when no extra info button is wanted, and one previously missing button_text argument is supplied. No cryptographic, authorization, or trust-boundary logic is touched.
Changed components
Trezor firmware UI layout APIconfirm_with_info dialog implementation across bolt/caesar/delizia/eckhart layoutsInspect captured patch +20 / −19
diff --git a/core/embed/rust/src/ui/api/firmware_micropython.rs b/core/embed/rust/src/ui/api/firmware_micropython.rs
index 1a245415..e001951f 100644
--- a/core/embed/rust/src/ui/api/firmware_micropython.rs
+++ b/core/embed/rust/src/ui/api/firmware_micropython.rs
@@ -471,7 +471,10 @@ extern "C" fn new_confirm_with_info(n_args: usize, args: *const Obj, kwargs: *mu
.try_into_option()?;
let items: Obj = kwargs.get(Qstr::MP_QSTR_items)?;
let verb: TString = kwargs.get(Qstr::MP_QSTR_verb)?.try_into()?;
- let verb_info: TString = kwargs.get(Qstr::MP_QSTR_verb_info)?.try_into()?;
+ let verb_info: Option<TString<'static>> = kwargs
+ .get(Qstr::MP_QSTR_verb_info)
+ .unwrap_or_else(|_| Obj::const_none())
+ .try_into_option()?;
let verb_cancel: Option<TString<'static>> = kwargs
.get(Qstr::MP_QSTR_verb_cancel)
.unwrap_or_else(|_| Obj::const_none())
@@ -1686,7 +1689,7 @@ pub static mp_module_trezorui_api: Module = obj_module! {
/// subtitle: str | None = None,
/// items: Iterable[tuple[StrOrBytes, bool]],
/// verb: str,
- /// verb_info: str,
+ /// verb_info: str | None = None,
/// verb_cancel: str | None = None,
/// external_menu: bool = False,
/// ) -> LayoutObj[UiResult]:
diff --git a/core/embed/rust/src/ui/layout_bolt/ui_firmware.rs b/core/embed/rust/src/ui/layout_bolt/ui_firmware.rs
index 9db05930..d4455cf5 100644
--- a/core/embed/rust/src/ui/layout_bolt/ui_firmware.rs
+++ b/core/embed/rust/src/ui/layout_bolt/ui_firmware.rs
@@ -493,7 +493,7 @@ impl FirmwareUI for UIBolt {
_subtitle: Option<TString<'static>>,
items: Obj,
verb: TString<'static>,
- verb_info: TString<'static>,
+ verb_info: Option<TString<'static>>,
_verb_cancel: Option<TString<'static>>,
_external_menu: bool,
) -> Result<Gc<LayoutObj>, Error> {
@@ -521,16 +521,15 @@ impl FirmwareUI for UIBolt {
}
.styled(theme::button_confirm());
- if verb_info.is_empty() {
- // hide the info button if its verb is empty
- let buttons = Button::cancel_confirm_text(None, Some(verb));
+ if let Some(verb_info) = verb_info {
+ let buttons = Button::cancel_info_confirm(confirm_button, verb_info);
LayoutObj::new(Frame::left_aligned(
theme::label_title(),
title,
Dialog::new(paragraphs.into_paragraphs(), buttons),
))
} else {
- let buttons = Button::cancel_info_confirm(confirm_button, verb_info);
+ let buttons = Button::cancel_confirm_text(None, Some(verb));
LayoutObj::new(Frame::left_aligned(
theme::label_title(),
title,
diff --git a/core/embed/rust/src/ui/layout_caesar/ui_firmware.rs b/core/embed/rust/src/ui/layout_caesar/ui_firmware.rs
index b37171c6..3c8b7ec5 100644
--- a/core/embed/rust/src/ui/layout_caesar/ui_firmware.rs
+++ b/core/embed/rust/src/ui/layout_caesar/ui_firmware.rs
@@ -624,7 +624,7 @@ impl FirmwareUI for UICaesar {
_subtitle: Option<TString<'static>>,
items: Obj,
verb: TString<'static>,
- verb_info: TString<'static>,
+ verb_info: Option<TString<'static>>,
verb_cancel: Option<TString<'static>>,
external_menu: bool,
) -> Result<Gc<LayoutObj>, Error> {
@@ -651,7 +651,7 @@ impl FirmwareUI for UICaesar {
paragraphs.into_paragraphs(),
verb_cancel,
verb,
- verb_info,
+ verb_info.unwrap_or_else(TString::empty),
)
.with_menu(external_menu),
))
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 42c117e8..b1f324a2 100644
--- a/core/embed/rust/src/ui/layout_delizia/ui_firmware.rs
+++ b/core/embed/rust/src/ui/layout_delizia/ui_firmware.rs
@@ -467,7 +467,7 @@ impl FirmwareUI for UIDelizia {
subtitle: Option<TString<'static>>,
items: Obj,
verb: TString<'static>,
- verb_info: TString<'static>,
+ verb_info: Option<TString<'static>>,
_verb_cancel: Option<TString<'static>>,
_external_menu: bool,
) -> Result<Gc<LayoutObj>, Error> {
@@ -495,9 +495,7 @@ impl FirmwareUI for UIDelizia {
}
let flow = flow::new_confirm_action_simple(
paragraphs.into_paragraphs(),
- ConfirmActionExtra::Menu(
- ConfirmActionMenuStrings::new().with_verb_info(Some(verb_info)),
- ),
+ ConfirmActionExtra::Menu(ConfirmActionMenuStrings::new().with_verb_info(verb_info)),
strings,
ConfirmActionOptions::new(),
)?;
diff --git a/core/embed/rust/src/ui/layout_eckhart/ui_firmware.rs b/core/embed/rust/src/ui/layout_eckhart/ui_firmware.rs
index f6d93031..b3a49d27 100644
--- a/core/embed/rust/src/ui/layout_eckhart/ui_firmware.rs
+++ b/core/embed/rust/src/ui/layout_eckhart/ui_firmware.rs
@@ -586,7 +586,7 @@ impl FirmwareUI for UIEckhart {
subtitle: Option<TString<'static>>,
items: Obj,
verb: TString<'static>,
- verb_info: TString<'static>,
+ verb_info: Option<TString<'static>>,
_verb_cancel: Option<TString<'static>>,
_external_menu: bool,
) -> Result<Gc<LayoutObj>, Error> {
@@ -617,7 +617,7 @@ impl FirmwareUI for UIEckhart {
None,
Some(verb),
false,
- Some(verb_info),
+ verb_info,
None,
)?;
LayoutObj::new_root(flow)
diff --git a/core/embed/rust/src/ui/ui_firmware.rs b/core/embed/rust/src/ui/ui_firmware.rs
index b290a0ef..50e3ba20 100644
--- a/core/embed/rust/src/ui/ui_firmware.rs
+++ b/core/embed/rust/src/ui/ui_firmware.rs
@@ -167,7 +167,7 @@ pub trait FirmwareUI {
subtitle: Option<TString<'static>>,
items: Obj, // TODO: replace Obj
verb: TString<'static>,
- verb_info: TString<'static>,
+ verb_info: Option<TString<'static>>,
verb_cancel: Option<TString<'static>>,
external_menu: bool,
) -> Result<Gc<LayoutObj>, Error>;
diff --git a/core/mocks/generated/trezorui_api.pyi b/core/mocks/generated/trezorui_api.pyi
index f31ac750..771107b0 100644
--- a/core/mocks/generated/trezorui_api.pyi
+++ b/core/mocks/generated/trezorui_api.pyi
@@ -345,7 +345,7 @@ def confirm_with_info(
subtitle: str | None = None,
items: Iterable[tuple[StrOrBytes, bool]],
verb: str,
- verb_info: str,
+ verb_info: str | None = None,
verb_cancel: str | None = None,
external_menu: bool = False,
) -> LayoutObj[UiResult]:
diff --git a/core/src/trezor/ui/layouts/bolt/__init__.py b/core/src/trezor/ui/layouts/bolt/__init__.py
index de3e6a5b..18a2f0bc 100644
--- a/core/src/trezor/ui/layouts/bolt/__init__.py
+++ b/core/src/trezor/ui/layouts/bolt/__init__.py
@@ -650,7 +650,7 @@ async def should_show_more(
title=title,
items=items,
verb=confirm or TR.buttons__confirm,
- verb_info=TR.buttons__show_all if button_text is None else button_text,
+ verb_info=button_text,
),
br_name,
br_code,
@@ -686,6 +686,7 @@ async def _confirm_ask_pagination(
if not await should_show_more(
title,
[(description, False), (data, True)],
+ button_text=TR.buttons__show_all,
br_name=br_name,
br_code=br_code,
confirm=DOWN_ARROW if extra_confirmation_if_not_read else None,
@@ -740,7 +741,7 @@ async def confirm_blob_prefix(
)
confirmed_len += len(prefix)
- button_text = TR.words__show_next if confirmed_len < total_len else ""
+ button_text = TR.words__show_next if confirmed_len < total_len else None
show_more = await should_show_more(
title=f"{title}:\n{confirmed_len} / {total_len} bytes",
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.