diff --git a/core/embed/rust/librust_qstr.h b/core/embed/rust/librust_qstr.h index 0ed3ec2bbe..b96383613c 100644 --- a/core/embed/rust/librust_qstr.h +++ b/core/embed/rust/librust_qstr.h @@ -82,6 +82,7 @@ static void _librust_qstrs(void) { MP_QSTR_address__title_cosigner; MP_QSTR_address__title_receive_address; MP_QSTR_address__title_yours; + MP_QSTR_address__xpub; MP_QSTR_address_details__account_info; MP_QSTR_address_details__derivation_path; MP_QSTR_address_details__derivation_path_colon; diff --git a/core/embed/rust/src/translations/generated/translated_string.rs b/core/embed/rust/src/translations/generated/translated_string.rs index da59231fef..2df438b65d 100644 --- a/core/embed/rust/src/translations/generated/translated_string.rs +++ b/core/embed/rust/src/translations/generated/translated_string.rs @@ -1445,6 +1445,7 @@ pub enum TranslatedString { recovery__share_from_group_entered_template = 1044, // "Share #{0} from Group #{1} entered." send__cancel_transaction = 1045, // "Cancel transaction" send__multisig_different_paths = 1046, // "Using different paths for different XPUBs." + address__xpub = 1047, // {"Bolt": "XPUB", "Caesar": "XPUB", "Delizia": "XPUB", "Eckhart": "Public key (XPUB)"} } impl TranslatedString { @@ -3195,6 +3196,14 @@ impl TranslatedString { (Self::recovery__share_from_group_entered_template, "Share #{0} from Group #{1} entered."), (Self::send__cancel_transaction, "Cancel transaction"), (Self::send__multisig_different_paths, "Using different paths for different XPUBs."), + #[cfg(feature = "layout_bolt")] + (Self::address__xpub, "XPUB"), + #[cfg(feature = "layout_caesar")] + (Self::address__xpub, "XPUB"), + #[cfg(feature = "layout_delizia")] + (Self::address__xpub, "XPUB"), + #[cfg(feature = "layout_eckhart")] + (Self::address__xpub, "Public key (XPUB)"), ]; #[cfg(feature = "micropython")] @@ -3215,6 +3224,7 @@ impl TranslatedString { (Qstr::MP_QSTR_address__title_cosigner, Self::address__title_cosigner), (Qstr::MP_QSTR_address__title_receive_address, Self::address__title_receive_address), (Qstr::MP_QSTR_address__title_yours, Self::address__title_yours), + (Qstr::MP_QSTR_address__xpub, Self::address__xpub), (Qstr::MP_QSTR_address_details__account_info, Self::address_details__account_info), (Qstr::MP_QSTR_address_details__derivation_path, Self::address_details__derivation_path), (Qstr::MP_QSTR_address_details__derivation_path_colon, Self::address_details__derivation_path_colon), diff --git a/core/embed/rust/src/ui/api/firmware_micropython.rs b/core/embed/rust/src/ui/api/firmware_micropython.rs index 01864d88a6..b3de968357 100644 --- a/core/embed/rust/src/ui/api/firmware_micropython.rs +++ b/core/embed/rust/src/ui/api/firmware_micropython.rs @@ -543,6 +543,7 @@ extern "C" fn new_flow_confirm_set_new_pin( extern "C" fn new_flow_get_address(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj { let block = move |_args: &[Obj], kwargs: &Map| { let title: TString = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?; + let subtitle: Option = kwargs.get(Qstr::MP_QSTR_subtitle)?.try_into_option()?; let description: Option = kwargs.get(Qstr::MP_QSTR_description)?.try_into_option()?; let extra: Option = kwargs.get(Qstr::MP_QSTR_extra)?.try_into_option()?; @@ -559,6 +560,7 @@ extern "C" fn new_flow_get_address(n_args: usize, args: *const Obj, kwargs: *mut let layout = ModelUI::flow_get_address( address, title, + subtitle, description, extra, chunkify, @@ -1515,6 +1517,7 @@ pub static mp_module_trezorui_api: Module = obj_module! { /// *, /// address: str | bytes, /// title: str, + /// subtitle: str | None, /// description: str | None, /// extra: str | None, /// chunkify: bool, diff --git a/core/embed/rust/src/ui/layout/util.rs b/core/embed/rust/src/ui/layout/util.rs index 91cda79e77..88d0b6e9a0 100644 --- a/core/embed/rust/src/ui/layout/util.rs +++ b/core/embed/rust/src/ui/layout/util.rs @@ -212,3 +212,7 @@ pub fn get_user_custom_image() -> Result, Error> { load_avatar(unsafe { Gc::<[u8]>::as_mut(&mut data) })?; Ok(data.into()) } + +/// Maximum number of extended public keys (xpubs) that can be displayed in +/// a layout or flow. +pub const MAX_XPUBS: usize = 16; diff --git a/core/embed/rust/src/ui/layout_bolt/component/address_details.rs b/core/embed/rust/src/ui/layout_bolt/component/address_details.rs index bb987408bb..6a8569903b 100644 --- a/core/embed/rust/src/ui/layout_bolt/component/address_details.rs +++ b/core/embed/rust/src/ui/layout_bolt/component/address_details.rs @@ -11,14 +11,13 @@ use crate::{ Component, Event, EventCtx, Paginate, Qr, }, geometry::Rect, + layout::util::MAX_XPUBS, shape::Renderer, }, }; use super::{theme, Frame, FrameMsg}; -const MAX_XPUBS: usize = 16; - pub struct AddressDetails { qr_code: Frame, details: Frame>>, 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 d0acd51d5c..1bea669328 100644 --- a/core/embed/rust/src/ui/layout_bolt/ui_firmware.rs +++ b/core/embed/rust/src/ui/layout_bolt/ui_firmware.rs @@ -591,6 +591,7 @@ impl FirmwareUI for UIBolt { fn flow_get_address( _address: Obj, _title: TString<'static>, + _subtitle: Option>, _description: Option>, _extra: Option>, _chunkify: bool, diff --git a/core/embed/rust/src/ui/layout_caesar/component/address_details.rs b/core/embed/rust/src/ui/layout_caesar/component/address_details.rs index 5252fa8996..96282c7f1f 100644 --- a/core/embed/rust/src/ui/layout_caesar/component/address_details.rs +++ b/core/embed/rust/src/ui/layout_caesar/component/address_details.rs @@ -11,6 +11,7 @@ use crate::{ Child, Component, Event, EventCtx, Pad, Paginate, Qr, }, geometry::Rect, + layout::util::MAX_XPUBS, shape::Renderer, }, }; @@ -19,7 +20,6 @@ use super::{ theme, ButtonController, ButtonControllerMsg, ButtonDetails, ButtonLayout, ButtonPos, Frame, }; -const MAX_XPUBS: usize = 16; const QR_BORDER: i16 = 3; pub struct AddressDetails { 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 d33a1f7e7b..e6ba5bcc04 100644 --- a/core/embed/rust/src/ui/layout_caesar/ui_firmware.rs +++ b/core/embed/rust/src/ui/layout_caesar/ui_firmware.rs @@ -727,6 +727,7 @@ impl FirmwareUI for UICaesar { fn flow_get_address( _address: Obj, _title: TString<'static>, + _subtitle: Option>, _description: Option>, _extra: Option>, _chunkify: bool, diff --git a/core/embed/rust/src/ui/layout_delizia/component/address_details.rs b/core/embed/rust/src/ui/layout_delizia/component/address_details.rs index 258b618f8f..1d35fde67c 100644 --- a/core/embed/rust/src/ui/layout_delizia/component/address_details.rs +++ b/core/embed/rust/src/ui/layout_delizia/component/address_details.rs @@ -14,6 +14,7 @@ use crate::{ event::SwipeEvent, flow::Swipable, geometry::{Direction, Rect}, + layout::util::MAX_XPUBS, shape::Renderer, util::Pager, }, @@ -21,8 +22,6 @@ use crate::{ use super::{theme, Frame, FrameMsg}; -const MAX_XPUBS: usize = 16; - pub struct AddressDetails { details: Frame>>, xpub_view: Frame>>, diff --git a/core/embed/rust/src/ui/layout_delizia/flow/get_address.rs b/core/embed/rust/src/ui/layout_delizia/flow/get_address.rs index 95a9646e61..5af98f99ab 100644 --- a/core/embed/rust/src/ui/layout_delizia/flow/get_address.rs +++ b/core/embed/rust/src/ui/layout_delizia/flow/get_address.rs @@ -15,7 +15,7 @@ use crate::{ FlowController, FlowMsg, SwipeFlow, SwipePage, }, geometry::Direction, - layout::util::ConfirmValueParams, + layout::util::{ConfirmValueParams, MAX_XPUBS}, }, }; 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 c060f7a02d..6b9a90e45e 100644 --- a/core/embed/rust/src/ui/layout_delizia/ui_firmware.rs +++ b/core/embed/rust/src/ui/layout_delizia/ui_firmware.rs @@ -621,6 +621,7 @@ impl FirmwareUI for UIDelizia { fn flow_get_address( address: Obj, title: TString<'static>, + _subtitle: Option>, description: Option>, extra: Option>, chunkify: bool, diff --git a/core/embed/rust/src/ui/layout_eckhart/flow/get_address.rs b/core/embed/rust/src/ui/layout_eckhart/flow/get_address.rs index 05f5be0f8b..4ca36ddaa7 100644 --- a/core/embed/rust/src/ui/layout_eckhart/flow/get_address.rs +++ b/core/embed/rust/src/ui/layout_eckhart/flow/get_address.rs @@ -6,7 +6,9 @@ use crate::{ ui::{ button_request::ButtonRequest, component::{ - text::paragraphs::{Paragraph, ParagraphSource, ParagraphVecLong, VecExt}, + text::paragraphs::{ + Paragraph, ParagraphSource, ParagraphVecLong, ParagraphVecShort, VecExt, + }, ButtonRequestExt, ComponentExt, Qr, }, flow::{ @@ -14,6 +16,7 @@ use crate::{ FlowController, FlowMsg, SwipeFlow, }, geometry::{Direction, LinearPlacement}, + layout::util::MAX_XPUBS, }, }; use heapless::Vec; @@ -29,7 +32,6 @@ use super::super::{ const ITEM_PADDING: i16 = 16; const GROUP_PADDING: i16 = 20; -const MAX_XPUBS: usize = 3; #[derive(Copy, Clone, PartialEq, Eq)] pub enum GetAddress { @@ -70,8 +72,9 @@ impl FlowController for GetAddress { #[allow(clippy::too_many_arguments)] pub fn new_get_address( title: TString<'static>, - _description: Option>, - _extra: Option>, + subtitle: Option>, + description: Option>, + extra: Option>, address: Obj, // TODO: get rid of Obj chunkify: bool, address_qr: TString<'static>, @@ -82,29 +85,42 @@ pub fn new_get_address( br_code: u16, br_name: TString<'static>, ) -> Result { - // Address - let flow_title: TString = TR::words__receive.into(); - - let test_style = if chunkify { + let text_style = if chunkify { let address: TString = address.try_into()?; theme::get_chunkified_text_style(address.len()) } else { &theme::TEXT_MONO_ADDRESS }; - let paragraphs = Paragraph::new(test_style, address.try_into().unwrap_or(TString::empty())) - .into_paragraphs() - .with_placement(LinearPlacement::vertical()); - let content_address = TextScreen::new(paragraphs) - .with_header(Header::new(flow_title).with_menu_button()) - .with_subtitle(title) - .with_action_bar(ActionBar::new_single( - Button::with_text(TR::buttons__confirm.into()).styled(theme::button_confirm()), - )) - .with_hint(Hint::new_instruction( - TR::address__check_with_source, - Some(theme::ICON_INFO), - )) + let mut paragraphs = ParagraphVecShort::new(); + if let Some(description) = description { + paragraphs.add( + Paragraph::new(&theme::TEXT_SMALL_LIGHT, description).with_bottom_padding(ITEM_PADDING), + ); + } + paragraphs.add(Paragraph::new( + text_style, + address.try_into().unwrap_or(TString::empty()), + )); + + let button = if extra.is_some() { + Button::with_text(TR::buttons__confirm.into()).styled(theme::button_cancel_gradient()) + } else { + Button::with_text(TR::buttons__confirm.into()).styled(theme::button_confirm()) + }; + + let mut address_screen = TextScreen::new( + paragraphs + .into_paragraphs() + .with_placement(LinearPlacement::vertical()), + ) + .with_header(Header::new(title).with_menu_button()) + .with_subtitle(subtitle.unwrap_or(TString::empty())) + .with_action_bar(ActionBar::new_single(button)); + if let Some(extra) = extra { + address_screen = address_screen.with_hint(Hint::new_warning_caution(extra)); + } + let content_address = address_screen .map(|msg| match msg { TextScreenMsg::Cancelled => Some(FlowMsg::Cancelled), TextScreenMsg::Confirmed => Some(FlowMsg::Confirmed), @@ -129,7 +145,7 @@ pub fn new_get_address( )), ) .with_header( - Header::new(flow_title) + Header::new(title) .with_right_button(Button::with_icon(theme::ICON_CROSS), HeaderMsg::Cancelled), ) .map(|msg| match msg { @@ -200,7 +216,7 @@ pub fn new_get_address( .into_paragraphs() .with_placement(LinearPlacement::vertical()), ) - .with_header(Header::new(flow_title)) + .with_header(Header::new(title)) .with_action_bar(ActionBar::new_double( Button::with_icon(theme::ICON_CHEVRON_LEFT), Button::with_text(TR::buttons__cancel.into()).styled(theme::button_cancel()), 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 41022b22d4..53b6865376 100644 --- a/core/embed/rust/src/ui/layout_eckhart/ui_firmware.rs +++ b/core/embed/rust/src/ui/layout_eckhart/ui_firmware.rs @@ -731,6 +731,7 @@ impl FirmwareUI for UIEckhart { fn flow_get_address( address: Obj, title: TString<'static>, + subtitle: Option>, description: Option>, extra: Option>, chunkify: bool, @@ -744,6 +745,7 @@ impl FirmwareUI for UIEckhart { ) -> Result { let flow = flow::get_address::new_get_address( title, + subtitle, description, extra, address, diff --git a/core/embed/rust/src/ui/ui_firmware.rs b/core/embed/rust/src/ui/ui_firmware.rs index 8a8de3b4cc..4903b8d6e9 100644 --- a/core/embed/rust/src/ui/ui_firmware.rs +++ b/core/embed/rust/src/ui/ui_firmware.rs @@ -197,6 +197,7 @@ pub trait FirmwareUI { fn flow_get_address( address: Obj, // TODO: replace Obj title: TString<'static>, + subtitle: Option>, description: Option>, extra: Option>, chunkify: bool, diff --git a/core/mocks/generated/trezorui_api.pyi b/core/mocks/generated/trezorui_api.pyi index ce47a69dd9..4ab6dabc82 100644 --- a/core/mocks/generated/trezorui_api.pyi +++ b/core/mocks/generated/trezorui_api.pyi @@ -359,6 +359,7 @@ def flow_get_address( *, address: str | bytes, title: str, + subtitle: str | None, description: str | None, extra: str | None, chunkify: bool, diff --git a/core/mocks/trezortranslate_keys.pyi b/core/mocks/trezortranslate_keys.pyi index 9035bd4e35..f20ba59788 100644 --- a/core/mocks/trezortranslate_keys.pyi +++ b/core/mocks/trezortranslate_keys.pyi @@ -18,6 +18,7 @@ class TR: address__title_cosigner: str = "Cosigner" address__title_receive_address: str = "Receive address" address__title_yours: str = "Yours" + address__xpub: str = "XPUB" address_details__account_info: str = "Account info" address_details__derivation_path: str = "Derivation path" address_details__derivation_path_colon: str = "Derivation path:" diff --git a/core/src/apps/bitcoin/get_address.py b/core/src/apps/bitcoin/get_address.py index e3a053cf80..cd5fadf9c7 100644 --- a/core/src/apps/bitcoin/get_address.py +++ b/core/src/apps/bitcoin/get_address.py @@ -36,6 +36,7 @@ def _get_xpubs( @with_keychain async def get_address(msg: GetAddress, keychain: Keychain, coin: CoinInfo) -> Address: + from trezor import TR from trezor.enums import InputScriptType from trezor.messages import Address from trezor.ui.layouts import ( @@ -105,6 +106,7 @@ async def get_address(msg: GetAddress, keychain: Keychain, coin: CoinInfo) -> Ad if msg.show_display: path = address_n_to_str(address_n) + subtitle = TR.address__coin_address_template.format(coin.coin_shortcut) if multisig: if multisig.nodes: pubnodes = multisig.nodes @@ -138,6 +140,7 @@ async def get_address(msg: GetAddress, keychain: Keychain, coin: CoinInfo) -> Ad await show_address( address_short, + subtitle=subtitle, case_sensitive=address_case_sensitive, path=path, multisig_index=multisig_index, @@ -149,6 +152,7 @@ async def get_address(msg: GetAddress, keychain: Keychain, coin: CoinInfo) -> Ad account = address_n_to_name_or_unknown(coin, address_n, script_type) await show_address( address_short, + subtitle=subtitle, address_qr=address, case_sensitive=address_case_sensitive, path=path, diff --git a/core/src/apps/bitcoin/get_public_key.py b/core/src/apps/bitcoin/get_public_key.py index 272aac54db..b750d8d688 100644 --- a/core/src/apps/bitcoin/get_public_key.py +++ b/core/src/apps/bitcoin/get_public_key.py @@ -110,10 +110,13 @@ async def get_public_key( show_xpub = descriptor await show_pubkey( show_xpub, - "XPUB", + TR.address__xpub, account=account, path=path, mismatch_title=TR.addr_mismatch__xpub_mismatch, + warning=( + TR.addr_mismatch__wrong_derivation_path if account is None else None + ), br_name="show_xpub", ) diff --git a/core/src/apps/cardano/layout.py b/core/src/apps/cardano/layout.py index 690f413082..45942c46eb 100644 --- a/core/src/apps/cardano/layout.py +++ b/core/src/apps/cardano/layout.py @@ -1111,6 +1111,7 @@ async def show_cardano_address( await layouts.show_address( address, + subtitle=TR.address__coin_address_template.format("ADA"), path=path, account=account, network=network_name, diff --git a/core/src/apps/ethereum/get_address.py b/core/src/apps/ethereum/get_address.py index 92ab88bd08..6709889b98 100644 --- a/core/src/apps/ethereum/get_address.py +++ b/core/src/apps/ethereum/get_address.py @@ -16,6 +16,7 @@ async def get_address( keychain: Keychain, defs: Definitions, ) -> EthereumAddress: + from trezor import TR from trezor.messages import EthereumAddress from trezor.ui.layouts import show_address @@ -33,11 +34,13 @@ async def get_address( if msg.show_display: slip44_id = address_n[1] # it depends on the network (ETH vs ETC...) + coin = "ETH" await show_address( address, + subtitle=TR.address__coin_address_template.format(coin), path=paths.address_n_to_str(address_n), account=paths.get_account_name( - "ETH", address_n, PATTERNS_ADDRESS, slip44_id + coin, address_n, PATTERNS_ADDRESS, slip44_id ), chunkify=bool(msg.chunkify), ) diff --git a/core/src/apps/monero/get_address.py b/core/src/apps/monero/get_address.py index 3653f0af11..99b3e83b1b 100644 --- a/core/src/apps/monero/get_address.py +++ b/core/src/apps/monero/get_address.py @@ -10,7 +10,7 @@ if TYPE_CHECKING: @auto_keychain(__name__) async def get_address(msg: MoneroGetAddress, keychain: Keychain) -> MoneroAddress: - from trezor import wire + from trezor import TR, wire from trezor.messages import MoneroAddress from trezor.ui.layouts import show_address @@ -68,11 +68,13 @@ async def get_address(msg: MoneroGetAddress, keychain: Keychain) -> MoneroAddres if msg.show_display: from . import PATTERN, SLIP44_ID + coin = "XMR" await show_address( addr, + subtitle=TR.address__coin_address_template.format(coin), address_qr="monero:" + addr, path=paths.address_n_to_str(address_n), - account=paths.get_account_name("XMR", msg.address_n, PATTERN, SLIP44_ID), + account=paths.get_account_name(coin, msg.address_n, PATTERN, SLIP44_ID), chunkify=bool(msg.chunkify), ) diff --git a/core/src/apps/nem/get_address.py b/core/src/apps/nem/get_address.py index c09299ae73..72d4e4b55a 100644 --- a/core/src/apps/nem/get_address.py +++ b/core/src/apps/nem/get_address.py @@ -12,6 +12,7 @@ if TYPE_CHECKING: @with_slip44_keychain(*PATTERNS, slip44_id=SLIP44_ID, curve=CURVE) async def get_address(msg: NEMGetAddress, keychain: Keychain) -> NEMAddress: + from trezor import TR from trezor.messages import NEMAddress from trezor.ui.layouts import show_address @@ -32,11 +33,13 @@ async def get_address(msg: NEMGetAddress, keychain: Keychain) -> NEMAddress: if msg.show_display: from . import PATTERNS, SLIP44_ID + coin = "NEM" await show_address( address, + subtitle=TR.address__coin_address_template.format(coin), case_sensitive=False, path=paths.address_n_to_str(address_n), - account=paths.get_account_name("NEM", msg.address_n, PATTERNS, SLIP44_ID), + account=paths.get_account_name(coin, msg.address_n, PATTERNS, SLIP44_ID), network=get_network_str(network), chunkify=bool(msg.chunkify), ) diff --git a/core/src/apps/ripple/get_address.py b/core/src/apps/ripple/get_address.py index 1450669817..c10044056a 100644 --- a/core/src/apps/ripple/get_address.py +++ b/core/src/apps/ripple/get_address.py @@ -11,6 +11,7 @@ if TYPE_CHECKING: @auto_keychain(__name__) async def get_address(msg: RippleGetAddress, keychain: Keychain) -> RippleAddress: # NOTE: local imports here saves 20 bytes + from trezor import TR from trezor.messages import RippleAddress from trezor.ui.layouts import show_address @@ -29,10 +30,12 @@ async def get_address(msg: RippleGetAddress, keychain: Keychain) -> RippleAddres if msg.show_display: from . import PATTERN, SLIP44_ID + coin = "XRP" await show_address( address, + subtitle=TR.address__coin_address_template.format(coin), path=paths.address_n_to_str(address_n), - account=paths.get_account_name("XRP", msg.address_n, PATTERN, SLIP44_ID), + account=paths.get_account_name(coin, msg.address_n, PATTERN, SLIP44_ID), chunkify=bool(msg.chunkify), ) diff --git a/core/src/apps/solana/get_address.py b/core/src/apps/solana/get_address.py index b543b8245d..eb45b4cbac 100644 --- a/core/src/apps/solana/get_address.py +++ b/core/src/apps/solana/get_address.py @@ -17,6 +17,7 @@ async def get_address( msg: SolanaGetAddress, keychain: Keychain, ) -> SolanaAddress: + from trezor import TR from trezor.messages import SolanaAddress from trezor.ui.layouts import show_address @@ -30,6 +31,7 @@ async def get_address( if msg.show_display: await show_address( address, + subtitle=TR.address__coin_address_template.format("SOL"), path=paths.address_n_to_str(msg.address_n), chunkify=bool(msg.chunkify), ) diff --git a/core/src/apps/stellar/get_address.py b/core/src/apps/stellar/get_address.py index 65b323bbf8..f58bb38b7b 100644 --- a/core/src/apps/stellar/get_address.py +++ b/core/src/apps/stellar/get_address.py @@ -10,6 +10,7 @@ if TYPE_CHECKING: @auto_keychain(__name__) async def get_address(msg: StellarGetAddress, keychain: Keychain) -> StellarAddress: + from trezor import TR from trezor.messages import StellarAddress from trezor.ui.layouts import show_address @@ -28,11 +29,13 @@ async def get_address(msg: StellarGetAddress, keychain: Keychain) -> StellarAddr if msg.show_display: from . import PATTERN, SLIP44_ID + coin = "XLM" await show_address( address, + subtitle=TR.address__coin_address_template.format(coin), case_sensitive=False, path=paths.address_n_to_str(address_n), - account=paths.get_account_name("XLM", msg.address_n, PATTERN, SLIP44_ID), + account=paths.get_account_name(coin, msg.address_n, PATTERN, SLIP44_ID), chunkify=bool(msg.chunkify), ) diff --git a/core/src/apps/tezos/get_address.py b/core/src/apps/tezos/get_address.py index 8269fce154..973695af74 100644 --- a/core/src/apps/tezos/get_address.py +++ b/core/src/apps/tezos/get_address.py @@ -12,6 +12,7 @@ if TYPE_CHECKING: @with_slip44_keychain(*PATTERNS, slip44_id=SLIP44_ID, curve=CURVE) async def get_address(msg: TezosGetAddress, keychain: Keychain) -> TezosAddress: + from trezor import TR from trezor.crypto import hashlib from trezor.messages import TezosAddress from trezor.ui.layouts import show_address @@ -33,10 +34,12 @@ async def get_address(msg: TezosGetAddress, keychain: Keychain) -> TezosAddress: if msg.show_display: from . import PATTERNS, SLIP44_ID + coin = "XTZ" await show_address( address, + subtitle=TR.address__coin_address_template.format(coin), path=paths.address_n_to_str(address_n), - account=paths.get_account_name("XTZ", address_n, PATTERNS, SLIP44_ID), + account=paths.get_account_name(coin, address_n, PATTERNS, SLIP44_ID), chunkify=bool(msg.chunkify), ) diff --git a/core/src/trezor/ui/layouts/bolt/__init__.py b/core/src/trezor/ui/layouts/bolt/__init__.py index d9b1b3d3c7..262aff05bf 100644 --- a/core/src/trezor/ui/layouts/bolt/__init__.py +++ b/core/src/trezor/ui/layouts/bolt/__init__.py @@ -279,6 +279,7 @@ async def show_address( address: str, *, title: str | None = None, + subtitle: str | None = None, address_qr: str | None = None, case_sensitive: bool = True, path: str | None = None, @@ -288,6 +289,7 @@ async def show_address( xpubs: Sequence[str] = (), mismatch_title: str | None = None, details_title: str | None = None, + warning: str | None = None, br_name: str = "show_address", br_code: ButtonRequestType = ButtonRequestType.Address, chunkify: bool = False, @@ -369,6 +371,7 @@ def show_pubkey( account: str | None = None, path: str | None = None, mismatch_title: str | None = None, + warning: str | None = None, br_name: str = "show_pubkey", ) -> Awaitable[None]: title = title or TR.address__public_key # def_arg diff --git a/core/src/trezor/ui/layouts/caesar/__init__.py b/core/src/trezor/ui/layouts/caesar/__init__.py index dc1b8d053f..7ea9cf0b3c 100644 --- a/core/src/trezor/ui/layouts/caesar/__init__.py +++ b/core/src/trezor/ui/layouts/caesar/__init__.py @@ -296,6 +296,7 @@ async def show_address( address: str, *, title: str | None = None, + subtitle: str | None = None, address_qr: str | None = None, case_sensitive: bool = True, path: str | None = None, @@ -304,6 +305,7 @@ async def show_address( multisig_index: int | None = None, xpubs: Sequence[str] = (), mismatch_title: str | None = None, + warning: str | None = None, br_name: str = "show_address", br_code: ButtonRequestType = ButtonRequestType.Address, chunkify: bool = False, @@ -384,6 +386,7 @@ def show_pubkey( account: str | None = None, path: str | None = None, mismatch_title: str | None = None, + warning: str | None = None, br_name: str = "show_pubkey", ) -> Awaitable[None]: title = title or TR.address__public_key # def_arg diff --git a/core/src/trezor/ui/layouts/delizia/__init__.py b/core/src/trezor/ui/layouts/delizia/__init__.py index 41b7324920..8c85b6a93f 100644 --- a/core/src/trezor/ui/layouts/delizia/__init__.py +++ b/core/src/trezor/ui/layouts/delizia/__init__.py @@ -276,6 +276,7 @@ async def show_address( address: str, *, title: str | None = None, + subtitle: str | None = None, address_qr: str | None = None, case_sensitive: bool = True, path: str | None = None, @@ -285,6 +286,7 @@ async def show_address( xpubs: Sequence[str] = (), mismatch_title: str | None = None, details_title: str | None = None, + warning: str | None = None, br_name: str = "show_address", br_code: ButtonRequestType = ButtonRequestType.Address, chunkify: bool = False, @@ -302,6 +304,7 @@ async def show_address( trezorui_api.flow_get_address( address=address, title=title or TR.address__title_receive_address, + subtitle=None, description=network or "", extra=None, chunkify=chunkify, @@ -330,6 +333,7 @@ def show_pubkey( account: str | None = None, path: str | None = None, mismatch_title: str | None = None, + warning: str | None = None, br_name: str = "show_pubkey", ) -> Awaitable[None]: title = title or TR.address__public_key # def_arg diff --git a/core/src/trezor/ui/layouts/eckhart/__init__.py b/core/src/trezor/ui/layouts/eckhart/__init__.py index cd25c560c0..bc99385bff 100644 --- a/core/src/trezor/ui/layouts/eckhart/__init__.py +++ b/core/src/trezor/ui/layouts/eckhart/__init__.py @@ -274,6 +274,7 @@ async def show_address( address: str, *, title: str | None = None, + subtitle: str | None = None, address_qr: str | None = None, case_sensitive: bool = True, path: str | None = None, @@ -283,6 +284,7 @@ async def show_address( xpubs: Sequence[str] = (), mismatch_title: str | None = None, details_title: str | None = None, + warning: str | None = None, br_name: str = "show_address", br_code: ButtonRequestType = ButtonRequestType.Address, chunkify: bool = False, @@ -296,12 +298,15 @@ async def show_address( ) return result + if warning is None and multisig_index is not None: + warning = TR.send__receiving_to_multisig await raise_if_not_confirmed( trezorui_api.flow_get_address( address=address, - title=title or TR.address__title_receive_address, + title=title or TR.words__receive, + subtitle=subtitle, description=network or "", - extra=None, + extra=warning, chunkify=chunkify, address_qr=address if address_qr is None else address_qr, case_sensitive=case_sensitive, @@ -328,18 +333,20 @@ def show_pubkey( account: str | None = None, path: str | None = None, mismatch_title: str | None = None, + warning: str | None = None, br_name: str = "show_pubkey", ) -> Awaitable[None]: - title = title or TR.address__public_key # def_arg mismatch_title = mismatch_title or TR.addr_mismatch__key_mismatch # def_arg return show_address( address=pubkey, - title=title, + title=title or TR.address__public_key, + subtitle=account, account=account, path=path, br_name=br_name, br_code=ButtonRequestType.PublicKey, mismatch_title=mismatch_title, + warning=warning, chunkify=False, ) diff --git a/core/translations/en.json b/core/translations/en.json index f91023a2b2..0c83e48263 100644 --- a/core/translations/en.json +++ b/core/translations/en.json @@ -20,6 +20,12 @@ "address__check_with_source": "Check the address with source.", "address__confirmed": "Receive address confirmed", "address__public_key": "Public key", + "address__xpub": { + "Bolt": "XPUB", + "Caesar": "XPUB", + "Delizia": "XPUB", + "Eckhart": "Public key (XPUB)" + }, "address__public_key_confirmed": "Public key confirmed", "address__qr_code": "QR code", "address__title_cosigner": "Cosigner", diff --git a/core/translations/order.json b/core/translations/order.json index 3497bbe7fa..ad8bb28a33 100644 --- a/core/translations/order.json +++ b/core/translations/order.json @@ -1045,5 +1045,6 @@ "1043": "reset__select_word_from_share_template", "1044": "recovery__share_from_group_entered_template", "1045": "send__cancel_transaction", - "1046": "send__multisig_different_paths" + "1046": "send__multisig_different_paths", + "1047": "address__xpub" } diff --git a/core/translations/signatures.json b/core/translations/signatures.json index a968d18b01..ca7da75032 100644 --- a/core/translations/signatures.json +++ b/core/translations/signatures.json @@ -1,8 +1,8 @@ { "current": { - "merkle_root": "fbe14244ebd30a0fe40a3c2b52d86a650543ac3fec026cb1130de666ff141728", - "datetime": "2025-06-25T14:18:09.981692+00:00", - "commit": "b1b4095ad0078a4f97ffd477f7716b27063adb79" + "merkle_root": "298ecf2b73797fb62a725f64a2bb6c2233e1280c60a433b9ec87560ccb9cf69a", + "datetime": "2025-06-26T11:56:11.247313+00:00", + "commit": "545997e8726deae49f23a2108bd14844741d6a3e" }, "history": [ { diff --git a/tests/input_flows.py b/tests/input_flows.py index e17256de2f..6f2a60f3b5 100644 --- a/tests/input_flows.py +++ b/tests/input_flows.py @@ -763,7 +763,7 @@ class InputFlowShowMultisigXPUBs(InputFlowBase): yield # show address layout = self.debug.read_layout() - assert TR.address__title_receive_address in layout.subtitle() + assert TR.regexp("address__coin_address_template").match(layout.subtitle()) assert layout.text_content().replace(" ", "").strip() == self.address self.debug.click(self.debug.screen_buttons.menu()) @@ -927,7 +927,7 @@ class InputFlowShowXpubQRCode(InputFlowBase): for _ in range(br.pages - 1): self.debug.click(self.debug.screen_buttons.ok()) - assert layout.subtitle() in (TR.address__public_key, "XPUB") + assert TR.address__public_key in layout.title() self.debug.click(self.debug.screen_buttons.menu()) assert "VerticalMenu" in self.all_components()