diff --git a/core/embed/rust/librust_qstr.h b/core/embed/rust/librust_qstr.h index 28fb4fc5f5..c3d864af95 100644 --- a/core/embed/rust/librust_qstr.h +++ b/core/embed/rust/librust_qstr.h @@ -63,9 +63,9 @@ 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_address_title; MP_QSTR_allow_cancel; MP_QSTR_amount; MP_QSTR_amount_change; @@ -232,6 +232,7 @@ static void _librust_qstrs(void) { MP_QSTR_experimental_mode__only_for_dev; MP_QSTR_experimental_mode__title; MP_QSTR_extra; + MP_QSTR_extra_item; MP_QSTR_extra_items; MP_QSTR_extra_title; MP_QSTR_fee; diff --git a/core/embed/rust/src/ui/api/firmware_micropython.rs b/core/embed/rust/src/ui/api/firmware_micropython.rs index 0f1ddbd16f..7eafb6603c 100644 --- a/core/embed/rust/src/ui/api/firmware_micropython.rs +++ b/core/embed/rust/src/ui/api/firmware_micropython.rs @@ -435,9 +435,22 @@ extern "C" fn new_flow_confirm_output(n_args: usize, args: *const Obj, kwargs: * 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: Option = kwargs.get(Qstr::MP_QSTR_address)?.try_into_option()?; - let address_title: Option = - kwargs.get(Qstr::MP_QSTR_address_title)?.try_into_option()?; + let address_item = kwargs + .get(Qstr::MP_QSTR_address_item)? + .try_into_option()? + .map(|item| -> Result<(TString, Obj), crate::error::Error> { + let pair: [Obj; 2] = util::iter_into_array(item)?; + Ok((pair[0].try_into()?, pair[1])) + }) + .transpose()?; + let extra_item = kwargs + .get(Qstr::MP_QSTR_extra_item)? + .try_into_option()? + .map(|item| -> Result<(TString, Obj), crate::error::Error> { + let pair: [Obj; 2] = util::iter_into_array(item)?; + Ok((pair[0].try_into()?, pair[1])) + }) + .transpose()?; let summary_items: Option = kwargs.get(Qstr::MP_QSTR_summary_items)?.try_into_option()?; let fee_items: Option = kwargs.get(Qstr::MP_QSTR_fee_items)?.try_into_option()?; @@ -464,8 +477,8 @@ extern "C" fn new_flow_confirm_output(n_args: usize, args: *const Obj, kwargs: * account_path, br_code, br_name, - address, - address_title, + address_item, + extra_item, summary_items, fee_items, summary_title, @@ -1333,8 +1346,8 @@ pub static mp_module_trezorui_api: Module = obj_module! { /// account_path: str | None, /// br_code: ButtonRequestType, /// br_name: str, - /// address: str | None, - /// address_title: str | None, + /// address_item: (str, str) | None, + /// extra_item: (str, str) | None, /// summary_items: Iterable[tuple[str, str]] | None = None, /// fee_items: Iterable[tuple[str, str]] | None = None, /// summary_title: str | None = None, diff --git a/core/embed/rust/src/ui/flow/swipe.rs b/core/embed/rust/src/ui/flow/swipe.rs index a4039072b4..f61972d951 100644 --- a/core/embed/rust/src/ui/flow/swipe.rs +++ b/core/embed/rust/src/ui/flow/swipe.rs @@ -98,7 +98,7 @@ pub struct SwipeFlow { /// Current state of the flow. state: FlowState, /// Store of all screens which are part of the flow. - store: Vec, 12>, + store: Vec, 16>, /// Swipe detector. swipe: SwipeDetect, /// Swipe allowed 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 f292d6cb2e..14fca09732 100644 --- a/core/embed/rust/src/ui/layout_bolt/ui_firmware.rs +++ b/core/embed/rust/src/ui/layout_bolt/ui_firmware.rs @@ -548,8 +548,8 @@ impl FirmwareUI for UIBolt { _account_path: Option>, _br_code: u16, _br_name: TString<'static>, - _address: Option, - _address_title: Option>, + _address_item: Option<(TString<'static>, Obj)>, + _extra_item: Option<(TString<'static>, Obj)>, _summary_items: Option, _fee_items: Option, _summary_title: Option>, 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 f9d1173416..ac87edb777 100644 --- a/core/embed/rust/src/ui/layout_caesar/ui_firmware.rs +++ b/core/embed/rust/src/ui/layout_caesar/ui_firmware.rs @@ -687,8 +687,8 @@ impl FirmwareUI for UICaesar { _account_path: Option>, _br_code: u16, _br_name: TString<'static>, - _address: Option, - _address_title: Option>, + _address_item: Option<(TString<'static>, Obj)>, + _extra_item: Option<(TString<'static>, Obj)>, _summary_items: Option, _fee_items: Option, _summary_title: Option>, 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 index 441bfba81e..96354f7b79 100644 --- a/core/embed/rust/src/ui/layout_delizia/flow/confirm_output.rs +++ b/core/embed/rust/src/ui/layout_delizia/flow/confirm_output.rs @@ -30,6 +30,7 @@ const MENU_ITEM_CANCEL: usize = 0; const MENU_ITEM_FEE_INFO: usize = 1; const MENU_ITEM_ADDRESS_INFO: usize = 2; const MENU_ITEM_ACCOUNT_INFO: usize = 3; +const MENU_ITEM_EXTRA_INFO: usize = 4; #[derive(Copy, Clone, PartialEq, Eq)] pub enum ConfirmOutput { @@ -123,6 +124,7 @@ pub enum ConfirmOutputWithSummary { HoldMenu, HoldMenuCancel, AccountInfo, + ExtraInfo, } impl FlowController for ConfirmOutputWithSummary { @@ -144,6 +146,7 @@ impl FlowController for ConfirmOutputWithSummary { (Self::Summary, Direction::Down) => Self::Main.swipe(direction), (Self::SummaryMenu, Direction::Right) => Self::Summary.swipe(direction), (Self::SummaryMenuCancel, Direction::Right) => Self::SummaryMenu.swipe(direction), + (Self::ExtraInfo, Direction::Right) => Self::SummaryMenu.swipe(direction), (Self::FeeInfo, Direction::Right) => Self::SummaryMenu.swipe(direction), (Self::Hold, Direction::Left) => Self::HoldMenu.swipe(direction), (Self::Hold, Direction::Down) => Self::Summary.swipe(direction), @@ -162,6 +165,7 @@ impl FlowController for ConfirmOutputWithSummary { (Self::AccountInfo, FlowMsg::Cancelled) => Self::MainMenu.goto(), (Self::MainMenuCancel, FlowMsg::Cancelled) => Self::MainMenu.goto(), (Self::AddressInfo, FlowMsg::Info) => Self::MainMenu.goto(), + (Self::ExtraInfo, FlowMsg::Info) => Self::SummaryMenu.goto(), (Self::Summary, FlowMsg::Info) => Self::SummaryMenu.goto(), (Self::SummaryMenu, FlowMsg::Choice(MENU_ITEM_CANCEL)) => { Self::SummaryMenuCancel.swipe_left() @@ -173,6 +177,9 @@ impl FlowController for ConfirmOutputWithSummary { } (Self::HoldMenuCancel, FlowMsg::Cancelled) => Self::HoldMenu.goto(), (Self::SummaryMenu, FlowMsg::Choice(MENU_ITEM_FEE_INFO)) => Self::FeeInfo.swipe_left(), + (Self::SummaryMenu, FlowMsg::Choice(MENU_ITEM_EXTRA_INFO)) => { + Self::ExtraInfo.swipe_left() + } (Self::MainMenu, FlowMsg::Choice(MENU_ITEM_ADDRESS_INFO)) => { Self::AddressInfo.swipe_left() } @@ -221,7 +228,7 @@ pub fn new_confirm_output( br_code: u16, confirm_amount: Option, confirm_address: Option, - address_title: TString<'static>, + confirm_extra: Option, summary_items_params: Option, fee_items_params: ShowInfoParams, summary_br_name: Option>, @@ -236,8 +243,8 @@ pub fn new_confirm_output( // MainMenu let mut main_menu = VerticalMenu::empty(); let mut main_menu_items = Vec::::new(); - if confirm_address.is_some() { - main_menu = main_menu.item(theme::ICON_CHEVRON_RIGHT, address_title); + 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() { @@ -309,7 +316,11 @@ pub fn new_confirm_output( // SummaryMenu let mut summary_menu = VerticalMenu::empty(); - let mut summary_menu_items = Vec::::new(); + let mut summary_menu_items = Vec::::new(); + if let Some(ref confirm_extra) = confirm_extra { + summary_menu = summary_menu.item(theme::ICON_CHEVRON_RIGHT, confirm_extra.title()); + unwrap!(summary_menu_items.push(MENU_ITEM_EXTRA_INFO)); + } if has_fee_info { summary_menu = summary_menu.item( theme::ICON_CHEVRON_RIGHT, @@ -364,7 +375,8 @@ pub fn new_confirm_output( .map(|_| Some(FlowMsg::Cancelled)), )?; } - flow.with_page(&ConfirmOutputWithSummary::Summary, content_summary)? + flow = flow + .with_page(&ConfirmOutputWithSummary::Summary, content_summary)? .with_page(&ConfirmOutputWithSummary::SummaryMenu, content_summary_menu)? .with_page( &ConfirmOutputWithSummary::SummaryMenuCancel, @@ -374,7 +386,12 @@ pub fn new_confirm_output( .with_page(&ConfirmOutputWithSummary::Hold, content_hold)? .with_page(&ConfirmOutputWithSummary::HoldMenu, content_hold_menu)? .with_page(&ConfirmOutputWithSummary::HoldMenuCancel, get_cancel_page())? - .with_page(&ConfirmOutputWithSummary::AccountInfo, account_content)? + .with_page(&ConfirmOutputWithSummary::AccountInfo, account_content)?; + if let Some(confirm_extra) = confirm_extra { + let extra_content = confirm_extra.into_layout()?; + flow = flow.with_page(&ConfirmOutputWithSummary::ExtraInfo, extra_content)? + } + flow } else { SwipeFlow::new(&ConfirmOutput::Address)? .with_page(&ConfirmOutput::Address, main_content)? diff --git a/core/embed/rust/src/ui/layout_delizia/flow/util.rs b/core/embed/rust/src/ui/layout_delizia/flow/util.rs index d35eb99132..a7bbf89c10 100644 --- a/core/embed/rust/src/ui/layout_delizia/flow/util.rs +++ b/core/embed/rust/src/ui/layout_delizia/flow/util.rs @@ -300,6 +300,10 @@ impl ConfirmValue { self.page_counter, ) } + + pub fn title(&self) -> TString<'static> { + self.title + } } pub struct ShowInfoParams { 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 f5739d283e..5a628d92d8 100644 --- a/core/embed/rust/src/ui/layout_delizia/ui_firmware.rs +++ b/core/embed/rust/src/ui/layout_delizia/ui_firmware.rs @@ -519,8 +519,8 @@ impl FirmwareUI for UIDelizia { account_path: Option>, br_code: u16, br_name: TString<'static>, - address: Option, - address_title: Option>, + address_item: Option<(TString<'static>, Obj)>, + extra_item: Option<(TString<'static>, Obj)>, summary_items: Option, fee_items: Option, summary_title: Option>, @@ -528,8 +528,6 @@ impl FirmwareUI for UIDelizia { summary_br_name: Option>, cancel_text: Option>, ) -> Result { - let address_title = address_title.unwrap_or(TR::words__address.into()); - let confirm_main = ConfirmValue::new(title.unwrap_or(TString::empty()), message, description) .with_subtitle(subtitle) @@ -549,7 +547,7 @@ impl FirmwareUI for UIDelizia { .with_swipe_down() }); - let confirm_value = address.map(|address| { + let confirm_address = address_item.map(|(address_title, address)| { ConfirmValue::new(address_title, address, None) .with_cancel_button() .with_chunkify(true) @@ -557,6 +555,14 @@ impl FirmwareUI for UIDelizia { .with_swipe_right() }); + let confirm_extra = extra_item.map(|(extra_title, extra)| { + ConfirmValue::new(extra_title, extra, None) + .with_cancel_button() + .with_chunkify(true) + .with_text_mono(true) + .with_swipe_right() + }); + let mut fee_items_params = ShowInfoParams::new(TR::confirm_total__title_fee.into()).with_cancel_button(); if fee_items.is_some() { @@ -589,8 +595,8 @@ impl FirmwareUI for UIDelizia { br_name, br_code, confirm_amount, - confirm_value, - address_title, + confirm_address, + confirm_extra, summary_items_params, fee_items_params, summary_br_name, diff --git a/core/embed/rust/src/ui/ui_firmware.rs b/core/embed/rust/src/ui/ui_firmware.rs index 32cb2ca2a9..795e201513 100644 --- a/core/embed/rust/src/ui/ui_firmware.rs +++ b/core/embed/rust/src/ui/ui_firmware.rs @@ -169,8 +169,8 @@ pub trait FirmwareUI { account_path: Option>, br_code: u16, br_name: TString<'static>, - address: Option, // TODO: replace Obj - address_title: Option>, + address_item: Option<(TString<'static>, Obj)>, + extra_item: Option<(TString<'static>, Obj)>, summary_items: Option, // TODO: replace Obj fee_items: Option, // TODO: replace Obj summary_title: Option>, diff --git a/core/mocks/generated/trezorui_api.pyi b/core/mocks/generated/trezorui_api.pyi index 7575bde7e4..4728e59a71 100644 --- a/core/mocks/generated/trezorui_api.pyi +++ b/core/mocks/generated/trezorui_api.pyi @@ -319,8 +319,8 @@ def flow_confirm_output( account_path: str | None, br_code: ButtonRequestType, br_name: str, - address: str | None, - address_title: str | None, + address_item: (str, str) | None, + extra_item: (str, str) | None, summary_items: Iterable[tuple[str, str]] | None = None, fee_items: Iterable[tuple[str, str]] | None = None, summary_title: str | None = None, diff --git a/core/src/apps/solana/layout.py b/core/src/apps/solana/layout.py index 8b5040f3bb..ce9455af67 100644 --- a/core/src/apps/solana/layout.py +++ b/core/src/apps/solana/layout.py @@ -49,10 +49,6 @@ def _get_address_reference_props( ) -def _blockhash_item(blockhash: bytes) -> tuple[str, str]: - return (f"{TR.words__blockhash}:", base58.encode(blockhash)) - - async def confirm_instruction( instruction: Instruction, instructions_count: int, @@ -352,7 +348,7 @@ async def confirm_custom_transaction( fee_title=f"{TR.solana__expected_fee}:", items=( (f"{TR.words__account}:", _format_path(signer_path)), - _blockhash_item(blockhash), + (f"{TR.words__blockhash}:", base58.encode(blockhash)), ), ) @@ -426,7 +422,7 @@ async def confirm_stake_transaction( ), fee_item=_fee_summary(fee), fee_details=_fee_details(fee), - blockhash_item=_blockhash_item(blockhash), + blockhash_item=(TR.words__blockhash, base58.encode(blockhash)), br_name="confirm_stake_transaction", ) @@ -452,7 +448,7 @@ async def confirm_unstake_transaction( amount_item=("", ""), fee_item=_fee_summary(fee), fee_details=_fee_details(fee), - blockhash_item=_blockhash_item(blockhash), + blockhash_item=(TR.words__blockhash, base58.encode(blockhash)), br_name="confirm_unstake_transaction", ) @@ -481,7 +477,7 @@ async def confirm_claim_transaction( ), fee_item=_fee_summary(fee), fee_details=_fee_details(fee), - blockhash_item=_blockhash_item(blockhash), + blockhash_item=(TR.words__blockhash, base58.encode(blockhash)), br_name="confirm_claim_transaction", ) @@ -497,6 +493,6 @@ async def confirm_transaction( fee_title=fee_title, items=( (f"{TR.words__account}:", _format_path(signer_path)), - _blockhash_item(blockhash), + (f"{TR.words__blockhash}:", base58.encode(blockhash)), ), ) diff --git a/core/src/trezor/ui/layouts/delizia/__init__.py b/core/src/trezor/ui/layouts/delizia/__init__.py index 1ba80d5f27..1e970b3839 100644 --- a/core/src/trezor/ui/layouts/delizia/__init__.py +++ b/core/src/trezor/ui/layouts/delizia/__init__.py @@ -398,8 +398,8 @@ async def confirm_output( text_mono=True, account=source_account, account_path=source_account_path, - address=None, - address_title=None, + address_item=None, + extra_item=None, br_code=br_code, br_name="confirm_output", summary_items=None, @@ -778,8 +778,8 @@ if not utils.BITCOIN_ONLY: text_mono=True, account=account, account_path=account_path, - address=None, - address_title=None, + address_item=None, + extra_item=None, br_code=ButtonRequestType.SignTx, br_name="confirm_output", summary_items=( @@ -830,8 +830,8 @@ if not utils.BITCOIN_ONLY: account_path=account_path, br_code=br_code, br_name=br_name, - address=address, - address_title=address_title, + address_item=(address_title, address), + extra_item=None, summary_items=summary_items, fee_items=info_items, summary_title=verb, @@ -879,8 +879,6 @@ if not utils.BITCOIN_ONLY: blockhash_item: tuple[str, str], br_name: str, ) -> None: - (address_title, address) = stake_item - summary_items = (amount_item, fee_item) description = message if vote_account: description = f"{message}\n{TR.solana__stake_provider}:" @@ -898,11 +896,11 @@ if not utils.BITCOIN_ONLY: account_path=account_path, br_code=ButtonRequestType.SignTx, br_name=br_name, - address=address, - address_title=address_title, + address_item=stake_item, + extra_item=blockhash_item, fee_items=fee_details, summary_title=title, - summary_items=summary_items, + summary_items=(amount_item, fee_item), summary_br_name="confirm_solana_staking_tx_total", summary_br_code=ButtonRequestType.SignTx, cancel_text=TR.buttons__cancel,