diff --git a/core/embed/rust/librust_qstr.h b/core/embed/rust/librust_qstr.h index 7ac29af8f4e..741883517cf 100644 --- a/core/embed/rust/librust_qstr.h +++ b/core/embed/rust/librust_qstr.h @@ -166,6 +166,7 @@ static void _librust_qstrs(void) { MP_QSTR_buttons__try_again; MP_QSTR_buttons__turn_off; MP_QSTR_buttons__turn_on; + MP_QSTR_buttons__view_all_data; MP_QSTR_can_go_back; MP_QSTR_cancel_arrow; MP_QSTR_cancel_cross; @@ -217,8 +218,10 @@ static void _librust_qstrs(void) { MP_QSTR_debug__loading_seed; MP_QSTR_debug__loading_seed_not_recommended; MP_QSTR_decode; + MP_QSTR_default_cancel; MP_QSTR_deinit; MP_QSTR_description; + MP_QSTR_description_font_green; MP_QSTR_details_title; MP_QSTR_device_name__change_template; MP_QSTR_device_name__title; @@ -347,6 +350,7 @@ static void _librust_qstrs(void) { MP_QSTR_notification; MP_QSTR_notification_level; MP_QSTR_page_count; + MP_QSTR_page_limit; MP_QSTR_pages; MP_QSTR_paint; MP_QSTR_passphrase__access_wallet; @@ -728,6 +732,7 @@ static void _librust_qstrs(void) { MP_QSTR_value; MP_QSTR_verb; MP_QSTR_verb_cancel; + MP_QSTR_verb_info; MP_QSTR_verify; MP_QSTR_version; MP_QSTR_warning; @@ -800,6 +805,7 @@ static void _librust_qstrs(void) { MP_QSTR_words__title_threshold; MP_QSTR_words__try_again; MP_QSTR_words__unknown; + MP_QSTR_words__view_all_data_from_menu; MP_QSTR_words__warning; MP_QSTR_words__writable; MP_QSTR_words__yes; @@ -978,6 +984,7 @@ static void _librust_qstrs(void) { MP_QSTR_ethereum__data_size_template; MP_QSTR_ethereum__gas_limit; MP_QSTR_ethereum__gas_price; + MP_QSTR_ethereum__interaction_contract; MP_QSTR_ethereum__max_gas_price; MP_QSTR_ethereum__name_and_version; MP_QSTR_ethereum__new_contract; @@ -996,13 +1003,15 @@ static void _librust_qstrs(void) { MP_QSTR_ethereum__staking_stake_intro; MP_QSTR_ethereum__staking_unstake; MP_QSTR_ethereum__staking_unstake_intro; - MP_QSTR_ethereum__title_confirm_data; MP_QSTR_ethereum__title_confirm_domain; MP_QSTR_ethereum__title_confirm_message; MP_QSTR_ethereum__title_confirm_struct; MP_QSTR_ethereum__title_confirm_typed_data; + MP_QSTR_ethereum__title_input_data; MP_QSTR_ethereum__title_signing_address; + MP_QSTR_ethereum__token_contract; MP_QSTR_ethereum__units_template; + MP_QSTR_ethereum__unknown_contract_address; MP_QSTR_ethereum__unknown_token; MP_QSTR_ethereum__valid_signature; MP_QSTR_fido__already_registered; diff --git a/core/embed/rust/src/translations/generated/translated_string.rs b/core/embed/rust/src/translations/generated/translated_string.rs index 682f9572d8e..c03619ee132 100644 --- a/core/embed/rust/src/translations/generated/translated_string.rs +++ b/core/embed/rust/src/translations/generated/translated_string.rs @@ -473,7 +473,7 @@ pub enum TranslatedString { #[cfg(feature = "universal_fw")] ethereum__sign_eip712 = 285, // "Really sign EIP-712 typed data?" #[cfg(feature = "universal_fw")] - ethereum__title_confirm_data = 286, // "Confirm data" + ethereum__title_input_data = 286, // "Input data" #[cfg(feature = "universal_fw")] ethereum__title_confirm_domain = 287, // "Confirm domain" #[cfg(feature = "universal_fw")] @@ -1371,6 +1371,14 @@ pub enum TranslatedString { fido__title_credential_details = 965, // "Credential details" address__public_key_confirmed = 966, // "Public key confirmed" words__continue_anyway = 967, // "Continue anyway" + #[cfg(feature = "universal_fw")] + ethereum__unknown_contract_address = 968, // "Unknown contract address. Continue only if you know what you are doing." + #[cfg(feature = "universal_fw")] + ethereum__token_contract = 970, // "Token contract" + buttons__view_all_data = 971, // "View all data" + words__view_all_data_from_menu = 972, // "View all data from menu." + #[cfg(feature = "universal_fw")] + ethereum__interaction_contract = 973, // "Interaction contract" } impl TranslatedString { @@ -1838,7 +1846,7 @@ impl TranslatedString { #[cfg(feature = "universal_fw")] Self::ethereum__sign_eip712 => "Really sign EIP-712 typed data?", #[cfg(feature = "universal_fw")] - Self::ethereum__title_confirm_data => "Confirm data", + Self::ethereum__title_input_data => "Input data", #[cfg(feature = "universal_fw")] Self::ethereum__title_confirm_domain => "Confirm domain", #[cfg(feature = "universal_fw")] @@ -2736,6 +2744,14 @@ impl TranslatedString { Self::fido__title_credential_details => "Credential details", Self::address__public_key_confirmed => "Public key confirmed", Self::words__continue_anyway => "Continue anyway", + #[cfg(feature = "universal_fw")] + Self::ethereum__unknown_contract_address => "Unknown contract address. Continue only if you know what you are doing.", + #[cfg(feature = "universal_fw")] + Self::ethereum__token_contract => "Token contract", + Self::buttons__view_all_data => "View all data", + Self::words__view_all_data_from_menu => "View all data from menu.", + #[cfg(feature = "universal_fw")] + Self::ethereum__interaction_contract => "Interaction contract", } } @@ -3204,7 +3220,7 @@ impl TranslatedString { #[cfg(feature = "universal_fw")] Qstr::MP_QSTR_ethereum__sign_eip712 => Some(Self::ethereum__sign_eip712), #[cfg(feature = "universal_fw")] - Qstr::MP_QSTR_ethereum__title_confirm_data => Some(Self::ethereum__title_confirm_data), + Qstr::MP_QSTR_ethereum__title_input_data => Some(Self::ethereum__title_input_data), #[cfg(feature = "universal_fw")] Qstr::MP_QSTR_ethereum__title_confirm_domain => Some(Self::ethereum__title_confirm_domain), #[cfg(feature = "universal_fw")] @@ -4102,6 +4118,14 @@ impl TranslatedString { Qstr::MP_QSTR_fido__title_credential_details => Some(Self::fido__title_credential_details), Qstr::MP_QSTR_address__public_key_confirmed => Some(Self::address__public_key_confirmed), Qstr::MP_QSTR_words__continue_anyway => Some(Self::words__continue_anyway), + #[cfg(feature = "universal_fw")] + Qstr::MP_QSTR_ethereum__unknown_contract_address => Some(Self::ethereum__unknown_contract_address), + #[cfg(feature = "universal_fw")] + Qstr::MP_QSTR_ethereum__token_contract => Some(Self::ethereum__token_contract), + Qstr::MP_QSTR_buttons__view_all_data => Some(Self::buttons__view_all_data), + Qstr::MP_QSTR_words__view_all_data_from_menu => Some(Self::words__view_all_data_from_menu), + #[cfg(feature = "universal_fw")] + Qstr::MP_QSTR_ethereum__interaction_contract => Some(Self::ethereum__interaction_contract), _ => None, } } diff --git a/core/embed/rust/src/ui/flow/page.rs b/core/embed/rust/src/ui/flow/page.rs index 476ea1b72a1..e99f6d1ec5d 100644 --- a/core/embed/rust/src/ui/flow/page.rs +++ b/core/embed/rust/src/ui/flow/page.rs @@ -13,6 +13,7 @@ pub struct SwipePage { axis: Axis, pages: usize, current: usize, + limit: Option, } impl SwipePage { @@ -23,6 +24,7 @@ impl SwipePage { axis: Axis::Vertical, pages: 1, current: 0, + limit: None, } } @@ -33,12 +35,18 @@ impl SwipePage { axis: Axis::Horizontal, pages: 1, current: 0, + limit: None, } } pub fn inner(&self) -> &T { &self.inner } + + pub fn with_limit(mut self, limit: Option) -> Self { + self.limit = limit; + self + } } impl Component for SwipePage { @@ -47,6 +55,9 @@ impl Component for SwipePage { fn place(&mut self, bounds: Rect) -> Rect { self.bounds = self.inner.place(bounds); self.pages = self.inner.page_count(); + if let Some(limit) = self.limit { + self.pages = self.pages.min(limit); + } self.bounds } diff --git a/core/embed/rust/src/ui/model_mercury/component/frame.rs b/core/embed/rust/src/ui/model_mercury/component/frame.rs index 1e6aa12f484..de90949e916 100644 --- a/core/embed/rust/src/ui/model_mercury/component/frame.rs +++ b/core/embed/rust/src/ui/model_mercury/component/frame.rs @@ -150,6 +150,11 @@ where self.with_button(theme::ICON_MENU, FlowMsg::Info, true) } + pub fn with_danger_menu_button(self) -> Self { + self.with_button(theme::ICON_MENU, FlowMsg::Info, true) + .button_styled(theme::button_warning_high()) + } + pub fn with_warning_low_icon(self) -> Self { self.with_button(theme::ICON_WARNING, FlowMsg::Info, false) .button_styled(theme::button_warning_low()) diff --git a/core/embed/rust/src/ui/model_mercury/flow/confirm_action.rs b/core/embed/rust/src/ui/model_mercury/flow/confirm_action.rs index 0e2913f3200..4f2c9b5b0d5 100644 --- a/core/embed/rust/src/ui/model_mercury/flow/confirm_action.rs +++ b/core/embed/rust/src/ui/model_mercury/flow/confirm_action.rs @@ -96,6 +96,40 @@ impl FlowController for ConfirmActionSimple { } } +/// Flow similar to ConfirmActionSimple, but having swipe up cancel the flow +/// rather than confirm. To confirm, the user needs to open the menu. +#[derive(Copy, Clone, PartialEq, Eq)] +pub enum ConfirmActionSimpleDefaultCancel { + Intro, + Menu, +} + +impl FlowController for ConfirmActionSimpleDefaultCancel { + #[inline] + fn index(&'static self) -> usize { + *self as usize + } + + fn handle_swipe(&'static self, direction: Direction) -> Decision { + match (self, direction) { + (Self::Intro, Direction::Left) => Self::Menu.swipe(direction), + (Self::Menu, Direction::Right) => Self::Intro.swipe(direction), + (Self::Intro, Direction::Up) => self.return_msg(FlowMsg::Cancelled), + _ => self.do_nothing(), + } + } + + fn handle_event(&'static self, msg: FlowMsg) -> Decision { + match (self, msg) { + (Self::Intro, FlowMsg::Info) => Self::Menu.goto(), + (Self::Menu, FlowMsg::Cancelled) => Self::Intro.swipe_right(), + (Self::Menu, FlowMsg::Choice(0)) => self.return_msg(FlowMsg::Cancelled), + (Self::Menu, FlowMsg::Choice(1)) => self.return_msg(FlowMsg::Confirmed), + _ => self.do_nothing(), + } + } +} + #[allow(clippy::not_unsafe_ptr_arg_deref)] pub extern "C" fn new_confirm_action(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj { unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, new_confirm_action_obj) } @@ -144,31 +178,46 @@ fn new_confirm_action_obj(_args: &[Obj], kwargs: &Map) -> Result( +fn new_confirm_action_uni( content: T, title: TString<'static>, subtitle: Option>, verb_cancel: Option>, + verb_info: Option>, prompt_screen: Option>, hold: bool, info: bool, + default_cancel: bool, ) -> Result { - let (prompt_screen, prompt_pages, flow, page) = create_flow(title, prompt_screen, hold); + let (prompt_screen, prompt_pages, flow, page) = + create_flow(title, prompt_screen, hold, default_cancel); let mut content_intro = Frame::left_aligned(title, content) - .with_menu_button() - .with_footer(TR::instructions__swipe_up.into(), None) .with_swipe(Direction::Up, SwipeSettings::default()) .with_swipe(Direction::Left, SwipeSettings::default()) .with_vertical_pages(); + if default_cancel { + content_intro = content_intro.title_styled(theme::TEXT_WARNING); + content_intro = content_intro.with_danger_menu_button(); + content_intro = content_intro.with_footer( + TR::instructions__swipe_up.into(), + Some(TR::send__cancel_sign.into()), + ); + } else { + content_intro = content_intro.with_menu_button(); + content_intro = content_intro.with_footer(TR::instructions__swipe_up.into(), None); + } + if let Some(subtitle) = subtitle { content_intro = content_intro.with_subtitle(subtitle); } @@ -182,13 +231,23 @@ pub fn new_confirm_action_uni( let flow = flow?.with_page(page, content_intro)?; - create_menu_and_confirm(subtitle, verb_cancel, hold, info, prompt_screen, flow) + create_menu_and_confirm( + subtitle, + verb_cancel, + verb_info, + hold, + info, + prompt_screen, + default_cancel, + flow, + ) } fn create_flow( title: TString<'static>, prompt_screen: Option>, hold: bool, + default_cancel: bool, ) -> ( Option>, usize, @@ -198,16 +257,20 @@ fn create_flow( let prompt_screen = prompt_screen.or_else(|| hold.then_some(title)); let prompt_pages: usize = prompt_screen.is_some().into(); - let flow = if prompt_screen.is_some() { - SwipeFlow::new(&ConfirmAction::Intro) + let (flow, page): (Result, &dyn FlowController) = if prompt_screen.is_some() { + (SwipeFlow::new(&ConfirmAction::Intro), &ConfirmAction::Intro) } else { - SwipeFlow::new(&ConfirmActionSimple::Intro) - }; - - let page: &dyn FlowController = if prompt_screen.is_some() { - &ConfirmAction::Intro - } else { - &ConfirmActionSimple::Intro + if default_cancel { + ( + SwipeFlow::new(&ConfirmActionSimpleDefaultCancel::Intro), + &ConfirmActionSimpleDefaultCancel::Intro, + ) + } else { + ( + SwipeFlow::new(&ConfirmActionSimple::Intro), + &ConfirmActionSimple::Intro, + ) + } }; (prompt_screen, prompt_pages, flow, page) @@ -216,12 +279,21 @@ fn create_flow( fn create_menu_and_confirm( subtitle: Option>, verb_cancel: Option>, + verb_info: Option>, hold: bool, info: bool, prompt_screen: Option>, + default_cancel: bool, flow: SwipeFlow, ) -> Result { - let flow = create_menu(flow, verb_cancel, info, prompt_screen)?; + let flow = create_menu( + flow, + verb_cancel, + verb_info, + info, + default_cancel, + prompt_screen, + )?; let flow = create_confirm(flow, subtitle, hold, prompt_screen)?; @@ -231,19 +303,32 @@ fn create_menu_and_confirm( fn create_menu( flow: SwipeFlow, verb_cancel: Option>, + verb_info: Option>, info: bool, + default_cancel: bool, prompt_screen: Option>, ) -> Result { - let mut menu_choices = VerticalMenu::empty().danger( - theme::ICON_CANCEL, - verb_cancel.unwrap_or(TR::buttons__cancel.into()), - ); - if info { + let mut menu_choices = VerticalMenu::empty(); + if default_cancel { menu_choices = menu_choices.item( - theme::ICON_CHEVRON_RIGHT, - TR::words__title_information.into(), + theme::ICON_CANCEL, + verb_cancel.unwrap_or(TR::buttons__cancel.into()), + ); + menu_choices = + menu_choices.danger(theme::ICON_CHEVRON_RIGHT, TR::words__continue_anyway.into()); + } else { + menu_choices = menu_choices.danger( + theme::ICON_CANCEL, + verb_cancel.unwrap_or(TR::buttons__cancel.into()), ); + if info { + menu_choices = menu_choices.item( + theme::ICON_CHEVRON_RIGHT, + verb_info.unwrap_or(TR::words__title_information.into()), + ); + } } + let content_menu = Frame::left_aligned("".into(), menu_choices) .with_cancel_button() .with_swipe(Direction::Right, SwipeSettings::immediate()); @@ -307,17 +392,46 @@ pub fn new_confirm_action_simple title: TString<'static>, subtitle: Option>, verb_cancel: Option>, + verb_info: Option>, + prompt_screen: Option>, + hold: bool, + info: bool, + page_limit: Option, +) -> Result { + new_confirm_action_uni( + SwipeContent::new(SwipePage::vertical(content).with_limit(page_limit)), + title, + subtitle, + verb_cancel, + verb_info, + prompt_screen, + hold, + info, + false, + ) +} + +#[inline(never)] +pub fn new_confirm_action_simple_default_cancel( + content: T, + title: TString<'static>, + subtitle: Option>, + verb_cancel: Option>, + verb_info: Option>, prompt_screen: Option>, hold: bool, info: bool, + page_limit: Option, ) -> Result { new_confirm_action_uni( - SwipeContent::new(SwipePage::vertical(content)), + SwipeContent::new(SwipePage::vertical(content).with_limit(page_limit)), title, subtitle, verb_cancel, + verb_info, prompt_screen, hold, info, + true, ) } diff --git a/core/embed/rust/src/ui/model_mercury/flow/mod.rs b/core/embed/rust/src/ui/model_mercury/flow/mod.rs index 9cf4ff6c914..67bb15b7cc9 100644 --- a/core/embed/rust/src/ui/model_mercury/flow/mod.rs +++ b/core/embed/rust/src/ui/model_mercury/flow/mod.rs @@ -18,7 +18,9 @@ pub mod warning_hi_prio; mod util; -pub use confirm_action::{new_confirm_action, new_confirm_action_simple}; +pub use confirm_action::{ + new_confirm_action, new_confirm_action_simple, new_confirm_action_simple_default_cancel, +}; #[cfg(feature = "universal_fw")] pub use confirm_fido::new_confirm_fido; pub use confirm_firmware_update::new_confirm_firmware_update; diff --git a/core/embed/rust/src/ui/model_mercury/layout.rs b/core/embed/rust/src/ui/model_mercury/layout.rs index 9908af97361..07c87f694da 100644 --- a/core/embed/rust/src/ui/model_mercury/layout.rs +++ b/core/embed/rust/src/ui/model_mercury/layout.rs @@ -49,7 +49,7 @@ use crate::{ }, model_mercury::{ component::{check_homescreen_format, SwipeContent}, - flow::new_confirm_action_simple, + flow::{new_confirm_action_simple, new_confirm_action_simple_default_cancel}, theme::ICON_BULLET_CHECKMARK, }, }, @@ -237,14 +237,16 @@ extern "C" fn new_confirm_emphasized(n_args: usize, args: *const Obj, kwargs: *m } } - flow::new_confirm_action_simple( + new_confirm_action_simple( FormattedText::new(ops).vertically_centered(), title, None, None, + None, Some(title), false, false, + None, ) }; unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) } @@ -255,14 +257,18 @@ struct ConfirmBlobParams { subtitle: Option>, data: Obj, description: Option>, + description_font: &'static TextStyle, extra: Option>, verb: Option>, verb_cancel: Option>, + verb_info: Option>, info_button: bool, prompt: bool, hold: bool, chunkify: bool, text_mono: bool, + page_limit: Option, + default_cancel: bool, } impl ConfirmBlobParams { @@ -272,6 +278,7 @@ impl ConfirmBlobParams { description: Option>, verb: Option>, verb_cancel: Option>, + verb_info: Option>, prompt: bool, hold: bool, ) -> Self { @@ -280,14 +287,18 @@ impl ConfirmBlobParams { subtitle: None, data, description, + description_font: &theme::TEXT_NORMAL, extra: None, verb, verb_cancel, + verb_info, info_button: false, prompt, hold, chunkify: false, text_mono: true, + page_limit: None, + default_cancel: false, } } @@ -316,12 +327,27 @@ impl ConfirmBlobParams { self } + fn with_page_limit(mut self, page_limit: Option) -> Self { + self.page_limit = page_limit; + self + } + + fn with_default_cancel(mut self, default_cancel: bool) -> Self { + self.default_cancel = default_cancel; + self + } + + fn with_description_font(mut self, description_font: &'static TextStyle) -> Self { + self.description_font = description_font; + self + } + fn into_flow(self) -> Result { let paragraphs = ConfirmBlob { description: self.description.unwrap_or("".into()), extra: self.extra.unwrap_or("".into()), data: self.data.try_into()?, - description_font: &theme::TEXT_NORMAL, + description_font: self.description_font, extra_font: &theme::TEXT_DEMIBOLD, data_font: if self.chunkify { let data: TString = self.data.try_into()?; @@ -334,14 +360,22 @@ impl ConfirmBlobParams { } .into_paragraphs(); - flow::new_confirm_action_simple( + let build_flow = if self.default_cancel { + new_confirm_action_simple_default_cancel + } else { + new_confirm_action_simple + }; + + build_flow( paragraphs, self.title, self.subtitle, self.verb_cancel, + self.verb_info, self.prompt.then_some(self.title), self.hold, self.info_button, + self.page_limit, ) } } @@ -352,7 +386,11 @@ extern "C" fn new_confirm_blob(n_args: usize, args: *const Obj, kwargs: *mut Map let data: Obj = kwargs.get(Qstr::MP_QSTR_data)?; let description: Option = kwargs.get(Qstr::MP_QSTR_description)?.try_into_option()?; + let description_font_green: bool = + kwargs.get_or(Qstr::MP_QSTR_description_font_green, false)?; + let text_mono: bool = kwargs.get_or(Qstr::MP_QSTR_text_mono, true)?; let extra: Option = kwargs.get(Qstr::MP_QSTR_extra)?.try_into_option()?; + let subtitle: Option = kwargs.get(Qstr::MP_QSTR_subtitle)?.try_into_option()?; let verb: Option = kwargs .get(Qstr::MP_QSTR_verb) .unwrap_or_else(|_| Obj::const_none()) @@ -361,9 +399,22 @@ extern "C" fn new_confirm_blob(n_args: usize, args: *const Obj, kwargs: *mut Map .get(Qstr::MP_QSTR_verb_cancel) .unwrap_or_else(|_| Obj::const_none()) .try_into_option()?; + let verb_info: Option = kwargs + .get(Qstr::MP_QSTR_verb_info) + .unwrap_or_else(|_| Obj::const_none()) + .try_into_option()?; + let info: bool = kwargs.get_or(Qstr::MP_QSTR_info, true)?; let hold: bool = kwargs.get_or(Qstr::MP_QSTR_hold, false)?; let chunkify: bool = kwargs.get_or(Qstr::MP_QSTR_chunkify, false)?; let prompt_screen: bool = kwargs.get_or(Qstr::MP_QSTR_prompt_screen, true)?; + let default_cancel: bool = kwargs.get_or(Qstr::MP_QSTR_default_cancel, false)?; + let page_limit: Option = kwargs.get(Qstr::MP_QSTR_page_limit)?.try_into_option()?; + + let description_font = if description_font_green { + &theme::TEXT_SUB_GREEN_LIME + } else { + &theme::TEXT_NORMAL + }; ConfirmBlobParams::new( title, @@ -371,11 +422,18 @@ extern "C" fn new_confirm_blob(n_args: usize, args: *const Obj, kwargs: *mut Map description, verb, verb_cancel, + verb_info, prompt_screen, hold, ) + .with_description_font(description_font) + .with_text_mono(text_mono) + .with_subtitle(subtitle) .with_extra(extra) + .with_info_button(info) .with_chunkify(chunkify) + .with_default_cancel(default_cancel) + .with_page_limit(page_limit) .into_flow() }; unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) } @@ -407,7 +465,9 @@ extern "C" fn new_confirm_address(n_args: usize, args: *const Obj, kwargs: *mut } .into_paragraphs(); - flow::new_confirm_action_simple(paragraphs, title, None, None, None, false, false) + new_confirm_action_simple( + paragraphs, title, None, None, None, None, false, false, None, + ) }; unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) } } @@ -425,14 +485,16 @@ extern "C" fn new_confirm_properties(n_args: usize, args: *const Obj, kwargs: *m &theme::TEXT_MONO, )?; - flow::new_confirm_action_simple( + new_confirm_action_simple( paragraphs.into_paragraphs(), title, None, None, + None, hold.then_some(title), hold, false, + None, ) }; unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) } @@ -459,9 +521,11 @@ extern "C" fn new_confirm_homescreen(n_args: usize, args: *const Obj, kwargs: *m TR::homescreen__settings_title.into(), Some(TR::homescreen__settings_subtitle.into()), None, + None, Some(TR::homescreen__settings_title.into()), false, false, + None, ) } else { if !check_homescreen_format(jpeg) { @@ -539,12 +603,21 @@ extern "C" fn new_confirm_value(n_args: usize, args: *const Obj, kwargs: *mut Ma let chunkify: bool = kwargs.get_or(Qstr::MP_QSTR_chunkify, false)?; let text_mono: bool = kwargs.get_or(Qstr::MP_QSTR_text_mono, true)?; - ConfirmBlobParams::new(title, value, description, verb, verb_cancel, hold, hold) - .with_subtitle(subtitle) - .with_info_button(info_button) - .with_chunkify(chunkify) - .with_text_mono(text_mono) - .into_flow() + ConfirmBlobParams::new( + title, + value, + description, + verb, + verb_cancel, + None, + hold, + hold, + ) + .with_subtitle(subtitle) + .with_info_button(info_button) + .with_chunkify(chunkify) + .with_text_mono(text_mono) + .into_flow() }; unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) } } @@ -562,14 +635,16 @@ extern "C" fn new_confirm_total(n_args: usize, args: *const Obj, kwargs: *mut Ma paragraphs.add(Paragraph::new(&theme::TEXT_MONO, value)); } - flow::new_confirm_action_simple( + new_confirm_action_simple( paragraphs.into_paragraphs(), title, None, None, + None, Some(title), true, true, + None, ) }; unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) } @@ -851,14 +926,16 @@ extern "C" fn new_confirm_coinjoin(n_args: usize, args: *const Obj, kwargs: *mut ]) .into_paragraphs(); - flow::new_confirm_action_simple( + new_confirm_action_simple( paragraphs, TR::coinjoin__title.into(), None, None, + None, Some(TR::coinjoin__title.into()), true, false, + None, ) }; unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) } @@ -1263,12 +1340,19 @@ pub static mp_module_trezorui2: Module = obj_module! { /// title: str, /// data: str | bytes, /// description: str | None, - /// extra: str | None, + /// description_font_green: bool = False, + /// text_mono: bool = True, + /// extra: str | None = None, + /// subtitle: str | None = None, /// verb: str | None = None, /// verb_cancel: str | None = None, + /// verb_info: str | None = None, + /// info: bool = True, /// hold: bool = False, /// chunkify: bool = False, /// prompt_screen: bool = False, + /// default_cancel: bool = False, + /// page_limit: int | None = None, /// ) -> LayoutObj[UiResult]: /// """Confirm byte sequence data.""" Qstr::MP_QSTR_confirm_blob => obj_fn_kw!(0, new_confirm_blob).as_obj(), diff --git a/core/embed/rust/src/ui/model_tr/layout.rs b/core/embed/rust/src/ui/model_tr/layout.rs index f957ca85100..c68603201f0 100644 --- a/core/embed/rust/src/ui/model_tr/layout.rs +++ b/core/embed/rust/src/ui/model_tr/layout.rs @@ -1679,12 +1679,19 @@ pub static mp_module_trezorui2: Module = obj_module! { /// title: str, /// data: str | bytes, /// description: str | None, - /// extra: str | None, + /// description_font_green: bool = False, + /// text_mono: bool = True, + /// extra: str | None = None, + /// subtitle: str | None = None, /// verb: str = "CONFIRM", /// verb_cancel: str | None = None, + /// verb_info: str | None = None, + /// info: bool = True, /// hold: bool = False, /// chunkify: bool = False, /// prompt_screen: bool = False, + /// default_cancel: bool = False, + /// page_limit: int | None = None, /// ) -> LayoutObj[UiResult]: /// """Confirm byte sequence data.""" Qstr::MP_QSTR_confirm_blob => obj_fn_kw!(0, new_confirm_blob).as_obj(), diff --git a/core/embed/rust/src/ui/model_tt/layout.rs b/core/embed/rust/src/ui/model_tt/layout.rs index 3d932594123..5ea63427b26 100644 --- a/core/embed/rust/src/ui/model_tt/layout.rs +++ b/core/embed/rust/src/ui/model_tt/layout.rs @@ -1762,12 +1762,19 @@ pub static mp_module_trezorui2: Module = obj_module! { /// title: str, /// data: str | bytes, /// description: str | None, - /// extra: str | None, + /// description_font_green: bool = False, + /// text_mono: bool = True, + /// extra: str | None = None, + /// subtitle: str | None = None, /// verb: str | None = None, /// verb_cancel: str | None = None, + /// verb_info: str | None = None, + /// info: bool = True, /// hold: bool = False, /// chunkify: bool = False, /// prompt_screen: bool = False, + /// default_cancel: bool = False, + /// page_limit: int | None = None, /// ) -> LayoutObj[UiResult]: /// """Confirm byte sequence data.""" Qstr::MP_QSTR_confirm_blob => obj_fn_kw!(0, new_confirm_blob).as_obj(), diff --git a/core/mocks/generated/trezorui2.pyi b/core/mocks/generated/trezorui2.pyi index 1e5c90e6394..6dff117cf86 100644 --- a/core/mocks/generated/trezorui2.pyi +++ b/core/mocks/generated/trezorui2.pyi @@ -118,12 +118,19 @@ def confirm_blob( title: str, data: str | bytes, description: str | None, - extra: str | None, + description_font_green: bool = False, + text_mono: bool = True, + extra: str | None = None, + subtitle: str | None = None, verb: str | None = None, verb_cancel: str | None = None, + verb_info: str | None = None, + info: bool = True, hold: bool = False, chunkify: bool = False, prompt_screen: bool = False, + default_cancel: bool = False, + page_limit: int | None = None, ) -> LayoutObj[UiResult]: """Confirm byte sequence data.""" @@ -674,12 +681,19 @@ def confirm_blob( title: str, data: str | bytes, description: str | None, - extra: str | None, + description_font_green: bool = False, + text_mono: bool = True, + extra: str | None = None, + subtitle: str | None = None, verb: str = "CONFIRM", verb_cancel: str | None = None, + verb_info: str | None = None, + info: bool = True, hold: bool = False, chunkify: bool = False, prompt_screen: bool = False, + default_cancel: bool = False, + page_limit: int | None = None, ) -> LayoutObj[UiResult]: """Confirm byte sequence data.""" @@ -1228,12 +1242,19 @@ def confirm_blob( title: str, data: str | bytes, description: str | None, - extra: str | None, + description_font_green: bool = False, + text_mono: bool = True, + extra: str | None = None, + subtitle: str | None = None, verb: str | None = None, verb_cancel: str | None = None, + verb_info: str | None = None, + info: bool = True, hold: bool = False, chunkify: bool = False, prompt_screen: bool = False, + default_cancel: bool = False, + page_limit: int | None = None, ) -> LayoutObj[UiResult]: """Confirm byte sequence data.""" diff --git a/core/mocks/trezortranslate_keys.pyi b/core/mocks/trezortranslate_keys.pyi index 7bf9f6f03c6..74e4f2cf921 100644 --- a/core/mocks/trezortranslate_keys.pyi +++ b/core/mocks/trezortranslate_keys.pyi @@ -121,6 +121,7 @@ class TR: buttons__try_again: str = "Try again" buttons__turn_off: str = "Turn off" buttons__turn_on: str = "Turn on" + buttons__view_all_data: str = "View all data" cardano__addr_base: str = "Base" cardano__addr_enterprise: str = "Enterprise" cardano__addr_legacy: str = "Legacy" @@ -298,6 +299,7 @@ class TR: ethereum__data_size_template: str = "Size: {0} bytes" ethereum__gas_limit: str = "Gas limit" ethereum__gas_price: str = "Gas price" + ethereum__interaction_contract: str = "Interaction contract" ethereum__max_gas_price: str = "Max gas price" ethereum__name_and_version: str = "Name and version" ethereum__new_contract: str = "new contract?" @@ -316,13 +318,15 @@ class TR: ethereum__staking_stake_intro: str = "Stake ETH on Everstake?" ethereum__staking_unstake: str = "Unstake" ethereum__staking_unstake_intro: str = "Unstake ETH from Everstake?" - ethereum__title_confirm_data: str = "Confirm data" ethereum__title_confirm_domain: str = "Confirm domain" ethereum__title_confirm_message: str = "Confirm message" ethereum__title_confirm_struct: str = "Confirm struct" ethereum__title_confirm_typed_data: str = "Confirm typed data" + ethereum__title_input_data: str = "Input data" ethereum__title_signing_address: str = "Signing address" + ethereum__token_contract: str = "Token contract" ethereum__units_template: str = "{0} units" + ethereum__unknown_contract_address: str = "Unknown contract address. Continue only if you know what you are doing." ethereum__unknown_token: str = "Unknown token" ethereum__valid_signature: str = "The signature is valid." experimental_mode__enable: str = "Enable experimental features?" @@ -957,6 +961,7 @@ class TR: words__title_threshold: str = "Threshold" words__try_again: str = "Try again." words__unknown: str = "Unknown" + words__view_all_data_from_menu: str = "View all data from menu." words__warning: str = "Warning" words__writable: str = "Writable" words__yes: str = "Yes" diff --git a/core/src/apps/ethereum/layout.py b/core/src/apps/ethereum/layout.py index 8f88d9f8020..9dad59537c5 100644 --- a/core/src/apps/ethereum/layout.py +++ b/core/src/apps/ethereum/layout.py @@ -4,6 +4,7 @@ from trezor.enums import ButtonRequestType from trezor.ui.layouts import ( confirm_blob, + confirm_blob_with_optional_pagination, confirm_ethereum_staking_tx, confirm_text, should_show_more, @@ -35,6 +36,7 @@ async def require_confirm_tx( fee_info_items: Iterable[tuple[str, str]], network: EthereumNetworkInfo, token: EthereumTokenInfo | None, + is_contract_interaction: bool, chunkify: bool, ) -> None: from trezor.ui.layouts import confirm_ethereum_tx @@ -56,6 +58,7 @@ async def require_confirm_tx( account_path, maximum_fee, fee_info_items, + is_contract_interaction, chunkify=chunkify, ) @@ -145,17 +148,27 @@ async def require_confirm_claim( ) -def require_confirm_unknown_token(address_bytes: bytes) -> Awaitable[None]: +async def require_confirm_unknown_token(address_bytes: bytes): from ubinascii import hexlify - from trezor.ui.layouts import confirm_address + from trezor.ui.layouts import confirm_address, confirm_blob + + await confirm_blob( + "unknown_contract_warning", + TR.words__warning, + TR.ethereum__unknown_contract_address, + text_mono=False, + verb_cancel=TR.send__cancel_sign, + default_cancel=True, + prompt_screen=False, + ) contract_address_hex = "0x" + hexlify(address_bytes).decode() - return confirm_address( - TR.ethereum__unknown_token, + await confirm_address( + TR.words__address, contract_address_hex, - TR.ethereum__contract, - "unknown_token", + subtitle=TR.ethereum__token_contract, + br_name="unknown_token", br_code=ButtonRequestType.SignTx, ) @@ -174,13 +187,13 @@ def require_confirm_address(address_bytes: bytes) -> Awaitable[None]: def require_confirm_other_data(data: bytes, data_total: int) -> Awaitable[None]: - return confirm_blob( + return confirm_blob_with_optional_pagination( "confirm_data", - TR.ethereum__title_confirm_data, + TR.ethereum__title_input_data, data, - TR.ethereum__data_size_template.format(data_total), + subtitle=TR.ethereum__data_size_template.format(data_total), + verb_cancel=TR.send__cancel_sign, br_code=ButtonRequestType.SignTx, - ask_pagination=True, ) @@ -301,7 +314,6 @@ async def confirm_typed_value( title, data, description, - ask_pagination=True, ) else: await confirm_text( diff --git a/core/src/apps/ethereum/sign_tx.py b/core/src/apps/ethereum/sign_tx.py index 7e1dc3d0ae3..0a0dbeced63 100644 --- a/core/src/apps/ethereum/sign_tx.py +++ b/core/src/apps/ethereum/sign_tx.py @@ -56,7 +56,7 @@ async def sign_tx( raise DataError("Fee overflow") check_common_fields(msg) - # have a user confirm signing + # have the user confirm signing await paths.validate_path(keychain, msg.address_n) address_bytes = bytes_from_address(msg.to) gas_price = int.from_bytes(msg.gas_price, "big") @@ -130,9 +130,9 @@ async def confirm_tx_data( return # Handle ERC-20, currently only 'transfer' function - token, recipient, value = await handle_erc20_transfer(msg, defs, address_bytes) + token, recipient, value = await _handle_erc20_transfer(msg, defs, address_bytes) - if token is None and data_total_len > 0: + if data_total_len > 0: await require_confirm_other_data(msg.data_initial_chunk, data_total_len) await require_confirm_tx( @@ -143,7 +143,8 @@ async def confirm_tx_data( fee_items, defs.network, token, - bool(msg.chunkify), + is_contract_interaction=(data_total_len > 0), + chunkify=bool(msg.chunkify), ) @@ -189,7 +190,7 @@ async def handle_staking( return False -async def handle_erc20_transfer( +async def _handle_erc20_transfer( msg: MsgInSignTx, definitions: Definitions, address_bytes: bytes, diff --git a/core/src/apps/misc/get_ecdh_session_key.py b/core/src/apps/misc/get_ecdh_session_key.py index b4403e26104..8698389556e 100644 --- a/core/src/apps/misc/get_ecdh_session_key.py +++ b/core/src/apps/misc/get_ecdh_session_key.py @@ -31,10 +31,8 @@ async def get_ecdh_session_key(msg: GetECDHSessionKey) -> ECDHSessionKey: # require_confirm_ecdh_session_key proto = msg_identity.proto.upper() if msg_identity.proto else "identity" await confirm_address( - # TODO: translate? - f"Decrypt {proto}", + f"Decrypt {proto}", # TODO: translate? serialize_identity_without_proto(msg_identity), - "", ) # END require_confirm_ecdh_session_key diff --git a/core/src/apps/nem/multisig/layout.py b/core/src/apps/nem/multisig/layout.py index 328cd563689..09ad4ae8e6c 100644 --- a/core/src/apps/nem/multisig/layout.py +++ b/core/src/apps/nem/multisig/layout.py @@ -61,7 +61,7 @@ async def _require_confirm_address(action: str, address: str) -> None: await confirm_address( TR.nem__confirm_address, address, - action, - "confirm_multisig", - ButtonRequestType.ConfirmOutput, + description=action, + br_name="confirm_multisig", + br_code=ButtonRequestType.ConfirmOutput, ) diff --git a/core/src/apps/stellar/layout.py b/core/src/apps/stellar/layout.py index 3d3a5107d00..374f53bddfa 100644 --- a/core/src/apps/stellar/layout.py +++ b/core/src/apps/stellar/layout.py @@ -24,8 +24,8 @@ async def require_confirm_init( await layouts.confirm_address( TR.stellar__confirm_stellar, address, - description, - "confirm_init", + description=description, + br_name="confirm_init", ) # get_network_warning diff --git a/core/src/apps/stellar/operations/layout.py b/core/src/apps/stellar/operations/layout.py index 84672b15ef3..332e5097487 100644 --- a/core/src/apps/stellar/operations/layout.py +++ b/core/src/apps/stellar/operations/layout.py @@ -37,8 +37,8 @@ async def confirm_source_account(source_account: str) -> None: await confirm_address( TR.stellar__confirm_operation, source_account, - TR.stellar__source_account, - "op_source_account", + description=TR.stellar__source_account, + br_name="op_source_account", ) @@ -57,8 +57,8 @@ async def confirm_account_merge_op(op: StellarAccountMergeOp) -> None: await confirm_address( TR.stellar__account_merge, op.destination_account, - TR.stellar__all_will_be_sent_to, - "op_account_merge", + description=TR.stellar__all_will_be_sent_to, + br_name="op_account_merge", ) @@ -240,8 +240,8 @@ async def confirm_set_options_op(op: StellarSetOptionsOp) -> None: await confirm_address( TR.stellar__inflation, op.inflation_destination_account, - TR.stellar__destination, - "op_inflation", + description=TR.stellar__destination, + br_name="op_inflation", ) if op.clear_flags: @@ -339,6 +339,6 @@ async def confirm_asset_issuer(asset: StellarAsset) -> None: await confirm_address( TR.stellar__confirm_issuer, asset.issuer, - TR.stellar__issuer_template.format(asset.code), - "confirm_asset_issuer", + description=TR.stellar__issuer_template.format(asset.code), + br_name="confirm_asset_issuer", ) diff --git a/core/src/apps/tezos/layout.py b/core/src/apps/tezos/layout.py index 4981dbf8159..78029249450 100644 --- a/core/src/apps/tezos/layout.py +++ b/core/src/apps/tezos/layout.py @@ -30,9 +30,9 @@ async def require_confirm_origination(address: str) -> None: await confirm_address( TR.tezos__confirm_origination, address, - f"{TR.words__address}:", - "confirm_origination", - BR_SIGN_TX, + description=f"{TR.words__address}:", + br_name="confirm_origination", + br_code=BR_SIGN_TX, ) @@ -52,9 +52,9 @@ async def require_confirm_delegation_baker(baker: str) -> None: await confirm_address( TR.tezos__confirm_delegation, baker, - TR.tezos__baker_address, - "confirm_delegation", - BR_SIGN_TX, + description=TR.tezos__baker_address, + br_name="confirm_delegation", + br_code=BR_SIGN_TX, ) @@ -121,9 +121,9 @@ async def require_confirm_delegation_manager_withdraw(address: str) -> None: await confirm_address( TR.tezos__remove_delegation, address, - TR.tezos__delegator, - "confirm_undelegation", - BR_SIGN_TX, + description=TR.tezos__delegator, + br_name="confirm_undelegation", + br_code=BR_SIGN_TX, ) diff --git a/core/src/trezor/ui/layouts/mercury/__init__.py b/core/src/trezor/ui/layouts/mercury/__init__.py index 9e819f44b0b..6afb8a8ff1e 100644 --- a/core/src/trezor/ui/layouts/mercury/__init__.py +++ b/core/src/trezor/ui/layouts/mercury/__init__.py @@ -750,43 +750,56 @@ async def should_show_more( raise ActionCancelled -async def _confirm_ask_pagination( +async def confirm_blob_with_optional_pagination( br_name: str, title: str, data: bytes | str, - description: str, - br_code: ButtonRequestType, -) -> None: - paginated: ui.Layout | None = None - # TODO: make should_show_more/confirm_more accept bytes directly - if isinstance(data, bytes): - from ubinascii import hexlify - - data = hexlify(data).decode() - while True: - if not await should_show_more( - title, - para=[(ui.NORMAL, description), (ui.MONO, data)], + subtitle: str | None = None, + verb: str | None = None, + verb_cancel: str | None = None, + br_code: ButtonRequestType = BR_CODE_OTHER, + chunkify: bool = False, +): + # show first page first + layout = RustLayout( + trezorui2.confirm_blob( + title=title, + data=data, + description=TR.words__view_all_data_from_menu, + description_font_green=True, + extra=None, + subtitle=subtitle, + verb=verb, + verb_cancel=verb_cancel, + verb_info=TR.buttons__view_all_data, + hold=False, + chunkify=chunkify, + prompt_screen=False, + page_limit=1, + ) + ) + result = await interact( + layout, + br_name, + br_code, + ) + if result is INFO: + # user requested to view the whole blob + await confirm_blob( br_name=br_name, + title=title, + data=data, + description=None, + verb=verb, + verb_cancel=verb_cancel, + info=False, + hold=False, br_code=br_code, - ): - return - - if paginated is None: - paginated = RustLayout( - trezorui2.confirm_more( - title=title, - button=TR.buttons__close, - items=[(ui.MONO, data)], - ) - ) - else: - paginated.request_complete_repaint() - - result = await interact(paginated, br_name, br_code) - assert result in (CONFIRMED, CANCELLED) - - assert False + chunkify=chunkify, + prompt_screen=False, + ) + elif result is not CONFIRMED: + raise ActionCancelled def confirm_blob( @@ -794,45 +807,48 @@ def confirm_blob( title: str, data: bytes | str, description: str | None = None, + text_mono: bool = True, + subtitle: str | None = None, verb: str | None = None, verb_cancel: str | None = None, + info: bool = True, hold: bool = False, br_code: ButtonRequestType = BR_CODE_OTHER, - ask_pagination: bool = False, chunkify: bool = False, + default_cancel: bool = False, prompt_screen: bool = True, ) -> Awaitable[None]: layout = RustLayout( trezorui2.confirm_blob( title=title, - description=description, data=data, + description=description, + text_mono=text_mono, extra=None, - hold=hold, + subtitle=subtitle, verb=verb, verb_cancel=verb_cancel, + info=info, + hold=hold, chunkify=chunkify, prompt_screen=prompt_screen, + default_cancel=default_cancel, + page_limit=None, ) ) - - if ask_pagination and layout.page_count() > 1: - assert not hold - return _confirm_ask_pagination(br_name, title, data, description or "", br_code) - - else: - return raise_if_not_confirmed( - interact( - layout, - br_name, - br_code, - ) + return raise_if_not_confirmed( + interact( + layout, + br_name, + br_code, ) + ) def confirm_address( title: str, address: str, + subtitle: str | None = None, description: str | None = None, br_name: str = "confirm_address", br_code: ButtonRequestType = BR_CODE_OTHER, @@ -843,7 +859,9 @@ def confirm_address( description or "", br_name, br_code, + subtitle=subtitle, verb=TR.buttons__confirm, + chunkify=True, ) @@ -893,6 +911,7 @@ def confirm_value( subtitle: str | None = None, hold: bool = False, value_text_mono: bool = True, + chunkify: bool = False, info_items: Iterable[tuple[str, str]] | None = None, info_title: str | None = None, chunkify_info: bool = False, @@ -922,6 +941,7 @@ def confirm_value( verb=verb, hold=hold, info_button=bool(info_items), + chunkify=chunkify, text_mono=value_text_mono, ) ), @@ -1038,6 +1058,7 @@ async def confirm_ethereum_tx( account_path: str | None, maximum_fee: str, fee_info_items: Iterable[tuple[str, str]], + is_contract_interaction: bool, br_name: str = "confirm_ethereum_tx", br_code: ButtonRequestType = ButtonRequestType.SignTx, chunkify: bool = False, @@ -1046,7 +1067,11 @@ async def confirm_ethereum_tx( RustLayout( trezorui2.flow_confirm_output( title=TR.words__address, - subtitle=TR.words__recipient, + subtitle=( + TR.words__recipient + if not is_contract_interaction + else TR.ethereum__interaction_contract + ), message=recipient, amount=None, chunkify=chunkify, @@ -1178,7 +1203,7 @@ def confirm_replacement(title: str, txid: str) -> Awaitable[None]: title, txid, TR.send__transaction_id, - TR.buttons__continue, + verb=TR.buttons__continue, br_code=ButtonRequestType.SignTx, ) diff --git a/core/src/trezor/ui/layouts/tr/__init__.py b/core/src/trezor/ui/layouts/tr/__init__.py index 54105ffeb1f..23a4c0bdf14 100644 --- a/core/src/trezor/ui/layouts/tr/__init__.py +++ b/core/src/trezor/ui/layouts/tr/__init__.py @@ -991,6 +991,7 @@ async def _confirm_ask_pagination( def confirm_address( title: str, address: str, + subtitle: str | None = None, description: str | None = None, br_name: str = "confirm_address", br_code: ButtonRequestType = BR_CODE_OTHER, @@ -1274,6 +1275,7 @@ async def confirm_ethereum_tx( _account_path: str | None, maximum_fee: str, fee_info_items: Iterable[tuple[str, str]], + _is_contract_interaction: bool, br_name: str = "confirm_ethereum_tx", br_code: ButtonRequestType = ButtonRequestType.SignTx, chunkify: bool = False, diff --git a/core/src/trezor/ui/layouts/tt/__init__.py b/core/src/trezor/ui/layouts/tt/__init__.py index ba9d86fec18..b24015f1fd0 100644 --- a/core/src/trezor/ui/layouts/tt/__init__.py +++ b/core/src/trezor/ui/layouts/tt/__init__.py @@ -904,6 +904,7 @@ def confirm_blob( def confirm_address( title: str, address: str, + subtitle: str | None = None, description: str | None = None, br_name: str = "confirm_address", br_code: ButtonRequestType = BR_CODE_OTHER, @@ -914,6 +915,7 @@ def confirm_address( description or "", br_name, br_code, + subtitle=subtitle, verb=TR.buttons__confirm, ) @@ -1101,6 +1103,7 @@ async def confirm_ethereum_tx( _account_path: str | None, maximum_fee: str, fee_info_items: Iterable[tuple[str, str]], + _is_contract_interaction: bool, br_name: str = "confirm_ethereum_tx", br_code: ButtonRequestType = ButtonRequestType.SignTx, chunkify: bool = False, @@ -1255,7 +1258,7 @@ def confirm_replacement(title: str, txid: str) -> Awaitable[None]: title, txid, TR.send__transaction_id, - TR.buttons__continue, + verb=TR.buttons__continue, br_code=ButtonRequestType.SignTx, ) diff --git a/core/tools/translations/rules.json b/core/tools/translations/rules.json index 863ed1caea2..a57a724c6d5 100644 --- a/core/tools/translations/rules.json +++ b/core/tools/translations/rules.json @@ -284,7 +284,7 @@ "ethereum__show_full_message": "text,1", "ethereum__show_full_struct": "text,1", "ethereum__sign_eip712": "text,2", - "ethereum__title_confirm_data": "title,1", + "ethereum__title_input_data": "title,1", "ethereum__title_confirm_domain": "title,1", "ethereum__title_confirm_message": "title,1", "ethereum__title_confirm_struct": "title,1", diff --git a/core/translations/cs.json b/core/translations/cs.json index b7ffbc874f0..aa5c149c8c0 100644 --- a/core/translations/cs.json +++ b/core/translations/cs.json @@ -358,11 +358,11 @@ "ethereum__staking_stake_intro": "Stakovat ETH na Everstake?", "ethereum__staking_unstake": "Zrušit stakování", "ethereum__staking_unstake_intro": "Zrušit stakování ETH z Everstake?", - "ethereum__title_confirm_data": "Potvrdit data", "ethereum__title_confirm_domain": "Potvrdit doménu", "ethereum__title_confirm_message": "Potvrdit zprávu", "ethereum__title_confirm_struct": "Potvrdit strukturu", "ethereum__title_confirm_typed_data": "Potvrdit typ. data", + "ethereum__title_input_data": "Vstup data", "ethereum__title_signing_address": "Podepisování adresy", "ethereum__units_template": "Jednotky: {0}", "ethereum__unknown_token": "Neznámý token", diff --git a/core/translations/de.json b/core/translations/de.json index 1d2ec18f179..1945ec62b6c 100644 --- a/core/translations/de.json +++ b/core/translations/de.json @@ -358,11 +358,11 @@ "ethereum__staking_stake_intro": "ETH auf Everstake staken?", "ethereum__staking_unstake": "Entstaken", "ethereum__staking_unstake_intro": "ETH von Everstake entstaken?", - "ethereum__title_confirm_data": "Daten bestätigen", "ethereum__title_confirm_domain": "Domain bestätigen", "ethereum__title_confirm_message": "Nachr. bestätigen", "ethereum__title_confirm_struct": "Struktur bestätigen", "ethereum__title_confirm_typed_data": "Daten bestätigen", + "ethereum__title_input_data": "Eingabe-Daten", "ethereum__title_signing_address": "Signieradresse", "ethereum__units_template": "{0} Einheiten", "ethereum__unknown_token": "Ungültiger Token", @@ -953,8 +953,8 @@ "words__confirm": "Bestätigen", "words__confirm_fee": "Gebühr bestätigen", "words__contains": "Enthält", - "words__continue_anyway": "trotzdem fortfahren", - "words__continue_anyway_question": "trotzdem fortfahren?", + "words__continue_anyway": "Trotzdem fortfahren", + "words__continue_anyway_question": "Trotzdem fortfahren?", "words__continue_with": "Weiter mit", "words__error": "Fehler", "words__fee": "Gebühr", diff --git a/core/translations/en.json b/core/translations/en.json index 2f0499af778..3ec5c24a284 100644 --- a/core/translations/en.json +++ b/core/translations/en.json @@ -123,6 +123,7 @@ "buttons__try_again": "Try again", "buttons__turn_off": "Turn off", "buttons__turn_on": "Turn on", + "buttons__view_all_data": "View all data", "cardano__addr_base": "Base", "cardano__addr_enterprise": "Enterprise", "cardano__addr_legacy": "Legacy", @@ -300,6 +301,7 @@ "ethereum__data_size_template": "Size: {0} bytes", "ethereum__gas_limit": "Gas limit", "ethereum__gas_price": "Gas price", + "ethereum__interaction_contract": "Interaction contract", "ethereum__max_gas_price": "Max gas price", "ethereum__name_and_version": "Name and version", "ethereum__new_contract": "new contract?", @@ -318,13 +320,15 @@ "ethereum__staking_stake_intro": "Stake ETH on Everstake?", "ethereum__staking_unstake": "Unstake", "ethereum__staking_unstake_intro": "Unstake ETH from Everstake?", - "ethereum__title_confirm_data": "Confirm data", "ethereum__title_confirm_domain": "Confirm domain", "ethereum__title_confirm_message": "Confirm message", "ethereum__title_confirm_struct": "Confirm struct", "ethereum__title_confirm_typed_data": "Confirm typed data", + "ethereum__title_input_data": "Input data", "ethereum__title_signing_address": "Signing address", + "ethereum__token_contract": "Token contract", "ethereum__units_template": "{0} units", + "ethereum__unknown_contract_address": "Unknown contract address. Continue only if you know what you are doing.", "ethereum__unknown_token": "Unknown token", "ethereum__valid_signature": "The signature is valid.", "experimental_mode__enable": "Enable experimental features?", @@ -959,6 +963,7 @@ "words__title_threshold": "Threshold", "words__try_again": "Try again.", "words__unknown": "Unknown", + "words__view_all_data_from_menu": "View all data from menu.", "words__warning": "Warning", "words__writable": "Writable", "words__yes": "Yes" diff --git a/core/translations/es.json b/core/translations/es.json index 48e878bc0b8..7994ba8f045 100644 --- a/core/translations/es.json +++ b/core/translations/es.json @@ -358,11 +358,11 @@ "ethereum__staking_stake_intro": "¿Hacer stake de ETH en Everstake?", "ethereum__staking_unstake": "Retirar stake", "ethereum__staking_unstake_intro": "¿Retirar ETH de Everstake?", - "ethereum__title_confirm_data": "Validar datos", "ethereum__title_confirm_domain": "Validar dominio", "ethereum__title_confirm_message": "Validar mensaje", "ethereum__title_confirm_struct": "Validar estructura", "ethereum__title_confirm_typed_data": "Validar datos", + "ethereum__title_input_data": "Datos entrados", "ethereum__title_signing_address": "Dirección firma", "ethereum__units_template": "{0} unidades", "ethereum__unknown_token": "Token desconocido", diff --git a/core/translations/fr.json b/core/translations/fr.json index 15feb14ce64..cd8f72dc335 100644 --- a/core/translations/fr.json +++ b/core/translations/fr.json @@ -358,11 +358,11 @@ "ethereum__staking_stake_intro": "Staker de l'ETH sur Everstake ?", "ethereum__staking_unstake": "Unstake", "ethereum__staking_unstake_intro": "Terminer le Staking de l'ETH sur Everstake ?", - "ethereum__title_confirm_data": "Conf. données", "ethereum__title_confirm_domain": "Conf. domaine", "ethereum__title_confirm_message": "Conf. message", "ethereum__title_confirm_struct": "Conf. structure", "ethereum__title_confirm_typed_data": "Conf. données", + "ethereum__title_input_data": "Données entrées", "ethereum__title_signing_address": "Adr. de signature", "ethereum__units_template": "{0} unités", "ethereum__unknown_token": "Jeton inconnu", diff --git a/core/translations/it.json b/core/translations/it.json index d6a1d6c248c..09ca81db0f1 100644 --- a/core/translations/it.json +++ b/core/translations/it.json @@ -348,11 +348,11 @@ "ethereum__staking_stake_intro": "Staking di ETH su Everstake?", "ethereum__staking_unstake": "Unstaking", "ethereum__staking_unstake_intro": "Unstaking di ETH da Everstake?", - "ethereum__title_confirm_data": "Conferma dati", "ethereum__title_confirm_domain": "Conferma dom.", "ethereum__title_confirm_message": "Conferma mess.", "ethereum__title_confirm_struct": "Conferma strutt.", "ethereum__title_confirm_typed_data": "Conf. dati digitati", + "ethereum__title_input_data": "Dati input", "ethereum__title_signing_address": "Indirizzo di firma", "ethereum__units_template": "{0} unità", "ethereum__unknown_token": "Token sconosciuto", diff --git a/core/translations/order.json b/core/translations/order.json index 0b75816a2d0..369da4e5914 100644 --- a/core/translations/order.json +++ b/core/translations/order.json @@ -285,7 +285,7 @@ "283": "ethereum__show_full_message", "284": "ethereum__show_full_struct", "285": "ethereum__sign_eip712", - "286": "ethereum__title_confirm_data", + "286": "ethereum__title_input_data", "287": "ethereum__title_confirm_domain", "288": "ethereum__title_confirm_message", "289": "ethereum__title_confirm_struct", @@ -966,5 +966,10 @@ "964": "instructions__swipe_down", "965": "fido__title_credential_details", "966": "address__public_key_confirmed", - "967": "words__continue_anyway" + "967": "words__continue_anyway", + "968": "ethereum__unknown_contract_address", + "970": "ethereum__token_contract", + "971": "buttons__view_all_data", + "972": "words__view_all_data_from_menu", + "973": "ethereum__interaction_contract" } diff --git a/core/translations/pt.json b/core/translations/pt.json index 69dbe5a38c1..c7e731c032f 100644 --- a/core/translations/pt.json +++ b/core/translations/pt.json @@ -350,11 +350,11 @@ "ethereum__staking_stake_intro": "Fazer stake de ETH no Everstake?", "ethereum__staking_unstake": "Tirar do stake", "ethereum__staking_unstake_intro": "Tirar ETH do stake no Everstake?", - "ethereum__title_confirm_data": "Confirmar dados", "ethereum__title_confirm_domain": "Confirmar domínio", "ethereum__title_confirm_message": "Confirmar mensagem", "ethereum__title_confirm_struct": "Conf. estrutura", "ethereum__title_confirm_typed_data": "Conf. dados", + "ethereum__title_input_data": "Dados entrados", "ethereum__title_signing_address": "End. assinatura", "ethereum__units_template": "{0} unidades", "ethereum__unknown_token": "Token desconhecido", diff --git a/core/translations/signatures.json b/core/translations/signatures.json index 03d0cad2202..c71df2d9798 100644 --- a/core/translations/signatures.json +++ b/core/translations/signatures.json @@ -1,8 +1,8 @@ { "current": { - "merkle_root": "6eb9892932a20c647aa7f22063a003e792512daef20790cce84c1ef67aa62ad1", - "datetime": "2024-10-14T17:19:07.194969", - "commit": "7ca835a9a18fda9be9ca044378644bd5212cc33f" + "merkle_root": "7c80bd4562bdfef5c318b64fd1049d78ab9893ef0a669f87f75a7c3ae586e16d", + "datetime": "2024-10-18T13:06:13.529205", + "commit": "2d720de93efd4e5e640afdfc76189e57af723b0a" }, "history": [ { diff --git a/core/translations/tr.json b/core/translations/tr.json index 811125acc93..9f398ff7c92 100644 --- a/core/translations/tr.json +++ b/core/translations/tr.json @@ -348,11 +348,11 @@ "ethereum__staking_stake_intro": "Everstake'te ETH stake edilsin mi?", "ethereum__staking_unstake": "Unstake et", "ethereum__staking_unstake_intro": "Everstake'ten ETH unstake edilsin mi?", - "ethereum__title_confirm_data": "Veri̇leri̇ onayla", "ethereum__title_confirm_domain": "Etki̇ alanini onayla", "ethereum__title_confirm_message": "Mesaji onayla", "ethereum__title_confirm_struct": "Yapiyi onayla", "ethereum__title_confirm_typed_data": "Yzln veri̇yi̇ onayla", + "ethereum__title_input_data": "Veri̇leri̇ girdiyi", "ethereum__title_signing_address": "İmza adresi̇", "ethereum__units_template": "{0} birim", "ethereum__unknown_token": "Bilinmeyen token", diff --git a/tests/input_flows_helpers.py b/tests/input_flows_helpers.py index b03af50581f..eedd672217d 100644 --- a/tests/input_flows_helpers.py +++ b/tests/input_flows_helpers.py @@ -343,9 +343,7 @@ def __init__(self, client: Client): def confirm_data(self, info: bool = False, cancel: bool = False) -> BRGeneratorType: yield - TR.assert_equals( - self.debug.wait_layout().title(), "ethereum__title_confirm_data" - ) + TR.assert_equals(self.debug.wait_layout().title(), "ethereum__title_input_data") if info: self.debug.press_info() elif cancel: @@ -355,9 +353,7 @@ def confirm_data(self, info: bool = False, cancel: bool = False) -> BRGeneratorT def paginate_data(self) -> BRGeneratorType: br = yield - TR.assert_equals( - self.debug.wait_layout().title(), "ethereum__title_confirm_data" - ) + TR.assert_equals(self.debug.wait_layout().title(), "ethereum__title_input_data") assert br.pages is not None for i in range(br.pages): self.debug.wait_layout() @@ -367,9 +363,7 @@ def paginate_data(self) -> BRGeneratorType: def paginate_data_go_back(self) -> BRGeneratorType: br = yield - TR.assert_equals( - self.debug.wait_layout().title(), "ethereum__title_confirm_data" - ) + TR.assert_equals(self.debug.wait_layout().title(), "ethereum__title_input_data") assert br.pages is not None assert br.pages > 2 if self.client.layout_type is LayoutType.TR: