What changed, and why it matters
This commit removes an old, duplicated screen called flow_confirm_output and makes the confirm_output function always use the newer confirm_value-based flow. There is no security bug being fixed here; it is a cleanup that unifies how transaction recipient and amount confirmations are shown on newer Trezor device layouts.
No security action needed. Treat as normal refactoring; verify that existing UI tests for confirm_output still pass with the unified flow.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The change deletes the flow_confirm_output Rust flow implementation for Delizia and Eckhart layouts, removes its micropython API binding, trait method, and mock stub, and updates the Python confirm_output() helper in both layouts so the amount parameter is required and the legacy single-screen path is gone. All calls now go through confirm_linear_flow/confirm_value or confirm_value with a menu, which already existed and is the intended modern behavior.
Changed components
core/embed/rust/src/ui/api/firmware_micropython.rscore/embed/rust/src/ui/layout_delizia/flow/confirm_output.rscore/embed/rust/src/ui/layout_eckhart/flow/confirm_output.rscore/embed/rust/src/ui/layout_delizia/ui_firmware.rscore/embed/rust/src/ui/layout_eckhart/ui_firmware.rscore/embed/rust/src/ui/ui_firmware.rscore/src/trezor/ui/layouts/delizia/__init__.pycore/src/trezor/ui/layouts/eckhart/__init__.pyInspect captured patch +102 / −790
diff --git a/core/embed/rust/librust_qstr.h b/core/embed/rust/librust_qstr.h
index eb2b1112..0cebd951 100644
--- a/core/embed/rust/librust_qstr.h
+++ b/core/embed/rust/librust_qstr.h
@@ -82,7 +82,6 @@ static void _librust_qstrs(void) {
MP_QSTR_about_items;
MP_QSTR_account;
MP_QSTR_account_items;
- MP_QSTR_account_path;
MP_QSTR_account_title;
MP_QSTR_accounts;
MP_QSTR_action;
@@ -114,7 +113,6 @@ static void _librust_qstrs(void) {
MP_QSTR_address_details__derivation_path_colon;
MP_QSTR_address_details__title_receive_address;
MP_QSTR_address_details__title_receiving_to;
- MP_QSTR_address_item;
MP_QSTR_address_label;
MP_QSTR_address_qr;
MP_QSTR_allow_cancel;
@@ -268,7 +266,6 @@ static void _librust_qstrs(void) {
MP_QSTR_buy_amount;
MP_QSTR_can_go_back;
MP_QSTR_cancel;
- MP_QSTR_cancel_text;
MP_QSTR_case_sensitive;
MP_QSTR_check_homescreen_format;
MP_QSTR_chunkify;
@@ -350,7 +347,6 @@ static void _librust_qstrs(void) {
MP_QSTR_firmware_update__restart;
MP_QSTR_firmware_update__title;
MP_QSTR_firmware_update__title_fingerprint;
- MP_QSTR_flow_confirm_output;
MP_QSTR_flow_confirm_set_new_code;
MP_QSTR_flow_get_address;
MP_QSTR_flow_get_pubkey;
@@ -459,7 +455,6 @@ static void _librust_qstrs(void) {
MP_QSTR_max_ms;
MP_QSTR_max_rounds;
MP_QSTR_menu_title;
- MP_QSTR_message;
MP_QSTR_min_count;
MP_QSTR_min_ms;
MP_QSTR_misc__decrypt_value;
@@ -861,7 +856,6 @@ static void _librust_qstrs(void) {
MP_QSTR_text_check;
MP_QSTR_text_confirm;
MP_QSTR_text_footer;
- MP_QSTR_text_mono;
MP_QSTR_thp__autoconnect;
MP_QSTR_thp__autoconnect_app;
MP_QSTR_thp__autoconnect_title;
diff --git a/core/embed/rust/src/ui/api/firmware_micropython.rs b/core/embed/rust/src/ui/api/firmware_micropython.rs
index 1bb389e1..1fb9ea3b 100644
--- a/core/embed/rust/src/ui/api/firmware_micropython.rs
+++ b/core/embed/rust/src/ui/api/firmware_micropython.rs
@@ -523,49 +523,6 @@ extern "C" fn new_continue_recovery_homepage(
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
}
-extern "C" fn new_flow_confirm_output(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
- let block = move |_args: &[Obj], kwargs: &Map| {
- let title: Option<TString> = kwargs.get(Qstr::MP_QSTR_title)?.try_into_option()?;
- let subtitle: Option<TString> = kwargs.get(Qstr::MP_QSTR_subtitle)?.try_into_option()?;
- let extra: Option<TString> = kwargs.get(Qstr::MP_QSTR_extra)?.try_into_option()?;
- let description: Option<TString> =
- kwargs.get(Qstr::MP_QSTR_description)?.try_into_option()?;
- let message: TString = kwargs.get(Qstr::MP_QSTR_message)?.try_into()?;
- let chunkify: bool = kwargs.get_or(Qstr::MP_QSTR_chunkify, false)?;
- let text_mono: bool = kwargs.get_or(Qstr::MP_QSTR_text_mono, true)?;
- let account_title: TString = kwargs.get(Qstr::MP_QSTR_account_title)?.try_into()?;
- let account: Option<TString> = kwargs.get(Qstr::MP_QSTR_account)?.try_into_option()?;
- let account_path: Option<TString> =
- kwargs.get(Qstr::MP_QSTR_account_path)?.try_into_option()?;
- let br_code: u16 = kwargs.get(Qstr::MP_QSTR_br_code)?.try_into()?;
- let br_name: TString = kwargs.get(Qstr::MP_QSTR_br_name)?.try_into()?;
-
- let address_item: Option<Obj> =
- kwargs.get(Qstr::MP_QSTR_address_item)?.try_into_option()?;
- let cancel_text: Option<TString> =
- kwargs.get(Qstr::MP_QSTR_cancel_text)?.try_into_option()?;
-
- let layout = ModelUI::flow_confirm_output(
- title,
- subtitle,
- description,
- extra,
- message,
- chunkify,
- text_mono,
- account_title,
- account,
- account_path,
- br_code,
- br_name,
- address_item,
- cancel_text,
- )?;
- Ok(LayoutObj::new_root(layout)?.into())
- };
- unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
-}
-
extern "C" fn new_flow_confirm_set_new_code(
n_args: usize,
args: *const Obj,
@@ -1745,26 +1702,6 @@ pub static mp_module_trezorui_api: Module = obj_module! {
/// """Device recovery homescreen."""
Qstr::MP_QSTR_continue_recovery_homepage => obj_fn_kw!(0, new_continue_recovery_homepage).as_obj(),
- /// def flow_confirm_output(
- /// *,
- /// title: str | None,
- /// subtitle: str | None,
- /// message: str,
- /// description: str | None,
- /// extra: str | None,
- /// chunkify: bool,
- /// text_mono: bool,
- /// account_title: str,
- /// account: str | None,
- /// account_path: str | None,
- /// br_code: ButtonRequestType,
- /// br_name: str,
- /// address_item: PropertyType | None,
- /// cancel_text: str | None = None,
- /// ) -> LayoutObj[UiResult]:
- /// """Confirm the recipient, (optionally) confirm the amount and (optionally) confirm the summary and present a Hold to Sign page."""
- Qstr::MP_QSTR_flow_confirm_output => obj_fn_kw!(0, new_flow_confirm_output).as_obj(),
-
/// def flow_confirm_set_new_code(
/// *,
/// is_wipe_code: bool,
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 41892e7c..9db05930 100644
--- a/core/embed/rust/src/ui/layout_bolt/ui_firmware.rs
+++ b/core/embed/rust/src/ui/layout_bolt/ui_firmware.rs
@@ -587,25 +587,6 @@ impl FirmwareUI for UIBolt {
}
}
- fn flow_confirm_output(
- _title: Option<TString<'static>>,
- _subtitle: Option<TString<'static>>,
- _description: Option<TString<'static>>,
- _extra: Option<TString<'static>>,
- _message: TString<'static>,
- _chunkify: bool,
- _text_mono: bool,
- _account_title: TString<'static>,
- _account: Option<TString<'static>>,
- _account_path: Option<TString<'static>>,
- _br_code: u16,
- _br_name: TString<'static>,
- _address_item: Option<Obj>,
- _cancel_text: Option<TString<'static>>,
- ) -> Result<impl LayoutMaybeTrace, Error> {
- Err::<RootComponent<Empty, ModelUI>, Error>(Error::NotImplementedError)
- }
-
fn flow_confirm_set_new_code(_is_wipe_code: bool) -> Result<impl LayoutMaybeTrace, Error> {
Err::<RootComponent<Empty, ModelUI>, Error>(Error::NotImplementedError)
}
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 5471eeac..da353b9c 100644
--- a/core/embed/rust/src/ui/layout_caesar/ui_firmware.rs
+++ b/core/embed/rust/src/ui/layout_caesar/ui_firmware.rs
@@ -701,25 +701,6 @@ impl FirmwareUI for UICaesar {
LayoutObj::new_root(layout)
}
- fn flow_confirm_output(
- _title: Option<TString<'static>>,
- _subtitle: Option<TString<'static>>,
- _description: Option<TString<'static>>,
- _extra: Option<TString<'static>>,
- _message: TString<'static>,
- _chunkify: bool,
- _text_mono: bool,
- _account_title: TString<'static>,
- _account: Option<TString<'static>>,
- _account_path: Option<TString<'static>>,
- _br_code: u16,
- _br_name: TString<'static>,
- _address_item: Option<Obj>,
- _cancel_text: Option<TString<'static>>,
- ) -> Result<impl LayoutMaybeTrace, Error> {
- Err::<RootComponent<Empty, ModelUI>, Error>(Error::NotImplementedError)
- }
-
fn flow_confirm_set_new_code(_is_wipe_code: bool) -> Result<impl LayoutMaybeTrace, Error> {
Err::<RootComponent<Empty, ModelUI>, Error>(Error::NotImplementedError)
}
diff --git a/core/embed/rust/src/ui/layout_delizia/flow/confirm_output.rs b/core/embed/rust/src/ui/layout_delizia/flow/confirm_output.rs
deleted file mode 100644
index 676fcadb..00000000
--- a/core/embed/rust/src/ui/layout_delizia/flow/confirm_output.rs
+++ /dev/null
@@ -1,131 +0,0 @@
-use heapless::Vec;
-
-use crate::{
- error,
- strutil::TString,
- translations::TR,
- ui::{
- button_request::ButtonRequest,
- component::{ButtonRequestExt, ComponentExt, MsgMap},
- flow::{
- base::{Decision, DecisionBuilder as _},
- FlowController, FlowMsg, SwipeFlow,
- },
- geometry::Direction,
- },
-};
-
-use super::{
- super::{
- component::{
- AddressDetails, Frame, FrameMsg, PromptMsg, PromptScreen, SwipeContent, VerticalMenu,
- VerticalMenuChoiceMsg,
- },
- theme,
- },
- util::ConfirmValue,
-};
-
-const MENU_ITEM_CANCEL: usize = 0;
-const MENU_ITEM_ADDRESS_INFO: usize = 1;
-const MENU_ITEM_ACCOUNT_INFO: usize = 2;
-
-#[derive(Copy, Clone, PartialEq, Eq)]
-pub enum ConfirmOutput {
- Address,
- Menu,
- AccountInfo,
- CancelTap,
-}
-
-impl FlowController for ConfirmOutput {
- #[inline]
- fn index(&'static self) -> usize {
- *self as usize
- }
-
- fn handle_swipe(&'static self, direction: Direction) -> Decision {
- match (self, direction) {
- (Self::Address, Direction::Up) => self.return_msg(FlowMsg::Confirmed),
- _ => self.do_nothing(),
- }
- }
-
- fn handle_event(&'static self, msg: FlowMsg) -> Decision {
- match (self, msg) {
- (_, FlowMsg::Info) => Self::Menu.goto(),
- (Self::Menu, FlowMsg::Choice(MENU_ITEM_CANCEL)) => Self::CancelTap.swipe_left(),
- (Self::Menu, FlowMsg::Choice(MENU_ITEM_ACCOUNT_INFO)) => Self::AccountInfo.goto(),
- (Self::Menu, FlowMsg::Cancelled) => Self::Address.swipe_right(),
- (Self::CancelTap, FlowMsg::Confirmed) => self.return_msg(FlowMsg::Cancelled),
- (_, FlowMsg::Cancelled) => Self::Menu.goto(),
- _ => self.do_nothing(),
- }
- }
-}
-
-fn get_cancel_page(
-) -> MsgMap<Frame<SwipeContent<PromptScreen>>, impl Fn(FrameMsg<PromptMsg>) -> Option<FlowMsg>> {
- Frame::left_aligned(
- TR::send__cancel_sign.into(),
- SwipeContent::new(PromptScreen::new_tap_to_cancel()),
- )
- .with_cancel_button()
- .with_footer(TR::instructions__tap_to_confirm.into(), None)
- .map(super::util::map_to_confirm)
-}
-
-#[allow(clippy::too_many_arguments)]
-pub fn new_confirm_output(
- confirm_main: ConfirmValue,
- account_title: TString<'static>,
- account: Option<TString<'static>>,
- account_path: Option<TString<'static>>,
- br_name: TString<'static>,
- br_code: u16,
- confirm_address: Option<ConfirmValue>,
- cancel_text: Option<TString<'static>>,
-) -> Result<SwipeFlow, error::Error> {
- // Main
- let main_content = confirm_main
- .with_flow_menu(true)
- .into_layout()?
- .one_button_request(ButtonRequest::from_num(br_code, br_name));
-
- // MainMenu
- let mut main_menu = VerticalMenu::empty();
- let mut main_menu_items = Vec::<usize, 3>::new();
- if let Some(ref confirm_address) = confirm_address {
- main_menu = main_menu.item(theme::ICON_CHEVRON_RIGHT, confirm_address.title());
- unwrap!(main_menu_items.push(MENU_ITEM_ADDRESS_INFO));
- }
- if account.is_some() && account_path.is_some() {
- main_menu = main_menu.item(
- theme::ICON_CHEVRON_RIGHT,
- TR::address_details__account_info.into(),
- );
- unwrap!(main_menu_items.push(MENU_ITEM_ACCOUNT_INFO));
- }
- main_menu = main_menu.cancel_item(cancel_text.unwrap_or(TR::send__cancel_sign.into()));
- unwrap!(main_menu_items.push(MENU_ITEM_CANCEL));
- let content_main_menu = Frame::left_aligned(TString::empty(), main_menu)
- .with_cancel_button()
- .map(move |msg| match msg {
- VerticalMenuChoiceMsg::Selected(i) => {
- let selected_item = main_menu_items[i];
- Some(FlowMsg::Choice(selected_item))
- }
- });
-
- // AccountInfo
- let ac = AddressDetails::new(account_title, account, account_path)?;
- let account_content = ac.map(|_| Some(FlowMsg::Cancelled));
-
- let mut flow = SwipeFlow::new(&ConfirmOutput::Address)?;
- flow.add_page(&ConfirmOutput::Address, main_content)?
- .add_page(&ConfirmOutput::Menu, content_main_menu)?
- .add_page(&ConfirmOutput::AccountInfo, account_content)?
- .add_page(&ConfirmOutput::CancelTap, get_cancel_page())?;
-
- Ok(flow)
-}
diff --git a/core/embed/rust/src/ui/layout_delizia/flow/mod.rs b/core/embed/rust/src/ui/layout_delizia/flow/mod.rs
index 89d563fb..3f20d60c 100644
--- a/core/embed/rust/src/ui/layout_delizia/flow/mod.rs
+++ b/core/embed/rust/src/ui/layout_delizia/flow/mod.rs
@@ -3,7 +3,6 @@ pub mod confirm_action;
pub mod confirm_fido;
pub mod confirm_firmware_update;
pub mod confirm_homescreen;
-pub mod confirm_output;
pub mod confirm_reset;
pub mod confirm_set_new_code;
pub mod confirm_summary;
@@ -26,7 +25,6 @@ pub use confirm_action::{
pub use confirm_fido::new_confirm_fido;
pub use confirm_firmware_update::new_confirm_firmware_update;
pub use confirm_homescreen::new_confirm_homescreen;
-pub use confirm_output::new_confirm_output;
pub use confirm_reset::new_confirm_reset;
pub use confirm_set_new_code::SetNewCode;
pub use confirm_summary::new_confirm_summary;
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 b229ff4d..c170c938 100644
--- a/core/embed/rust/src/ui/layout_delizia/ui_firmware.rs
+++ b/core/embed/rust/src/ui/layout_delizia/ui_firmware.rs
@@ -538,61 +538,6 @@ impl FirmwareUI for UIDelizia {
LayoutObj::new_root(flow)
}
- fn flow_confirm_output(
- title: Option<TString<'static>>,
- subtitle: Option<TString<'static>>,
- description: Option<TString<'static>>,
- extra: Option<TString<'static>>,
- message: TString<'static>,
- chunkify: bool,
- text_mono: bool,
- account_title: TString<'static>,
- account: Option<TString<'static>>,
- account_path: Option<TString<'static>>,
- br_code: u16,
- br_name: TString<'static>,
- address_item: Option<Obj>,
- cancel_text: Option<TString<'static>>,
- ) -> Result<impl LayoutMaybeTrace, Error> {
- let confirm_main = ConfirmValue::new(
- title.unwrap_or(TString::empty()),
- message.into(),
- description,
- )
- .with_description_font(&theme::TEXT_MAIN_GREY_LIGHT)
- .with_subtitle(subtitle)
- .with_extra(extra)
- .with_extra_font(&theme::TEXT_SUB_GREY)
- .with_menu_button()
- .with_swipeup_footer(None)
- .with_chunkify(chunkify)
- .with_text_mono(text_mono);
-
- let confirm_address = address_item.map(|address_item| {
- let [key, value, _is_data]: [Obj; 3] = unwrap!(util::iter_into_array(address_item));
- ConfirmValue::new(
- key.try_into().unwrap_or(TString::empty()),
- value.try_into().unwrap_or(StrOrBytes::Str("".into())),
- None,
- )
- .with_cancel_button()
- .with_chunkify(true)
- .with_text_mono(true)
- });
-
- let flow = flow::confirm_output::new_confirm_output(
- confirm_main,
- account_title,
- account,
- account_path,
- br_name,
- br_code,
- confirm_address,
- cancel_text,
- )?;
- Ok(flow)
- }
-
fn flow_confirm_set_new_code(is_wipe_code: bool) -> Result<impl LayoutMaybeTrace, Error> {
let flow = flow::confirm_set_new_code::new_set_new_code(is_wipe_code)?;
Ok(flow)
diff --git a/core/embed/rust/src/ui/layout_eckhart/flow/confirm_output.rs b/core/embed/rust/src/ui/layout_eckhart/flow/confirm_output.rs
deleted file mode 100644
index 04370958..00000000
--- a/core/embed/rust/src/ui/layout_eckhart/flow/confirm_output.rs
+++ /dev/null
@@ -1,205 +0,0 @@
-use heapless::Vec;
-
-use crate::{
- error,
- strutil::TString,
- time::Duration,
- translations::TR,
- ui::{
- button_request::ButtonRequest,
- component::{
- text::paragraphs::{Paragraph, ParagraphSource, ParagraphVecShort, Paragraphs},
- ButtonRequestExt, ComponentExt, MsgMap,
- },
- flow::{
- base::{Decision, DecisionBuilder as _},
- FlowController, FlowMsg, SwipeFlow,
- },
- geometry::{Direction, LinearPlacement},
- },
-};
-
-use super::super::{
- component::Button,
- firmware::{
- ActionBar, Header, Hint, ShortMenuVec, TextScreen, TextScreenMsg, VerticalMenu,
- VerticalMenuScreen, VerticalMenuScreenMsg,
- },
- flow::util::content_menu_info,
- theme::{self, gradient::Gradient},
-};
-
-const MENU_ITEM_CANCEL: usize = 0;
-const MENU_ITEM_ADDRESS_INFO: usize = 1;
-const MENU_ITEM_ACCOUNT_INFO: usize = 2;
-
-const TIMEOUT: Duration = Duration::from_secs(2);
-
-#[derive(Copy, Clone, PartialEq, Eq)]
-pub enum ConfirmOutput {
- Address,
- Menu,
- AccountInfo,
- Cancel,
- Cancelled,
-}
-
-impl FlowController for ConfirmOutput {
- #[inline]
- fn index(&'static self) -> usize {
- *self as usize
- }
-
- fn handle_swipe(&'static self, _direction: Direction) -> Decision {
- self.do_nothing()
- }
-
- fn handle_event(&'static self, msg: FlowMsg) -> Decision {
- match (self, msg) {
- (Self::Address, FlowMsg::Confirmed) => self.return_msg(FlowMsg::Confirmed),
- (Self::Address, FlowMsg::Info) => Self::Menu.goto(),
- (Self::Menu, FlowMsg::Choice(MENU_ITEM_CANCEL)) => Self::Cancel.goto(),
- (Self::Menu, FlowMsg::Choice(MENU_ITEM_ACCOUNT_INFO)) => Self::AccountInfo.goto(),
- (Self::Menu, FlowMsg::Cancelled) => Self::Address.goto(),
- (Self::AccountInfo, FlowMsg::Cancelled) => Self::Menu.goto(),
- (Self::Cancel, FlowMsg::Confirmed) => Self::Cancelled.goto(),
- (Self::Cancel, FlowMsg::Cancelled) => Self::Menu.goto(),
- (Self::Cancelled, _) => self.return_msg(FlowMsg::Cancelled),
- _ => self.do_nothing(),
- }
- }
-}
-
-fn content_cancel(
-) -> MsgMap<TextScreen<Paragraphs<Paragraph<'static>>>, impl Fn(TextScreenMsg) -> Option<FlowMsg>> {
- TextScreen::new(
- Paragraph::new(&theme::TEXT_REGULAR, TR::send__cancel_sign)
- .into_paragraphs()
- .with_placement(LinearPlacement::vertical()),
- )
- .with_header(Header::new(TR::words__send.into()))
- .with_action_bar(ActionBar::new_double(
- Button::with_icon(theme::ICON_CHEVRON_LEFT),
- Button::with_text(TR::buttons__cancel.into())
- .styled(theme::button_actionbar_danger())
- .with_gradient(Gradient::Alert),
- ))
- .map(|msg| match msg {
- TextScreenMsg::Confirmed => Some(FlowMsg::Confirmed),
- TextScreenMsg::Cancelled => Some(FlowMsg::Cancelled),
- _ => None,
- })
-}
-
-fn content_main_menu(
- address_title: TString<'static>,
- address_params: bool,
- account_params: bool,
- cancel_menu_label: TString<'static>,
-) -> MsgMap<VerticalMenuScreen<ShortMenuVec>, impl Fn(VerticalMenuScreenMsg) -> Option<FlowMsg>> {
- let mut main_menu = VerticalMenu::<ShortMenuVec>::empty();
- let mut main_menu_items = Vec::<usize, 3>::new();
- if address_params {
- main_menu.item(Button::new_menu_item(
- address_title,
- theme::menu_item_title(),
- ));
- unwrap!(main_menu_items.push(MENU_ITEM_ADDRESS_INFO));
- }
- if account_params {
- main_menu.item(Button::new_menu_item(
- TR::address_details__account_info.into(),
- theme::menu_item_title(),
- ));
- unwrap!(main_menu_items.push(MENU_ITEM_ACCOUNT_INFO));
- }
- main_menu.item(Button::new_cancel_menu_item(cancel_menu_label));
- unwrap!(main_menu_items.push(MENU_ITEM_CANCEL));
-
- VerticalMenuScreen::<ShortMenuVec>::new(main_menu)
- .with_header(Header::new(TString::empty()).with_close_button())
- .map(move |msg| match msg {
- VerticalMenuScreenMsg::Selected(i) => {
- let selected_item = main_menu_items[i];
- Some(FlowMsg::Choice(selected_item))
- }
- VerticalMenuScreenMsg::Close => Some(FlowMsg::Cancelled),
- _ => None,
- })
-}
-
-#[allow(clippy::too_many_arguments)]
-pub fn new_confirm_output(
- title: Option<TString<'static>>,
- subtitle: Option<TString<'static>>,
- main_paragraphs: ParagraphVecShort<'static>,
- br_name: TString<'static>,
- br_code: u16,
- account_title: TString<'static>,
- account_paragraphs: Option<ParagraphVecShort<'static>>,
- address_title: Option<TString<'static>>,
- address_paragraph: Option<Paragraph<'static>>,
- cancel_menu_label: Option<TString<'static>>,
-) -> Result<SwipeFlow, error::Error> {
- let cancel_menu_label = cancel_menu_label.unwrap_or(TR::buttons__cancel.into());
- let address_menu_item = address_paragraph.is_some();
- let account_menu_item = account_paragraphs.is_some();
- let address_title = address_title.unwrap_or(TR::words__address.into());
- let account_subtitle = Some(TR::send__send_from.into());
-
- // Main
- let content_main =
- TextScreen::new(main_paragraphs.into_paragraphs().with_placement(
- LinearPlacement::vertical().with_spacing(theme::TEXT_VERTICAL_SPACING),
- ))
- .with_flow_menu(true)
- .with_header(Header::new(title.unwrap_or(TString::empty())).with_menu_button())
- .with_subtitle(subtitle.unwrap_or(TString::empty()))
- .with_hint(Hint::new_page_counter())
- .with_action_bar(ActionBar::new_single(Button::with_text(
- TR::buttons__continue.into(),
- )))
- .map(|msg| match msg {
- TextScreenMsg::Confirmed => Some(FlowMsg::Confirmed),
- TextScreenMsg::Cancelled => Some(FlowMsg::Cancelled),
- TextScreenMsg::Menu => Some(FlowMsg::Info),
- })
- .one_button_request(ButtonRequest::from_num(br_code, br_name));
-
- // Cancelled
- let content_cancelled = TextScreen::new(
- Paragraph::new(&theme::TEXT_REGULAR, TR::send__sign_cancelled)
- .into_paragraphs()
- .with_placement(LinearPlacement::vertical()),
- )
- .with_header(Header::new(TR::words__title_done.into()).with_icon(theme::ICON_DONE, theme::GREY))
- .with_action_bar(ActionBar::new_timeout(
- Button::with_text(TR::instructions__continue_in_app.into()),
- TIMEOUT,
- ))
- .map(|_| Some(FlowMsg::Confirmed));
-
- let mut flow = SwipeFlow::new(&ConfirmOutput::Address)?;
- flow.add_page(&ConfirmOutput::Address, content_main)?
- .add_page(
- &ConfirmOutput::Menu,
- content_main_menu(
- address_title,
- address_menu_item,
- account_menu_item,
- cancel_menu_label,
- ),
- )?
- .add_page(
- &ConfirmOutput::AccountInfo,
- content_menu_info(
- account_title,
- account_subtitle,
- account_paragraphs.map_or_else(ParagraphVecShort::new, |p| p),
- ),
- )?
- .add_page(&ConfirmOutput::Cancel, content_cancel())?
- .add_page(&ConfirmOutput::Cancelled, content_cancelled)?;
-
- Ok(flow)
-}
diff --git a/core/embed/rust/src/ui/layout_eckhart/flow/mod.rs b/core/embed/rust/src/ui/layout_eckhart/flow/mod.rs
index 2c77ecac..5d79578f 100644
--- a/core/embed/rust/src/ui/layout_eckhart/flow/mod.rs
+++ b/core/embed/rust/src/ui/layout_eckhart/flow/mod.rs
@@ -1,7 +1,6 @@
#[cfg(feature = "universal_fw")]
pub mod confirm_fido;
pub mod confirm_firmware_update;
-pub mod confirm_output;
pub mod confirm_reset;
pub mod confirm_set_new_code;
pub mod confirm_summary;
@@ -21,7 +20,6 @@ pub mod util;
#[cfg(feature = "universal_fw")]
pub use confirm_fido::new_confirm_fido;
pub use confirm_firmware_update::new_confirm_firmware_update;
-pub use confirm_output::new_confirm_output;
pub use confirm_reset::new_confirm_reset;
pub use confirm_set_new_code::new_set_new_code;
pub use confirm_summary::new_confirm_summary;
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 b5585f34..f6d93031 100644
--- a/core/embed/rust/src/ui/layout_eckhart/ui_firmware.rs
+++ b/core/embed/rust/src/ui/layout_eckhart/ui_firmware.rs
@@ -671,107 +671,6 @@ impl FirmwareUI for UIEckhart {
LayoutObj::new_root(flow)
}
- fn flow_confirm_output(
- title: Option<TString<'static>>,
- subtitle: Option<TString<'static>>,
- description: Option<TString<'static>>,
- extra: Option<TString<'static>>,
- message: TString<'static>,
- chunkify: bool,
- text_mono: bool,
- account_title: TString<'static>,
- account: Option<TString<'static>>,
- account_path: Option<TString<'static>>,
- br_code: u16,
- br_name: TString<'static>,
- address_item: Option<Obj>,
- cancel_text: Option<TString<'static>>,
- ) -> Result<impl LayoutMaybeTrace, Error> {
- let mut main_paragraphs = ParagraphVecShort::new();
- if let Some(description) = description {
- main_paragraphs.add(
- Paragraph::new(&theme::TEXT_REGULAR, description)
- .with_bottom_padding(theme::PROPS_SPACING),
- );
- }
- if let Some(extra) = extra {
- main_paragraphs.add(
- Paragraph::new(&theme::TEXT_SMALL, extra).with_bottom_padding(theme::PROPS_SPACING),
- );
- }
- let font = if chunkify {
- &theme::TEXT_MONO_ADDRESS_CHUNKS
- } else if text_mono {
- &theme::TEXT_MONO_LIGHT_ELLIPSIS
- } else {
- &theme::TEXT_REGULAR
- };
- main_paragraphs.add(Paragraph::new(font, message));
-
- let (address_title, address_paragraph) = if let Some(address_item) = address_item {
- let [key, value, _is_data]: [Obj; 3] = util::iter_into_array(address_item)?;
- let paragraph = Paragraph::new(
- &theme::TEXT_MONO_ADDRESS_CHUNKS,
- value.try_into().unwrap_or(TString::empty()),
- );
- (
- Some(key.try_into().unwrap_or(TString::empty())),
- Some(paragraph),
- )
- } else {
- (None, None)
- };
-
- // collect available info
- let account_paragraphs = {
- let mut paragraphs = ParagraphVecShort::new();
- if let Some(account) = account {
- let mut para = Paragraph::new(&theme::TEXT_MONO_LIGHT, account);
- if account_path.is_some() {
- para = para.with_bottom_padding(theme::PROPS_SPACING);
- }
- paragraphs
- .add(
- Paragraph::new(&theme::TEXT_SMALL_LIGHT, TR::words__wallet)
- .with_bottom_padding(theme::PROP_INNER_SPACING)
- .no_break(),
- )
- .add(para);
- }
- if let Some(path) = account_path {
- paragraphs
- .add(
- Paragraph::new(
- &theme::TEXT_SMALL_LIGHT,
- TString::from_translation(TR::address_details__derivation_path),
- )
- .with_bottom_padding(theme::PROP_INNER_SPACING)
- .no_break(),
- )
- .add(Paragraph::new(&theme::TEXT_MONO_LIGHT, path));
- }
- if paragraphs.is_empty() {
- None
- } else {
- Some(paragraphs)
- }
- };
-
- let flow = flow::confirm_output::new_confirm_output(
- title,
- subtitle,
- main_paragraphs,
- br_name,
- br_code,
- account_title,
- account_paragraphs,
- address_title,
- address_paragraph,
- cancel_text,
- )?;
- Ok(flow)
- }
-
fn flow_confirm_set_new_code(is_wipe_code: bool) -> Result<impl LayoutMaybeTrace, Error> {
let flow = flow::confirm_set_new_code::new_set_new_code(is_wipe_code)?;
Ok(flow)
diff --git a/core/embed/rust/src/ui/ui_firmware.rs b/core/embed/rust/src/ui/ui_firmware.rs
index af11bab6..b290a0ef 100644
--- a/core/embed/rust/src/ui/ui_firmware.rs
+++ b/core/embed/rust/src/ui/ui_firmware.rs
@@ -183,24 +183,6 @@ pub trait FirmwareUI {
fn check_homescreen_format(image: BinaryData, accept_toif: bool) -> bool;
- #[allow(clippy::too_many_arguments)]
- fn flow_confirm_output(
- title: Option<TString<'static>>,
- subtitle: Option<TString<'static>>,
- description: Option<TString<'static>>,
- extra: Option<TString<'static>>,
- message: TString<'static>,
- chunkify: bool,
- text_mono: bool,
- account_title: TString<'static>,
- account: Option<TString<'static>>,
- account_path: Option<TString<'static>>,
- br_code: u16,
- br_name: TString<'static>,
- address_item: Option<Obj>,
- cancel_text: Option<TString<'static>>,
- ) -> Result<impl LayoutMaybeTrace, Error>;
-
fn flow_confirm_set_new_code(is_wipe_code: bool) -> Result<impl LayoutMaybeTrace, Error>;
#[allow(clippy::too_many_arguments)]
diff --git a/core/mocks/generated/trezorui_api.pyi b/core/mocks/generated/trezorui_api.pyi
index 923f2327..c0510f15 100644
--- a/core/mocks/generated/trezorui_api.pyi
+++ b/core/mocks/generated/trezorui_api.pyi
@@ -367,27 +367,6 @@ def continue_recovery_homepage(
"""Device recovery homescreen."""
-# rust/src/ui/api/firmware_micropython.rs
-def flow_confirm_output(
- *,
- title: str | None,
- subtitle: str | None,
- message: str,
- description: str | None,
- extra: str | None,
- chunkify: bool,
- text_mono: bool,
- account_title: str,
- account: str | None,
- account_path: str | None,
- br_code: ButtonRequestType,
- br_name: str,
- address_item: PropertyType | None,
- cancel_text: str | None = None,
-) -> LayoutObj[UiResult]:
- """Confirm the recipient, (optionally) confirm the amount and (optionally) confirm the summary and present a Hold to Sign page."""
-
-
# rust/src/ui/api/firmware_micropython.rs
def flow_confirm_set_new_code(
*,
diff --git a/core/src/trezor/ui/layouts/delizia/__init__.py b/core/src/trezor/ui/layouts/delizia/__init__.py
index 46b9942e..4d7a97dd 100644
--- a/core/src/trezor/ui/layouts/delizia/__init__.py
+++ b/core/src/trezor/ui/layouts/delizia/__init__.py
@@ -578,7 +578,7 @@ async def confirm_payment_request(
async def confirm_output(
address: str,
- amount: str | None = None,
+ amount: str,
title: str | None = None,
hold: bool = False,
br_code: ButtonRequestType = ButtonRequestType.ConfirmOutput,
@@ -599,74 +599,53 @@ async def confirm_output(
else:
title = TR.send__title_sending_to
- if amount is not None:
- account_properties: list[StrPropertyType] = []
- if source_account:
- account_properties.append((TR.words__account, source_account, None))
- if source_account_path and source_account_path != source_account:
- # the reason for this check is account_label in bitcoin/sign_tx/layout.py
- # which can return the derivation path instead of the account
- account_properties.append(
- (
- TR.address_details__derivation_path,
- source_account_path,
- None,
- )
+ account_properties: list[StrPropertyType] = []
+ if source_account:
+ account_properties.append((TR.words__account, source_account, None))
+ if source_account_path and source_account_path != source_account:
+ # the reason for this check is account_label in bitcoin/sign_tx/layout.py
+ # which can return the derivation path instead of the account
+ account_properties.append(
+ (
+ TR.address_details__derivation_path,
+ source_account_path,
+ None,
)
- if account_properties:
- info_items = [
- (
- TR.address_details__account_info,
- account_properties,
- TR.send__send_from,
- )
- ]
- else:
- info_items = []
- await confirm_linear_flow(
- lambda: confirm_value(
- TR.words__address,
- address,
- description or "",
- "confirm_output",
- br_code,
- subtitle=title,
- chunkify=chunkify,
- cancel_text=TR.send__cancel_sign,
- info_items=info_items,
- ),
- lambda: confirm_value(
- TR.words__amount,
- amount,
- description="",
- br_name="confirm_output",
- br_code=br_code,
- subtitle=title,
- cancel_text=TR.send__cancel_sign,
- info_items=info_items,
- can_go_back=True,
- ),
)
+ if account_properties:
+ info_items = [
+ (
+ TR.address_details__account_info,
+ account_properties,
+ TR.send__send_from,
+ )
+ ]
else:
- await raise_if_not_confirmed(
- trezorui_api.flow_confirm_output(
- title=TR.words__address,
- subtitle=title,
- message=address,
- extra=None,
- chunkify=chunkify,
- text_mono=True,
- account_title=TR.send__send_from,
- account=source_account,
- account_path=source_account_path,
- address_item=None,
- br_code=br_code,
- br_name="confirm_output",
- cancel_text=cancel_text,
- description=description,
- ),
- br_name=None,
- )
+ info_items = []
+ await confirm_linear_flow(
+ lambda: confirm_value(
+ TR.words__address,
+ address,
+ description or "",
+ "confirm_output",
+ br_code,
+ subtitle=title,
+ chunkify=chunkify,
+ cancel_text=TR.send__cancel_sign,
+ info_items=info_items,
+ ),
+ lambda: confirm_value(
+ TR.words__amount,
+ amount,
+ description="",
+ br_name="confirm_output",
+ br_code=br_code,
+ subtitle=title,
+ cancel_text=TR.send__cancel_sign,
+ info_items=info_items,
+ can_go_back=True,
+ ),
+ )
async def should_show_more(
diff --git a/core/src/trezor/ui/layouts/eckhart/__init__.py b/core/src/trezor/ui/layouts/eckhart/__init__.py
index 9cd12625..95bdeaaf 100644
--- a/core/src/trezor/ui/layouts/eckhart/__init__.py
+++ b/core/src/trezor/ui/layouts/eckhart/__init__.py
@@ -570,7 +570,7 @@ async def confirm_payment_request(
async def confirm_output(
address: str,
- amount: str | None = None,
+ amount: str,
title: str | None = None,
hold: bool = False,
br_code: ButtonRequestType = ButtonRequestType.ConfirmOutput,
@@ -593,93 +593,68 @@ async def confirm_output(
else:
title = TR.send__title_sending_to
- if amount is not None:
- account_properties: list[StrPropertyType] = []
- if source_account:
- account_properties.append((TR.words__wallet, source_account, None))
- if source_account_path and source_account_path != source_account:
- # the reason for this check is account_label in bitcoin/sign_tx/layout.py
- # which can return the derivation path instead of the account
- account_properties.append(
- (
- TR.address_details__derivation_path,
- source_account_path,
- None,
- )
+ account_properties: list[StrPropertyType] = []
+ if source_account:
+ account_properties.append((TR.words__wallet, source_account, None))
+ if source_account_path and source_account_path != source_account:
+ # the reason for this check is account_label in bitcoin/sign_tx/layout.py
+ # which can return the derivation path instead of the account
+ account_properties.append(
+ (
+ TR.address_details__derivation_path,
+ source_account_path,
+ None,
)
- if account_properties:
- menu_items = [
- create_details(
- TR.address_details__account_info,
- account_properties,
- title=TR.address_details__account_info,
- subtitle=TR.send__send_from,
- )
- ]
- else:
- menu_items = []
-
- menu = Menu.root(
- menu_items,
- cancel=Cancel.from_layout(
- name=TR.buttons__cancel,
- layout_factory=trezorui_api.confirm_cancel,
- ),
)
+ if account_properties:
+ menu_items = [
+ create_details(
+ TR.address_details__account_info,
+ account_properties,
+ title=TR.address_details__account_info,
+ subtitle=TR.send__send_from,
+ )
+ ]
+ else:
+ menu_items = []
- address_layout = trezorui_api.confirm_value(
- title=TR.words__send,
- value=address,
- description=description,
- subtitle=title,
- verb=TR.buttons__continue,
- chunkify=chunkify,
- page_counter=True, # TODO: this is for test_cardano_sign_tx_show_details - maybe we can do without?
- external_menu=True,
- )
+ menu = Menu.root(
+ menu_items,
+ cancel=Cancel.from_layout(
+ name=TR.buttons__cancel,
+ layout_factory=trezorui_api.confirm_cancel,
+ ),
+ )
- amount_layout = trezorui_api.confirm_value(
- title=TR.words__send,
- value=amount,
- description=TR.words__amount,
- is_data=False,
- subtitle=title,
- external_menu=True,
- back_button=True,
- )
+ address_layout = trezorui_api.confirm_value(
+ title=TR.words__send,
+ value=address,
+ description=description,
+ subtitle=title,
+ verb=TR.buttons__continue,
+ chunkify=chunkify,
+ page_counter=True, # TODO: this is for test_cardano_sign_tx_show_details - maybe we can do without?
+ external_menu=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
- else:
- await raise_if_not_confirmed(
- trezorui_api.flow_confirm_output(
- title=TR.words__send,
- subtitle=title,
- message=address,
- extra=None,
- chunkify=chunkify,
- text_mono=True,
- account_title=TR.send__send_from,
- account=source_account,
- account_path=source_account_path,
- address_item=None,
- br_code=br_code,
- br_name="confirm_output",
- cancel_text=cancel_text,
- description=description,
- ),
- br_name=None,
+ amount_layout = trezorui_api.confirm_value(
+ title=TR.words__send,
+ value=amount,
+ description=TR.words__amount,
+ is_data=False,
+ subtitle=title,
+ external_menu=True,
+ 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
async def should_show_more(
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.