mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-12-18 04:18:10 +00:00
feat(core/ethereum): new ETH contract flow
This commit is contained in:
parent
0bacbdf355
commit
c300576d6c
1
core/.changelog.d/4251.fixed
Normal file
1
core/.changelog.d/4251.fixed
Normal file
@ -0,0 +1 @@
|
||||
New EVM call contract flow.
|
@ -129,6 +129,7 @@ static void _librust_qstrs(void) {
|
||||
MP_QSTR_button;
|
||||
MP_QSTR_button_event;
|
||||
MP_QSTR_button_request;
|
||||
MP_QSTR_button_style_confirm;
|
||||
MP_QSTR_buttons__abort;
|
||||
MP_QSTR_buttons__access;
|
||||
MP_QSTR_buttons__again;
|
||||
@ -167,6 +168,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;
|
||||
@ -218,8 +220,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;
|
||||
@ -307,6 +311,7 @@ static void _librust_qstrs(void) {
|
||||
MP_QSTR_instructions__swipe_up;
|
||||
MP_QSTR_instructions__tap_to_confirm;
|
||||
MP_QSTR_instructions__tap_to_start;
|
||||
MP_QSTR_instructions__view_all_data;
|
||||
MP_QSTR_is_type_of;
|
||||
MP_QSTR_items;
|
||||
MP_QSTR_items_title;
|
||||
@ -349,6 +354,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;
|
||||
@ -730,6 +736,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;
|
||||
@ -980,6 +987,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;
|
||||
@ -998,13 +1006,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;
|
||||
|
@ -457,7 +457,7 @@ pub enum TranslatedString {
|
||||
#[cfg(feature = "universal_fw")]
|
||||
ethereum__name_and_version = 277, // "Name and version"
|
||||
#[cfg(feature = "universal_fw")]
|
||||
ethereum__new_contract = 278, // "new contract?"
|
||||
ethereum__new_contract = 278, // "New contract will be deployed"
|
||||
#[cfg(feature = "universal_fw")]
|
||||
ethereum__no_message_field = 279, // "No message field"
|
||||
#[cfg(feature = "universal_fw")]
|
||||
@ -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"
|
||||
instructions__view_all_data = 972, // "View all data in the menu."
|
||||
#[cfg(feature = "universal_fw")]
|
||||
ethereum__interaction_contract = 973, // "Interaction contract"
|
||||
}
|
||||
|
||||
impl TranslatedString {
|
||||
@ -1822,7 +1830,7 @@ impl TranslatedString {
|
||||
#[cfg(feature = "universal_fw")]
|
||||
Self::ethereum__name_and_version => "Name and version",
|
||||
#[cfg(feature = "universal_fw")]
|
||||
Self::ethereum__new_contract => "new contract?",
|
||||
Self::ethereum__new_contract => "New contract will be deployed",
|
||||
#[cfg(feature = "universal_fw")]
|
||||
Self::ethereum__no_message_field => "No message field",
|
||||
#[cfg(feature = "universal_fw")]
|
||||
@ -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::instructions__view_all_data => "View all data in the 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_instructions__view_all_data => Some(Self::instructions__view_all_data),
|
||||
#[cfg(feature = "universal_fw")]
|
||||
Qstr::MP_QSTR_ethereum__interaction_contract => Some(Self::ethereum__interaction_contract),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,9 @@ pub enum PageMsg<T> {
|
||||
/// Cancelled using page controls.
|
||||
Cancelled,
|
||||
|
||||
/// Info button pressed
|
||||
Info,
|
||||
|
||||
/// Page component was configured to react to swipes and user swiped left.
|
||||
SwipeLeft,
|
||||
|
||||
|
@ -13,6 +13,7 @@ pub struct SwipePage<T> {
|
||||
axis: Axis,
|
||||
pages: usize,
|
||||
current: usize,
|
||||
limit: Option<usize>,
|
||||
}
|
||||
|
||||
impl<T: Component + Paginate> SwipePage<T> {
|
||||
@ -23,6 +24,7 @@ impl<T: Component + Paginate> SwipePage<T> {
|
||||
axis: Axis::Vertical,
|
||||
pages: 1,
|
||||
current: 0,
|
||||
limit: None,
|
||||
}
|
||||
}
|
||||
|
||||
@ -33,12 +35,18 @@ impl<T: Component + Paginate> SwipePage<T> {
|
||||
axis: Axis::Horizontal,
|
||||
pages: 1,
|
||||
current: 0,
|
||||
limit: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn inner(&self) -> &T {
|
||||
&self.inner
|
||||
}
|
||||
|
||||
pub fn with_limit(mut self, limit: Option<usize>) -> Self {
|
||||
self.limit = limit;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Component + Paginate> Component for SwipePage<T> {
|
||||
@ -47,6 +55,9 @@ impl<T: Component + Paginate> Component for SwipePage<T> {
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -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())
|
||||
|
@ -96,6 +96,83 @@ 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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ConfirmActionMenu {
|
||||
verb_cancel: Option<TString<'static>>,
|
||||
info: bool,
|
||||
verb_info: Option<TString<'static>>,
|
||||
}
|
||||
|
||||
impl ConfirmActionMenu {
|
||||
pub fn new(
|
||||
verb_cancel: Option<TString<'static>>,
|
||||
info: bool,
|
||||
verb_info: Option<TString<'static>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
verb_cancel,
|
||||
info,
|
||||
verb_info,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ConfirmActionStrings {
|
||||
title: TString<'static>,
|
||||
subtitle: Option<TString<'static>>,
|
||||
verb: Option<TString<'static>>,
|
||||
prompt_screen: Option<TString<'static>>,
|
||||
}
|
||||
|
||||
impl ConfirmActionStrings {
|
||||
pub fn new(
|
||||
title: TString<'static>,
|
||||
subtitle: Option<TString<'static>>,
|
||||
verb: Option<TString<'static>>,
|
||||
prompt_screen: Option<TString<'static>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
title,
|
||||
subtitle,
|
||||
verb,
|
||||
prompt_screen,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[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) }
|
||||
@ -141,35 +218,43 @@ fn new_confirm_action_obj(_args: &[Obj], kwargs: &Map) -> Result<Obj, error::Err
|
||||
|
||||
new_confirm_action_simple(
|
||||
paragraphs,
|
||||
title,
|
||||
subtitle,
|
||||
verb_cancel,
|
||||
prompt_screen.then_some(prompt_title),
|
||||
ConfirmActionMenu::new(verb_cancel, false, None),
|
||||
ConfirmActionStrings::new(title, subtitle, None, prompt_screen.then_some(prompt_title)),
|
||||
hold,
|
||||
false,
|
||||
None,
|
||||
)
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
pub fn new_confirm_action_uni<T: Component + MaybeTrace + 'static>(
|
||||
fn new_confirm_action_uni<T: Component + MaybeTrace + 'static>(
|
||||
content: T,
|
||||
title: TString<'static>,
|
||||
subtitle: Option<TString<'static>>,
|
||||
verb_cancel: Option<TString<'static>>,
|
||||
prompt_screen: Option<TString<'static>>,
|
||||
menu: ConfirmActionMenu,
|
||||
strings: ConfirmActionStrings,
|
||||
hold: bool,
|
||||
info: bool,
|
||||
default_cancel: bool,
|
||||
) -> Result<Obj, error::Error> {
|
||||
let (prompt_screen, prompt_pages, flow, page) = create_flow(title, prompt_screen, hold);
|
||||
let (prompt_screen, prompt_pages, flow, page) =
|
||||
create_flow(strings.title, strings.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)
|
||||
let mut content_intro = Frame::left_aligned(strings.title, content)
|
||||
.with_swipe(Direction::Up, SwipeSettings::default())
|
||||
.with_swipe(Direction::Left, SwipeSettings::default())
|
||||
.with_vertical_pages();
|
||||
|
||||
if let Some(subtitle) = subtitle {
|
||||
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();
|
||||
// TODO: conditionally add the verb to the footer as well?
|
||||
content_intro = content_intro.with_footer(TR::instructions__swipe_up.into(), None);
|
||||
}
|
||||
|
||||
if let Some(subtitle) = strings.subtitle {
|
||||
content_intro = content_intro.with_subtitle(subtitle);
|
||||
}
|
||||
|
||||
@ -182,13 +267,18 @@ pub fn new_confirm_action_uni<T: Component + MaybeTrace + 'static>(
|
||||
|
||||
let flow = flow?.with_page(page, content_intro)?;
|
||||
|
||||
create_menu_and_confirm(subtitle, verb_cancel, hold, info, prompt_screen, flow)
|
||||
let flow = create_menu(flow, menu, default_cancel, prompt_screen)?;
|
||||
|
||||
let flow = create_confirm(flow, strings.subtitle, hold, prompt_screen)?;
|
||||
|
||||
Ok(LayoutObj::new(flow)?.into())
|
||||
}
|
||||
|
||||
fn create_flow(
|
||||
title: TString<'static>,
|
||||
prompt_screen: Option<TString<'static>>,
|
||||
hold: bool,
|
||||
default_cancel: bool,
|
||||
) -> (
|
||||
Option<TString<'static>>,
|
||||
usize,
|
||||
@ -198,52 +288,51 @@ 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<SwipeFlow, Error>, &dyn FlowController) = if prompt_screen.is_some() {
|
||||
(SwipeFlow::new(&ConfirmAction::Intro), &ConfirmAction::Intro)
|
||||
} else if default_cancel {
|
||||
(
|
||||
SwipeFlow::new(&ConfirmActionSimpleDefaultCancel::Intro),
|
||||
&ConfirmActionSimpleDefaultCancel::Intro,
|
||||
)
|
||||
} else {
|
||||
SwipeFlow::new(&ConfirmActionSimple::Intro)
|
||||
};
|
||||
|
||||
let page: &dyn FlowController = if prompt_screen.is_some() {
|
||||
&ConfirmAction::Intro
|
||||
} else {
|
||||
&ConfirmActionSimple::Intro
|
||||
(
|
||||
SwipeFlow::new(&ConfirmActionSimple::Intro),
|
||||
&ConfirmActionSimple::Intro,
|
||||
)
|
||||
};
|
||||
|
||||
(prompt_screen, prompt_pages, flow, page)
|
||||
}
|
||||
|
||||
fn create_menu_and_confirm(
|
||||
subtitle: Option<TString<'static>>,
|
||||
verb_cancel: Option<TString<'static>>,
|
||||
hold: bool,
|
||||
info: bool,
|
||||
prompt_screen: Option<TString<'static>>,
|
||||
flow: SwipeFlow,
|
||||
) -> Result<Obj, Error> {
|
||||
let flow = create_menu(flow, verb_cancel, info, prompt_screen)?;
|
||||
|
||||
let flow = create_confirm(flow, subtitle, hold, prompt_screen)?;
|
||||
|
||||
Ok(LayoutObj::new(flow)?.into())
|
||||
}
|
||||
|
||||
fn create_menu(
|
||||
flow: SwipeFlow,
|
||||
verb_cancel: Option<TString<'static>>,
|
||||
info: bool,
|
||||
menu: ConfirmActionMenu,
|
||||
default_cancel: bool,
|
||||
prompt_screen: Option<TString<'static>>,
|
||||
) -> Result<SwipeFlow, Error> {
|
||||
let mut menu_choices = VerticalMenu::empty().danger(
|
||||
let mut menu_choices = VerticalMenu::empty();
|
||||
if default_cancel {
|
||||
menu_choices = menu_choices.item(
|
||||
theme::ICON_CANCEL,
|
||||
verb_cancel.unwrap_or(TR::buttons__cancel.into()),
|
||||
menu.verb_cancel.unwrap_or(TR::buttons__cancel.into()),
|
||||
);
|
||||
if info {
|
||||
menu_choices =
|
||||
menu_choices.danger(theme::ICON_CHEVRON_RIGHT, TR::words__continue_anyway.into());
|
||||
} else {
|
||||
menu_choices = menu_choices.danger(
|
||||
theme::ICON_CANCEL,
|
||||
menu.verb_cancel.unwrap_or(TR::buttons__cancel.into()),
|
||||
);
|
||||
if menu.info {
|
||||
menu_choices = menu_choices.item(
|
||||
theme::ICON_CHEVRON_RIGHT,
|
||||
TR::words__title_information.into(),
|
||||
menu.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());
|
||||
@ -304,20 +393,33 @@ fn create_confirm(
|
||||
#[inline(never)]
|
||||
pub fn new_confirm_action_simple<T: Component + Paginate + MaybeTrace + 'static>(
|
||||
content: T,
|
||||
title: TString<'static>,
|
||||
subtitle: Option<TString<'static>>,
|
||||
verb_cancel: Option<TString<'static>>,
|
||||
prompt_screen: Option<TString<'static>>,
|
||||
menu: ConfirmActionMenu,
|
||||
strings: ConfirmActionStrings,
|
||||
hold: bool,
|
||||
info: bool,
|
||||
page_limit: Option<usize>,
|
||||
) -> Result<Obj, error::Error> {
|
||||
new_confirm_action_uni(
|
||||
SwipeContent::new(SwipePage::vertical(content)),
|
||||
title,
|
||||
subtitle,
|
||||
verb_cancel,
|
||||
prompt_screen,
|
||||
SwipeContent::new(SwipePage::vertical(content).with_limit(page_limit)),
|
||||
menu,
|
||||
strings,
|
||||
hold,
|
||||
info,
|
||||
false,
|
||||
)
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
pub fn new_confirm_action_simple_default_cancel<T: Component + Paginate + MaybeTrace + 'static>(
|
||||
content: T,
|
||||
menu: ConfirmActionMenu,
|
||||
strings: ConfirmActionStrings,
|
||||
hold: bool,
|
||||
page_limit: Option<usize>,
|
||||
) -> Result<Obj, error::Error> {
|
||||
new_confirm_action_uni(
|
||||
SwipeContent::new(SwipePage::vertical(content).with_limit(page_limit)),
|
||||
menu,
|
||||
strings,
|
||||
hold,
|
||||
true,
|
||||
)
|
||||
}
|
||||
|
@ -19,7 +19,10 @@ 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,
|
||||
ConfirmActionMenu, ConfirmActionStrings,
|
||||
};
|
||||
#[cfg(feature = "universal_fw")]
|
||||
pub use confirm_fido::new_confirm_fido;
|
||||
pub use confirm_firmware_update::new_confirm_firmware_update;
|
||||
|
@ -50,7 +50,10 @@ 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,
|
||||
ConfirmActionMenu, ConfirmActionStrings,
|
||||
},
|
||||
theme::ICON_BULLET_CHECKMARK,
|
||||
},
|
||||
},
|
||||
@ -238,14 +241,12 @@ 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,
|
||||
Some(title),
|
||||
false,
|
||||
ConfirmActionMenu::new(None, false, None),
|
||||
ConfirmActionStrings::new(title, None, None, Some(title)),
|
||||
false,
|
||||
None,
|
||||
)
|
||||
};
|
||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||
@ -256,14 +257,18 @@ struct ConfirmBlobParams {
|
||||
subtitle: Option<TString<'static>>,
|
||||
data: Obj,
|
||||
description: Option<TString<'static>>,
|
||||
description_font: &'static TextStyle,
|
||||
extra: Option<TString<'static>>,
|
||||
verb: Option<TString<'static>>,
|
||||
verb_cancel: Option<TString<'static>>,
|
||||
verb_info: Option<TString<'static>>,
|
||||
info_button: bool,
|
||||
prompt: bool,
|
||||
hold: bool,
|
||||
chunkify: bool,
|
||||
text_mono: bool,
|
||||
page_limit: Option<usize>,
|
||||
default_cancel: bool,
|
||||
}
|
||||
|
||||
impl ConfirmBlobParams {
|
||||
@ -272,7 +277,7 @@ impl ConfirmBlobParams {
|
||||
data: Obj,
|
||||
description: Option<TString<'static>>,
|
||||
verb: Option<TString<'static>>,
|
||||
verb_cancel: Option<TString<'static>>,
|
||||
verb_info: Option<TString<'static>>,
|
||||
prompt: bool,
|
||||
hold: bool,
|
||||
) -> Self {
|
||||
@ -281,14 +286,18 @@ impl ConfirmBlobParams {
|
||||
subtitle: None,
|
||||
data,
|
||||
description,
|
||||
description_font: &theme::TEXT_NORMAL,
|
||||
extra: None,
|
||||
verb,
|
||||
verb_cancel,
|
||||
verb_cancel: None,
|
||||
verb_info,
|
||||
info_button: false,
|
||||
prompt,
|
||||
hold,
|
||||
chunkify: false,
|
||||
text_mono: true,
|
||||
page_limit: None,
|
||||
default_cancel: false,
|
||||
}
|
||||
}
|
||||
|
||||
@ -302,6 +311,11 @@ impl ConfirmBlobParams {
|
||||
self
|
||||
}
|
||||
|
||||
fn with_verb_cancel(mut self, verb_cancel: Option<TString<'static>>) -> Self {
|
||||
self.verb_cancel = verb_cancel;
|
||||
self
|
||||
}
|
||||
|
||||
fn with_info_button(mut self, info_button: bool) -> Self {
|
||||
self.info_button = info_button;
|
||||
self
|
||||
@ -317,12 +331,27 @@ impl ConfirmBlobParams {
|
||||
self
|
||||
}
|
||||
|
||||
fn with_page_limit(mut self, page_limit: Option<usize>) -> 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<Obj, Error> {
|
||||
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()?;
|
||||
@ -335,14 +364,23 @@ 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,
|
||||
ConfirmActionMenu::new(self.verb_cancel, self.info_button, self.verb_info),
|
||||
ConfirmActionStrings::new(
|
||||
self.title,
|
||||
self.subtitle,
|
||||
self.verb_cancel,
|
||||
self.verb,
|
||||
self.prompt.then_some(self.title),
|
||||
),
|
||||
self.hold,
|
||||
self.info_button,
|
||||
self.page_limit,
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -353,7 +391,17 @@ 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<TString> =
|
||||
kwargs.get(Qstr::MP_QSTR_description)?.try_into_option()?;
|
||||
let extra: Option<TString> = kwargs.get(Qstr::MP_QSTR_extra)?.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<TString> = kwargs
|
||||
.get(Qstr::MP_QSTR_extra)
|
||||
.unwrap_or_else(|_| Obj::const_none())
|
||||
.try_into_option()?;
|
||||
let subtitle: Option<TString> = kwargs
|
||||
.get(Qstr::MP_QSTR_subtitle)
|
||||
.unwrap_or_else(|_| Obj::const_none())
|
||||
.try_into_option()?;
|
||||
let verb: Option<TString> = kwargs
|
||||
.get(Qstr::MP_QSTR_verb)
|
||||
.unwrap_or_else(|_| Obj::const_none())
|
||||
@ -362,21 +410,44 @@ 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<TString> = 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<usize> = kwargs
|
||||
.get(Qstr::MP_QSTR_page_limit)
|
||||
.unwrap_or_else(|_| Obj::const_none())
|
||||
.try_into_option()?;
|
||||
|
||||
let description_font = if description_font_green {
|
||||
&theme::TEXT_SUB_GREEN_LIME
|
||||
} else {
|
||||
&theme::TEXT_NORMAL
|
||||
};
|
||||
|
||||
ConfirmBlobParams::new(
|
||||
title,
|
||||
data,
|
||||
description,
|
||||
verb,
|
||||
verb_cancel,
|
||||
verb_info,
|
||||
prompt_screen,
|
||||
hold,
|
||||
)
|
||||
.with_description_font(description_font)
|
||||
.with_text_mono(text_mono)
|
||||
.with_subtitle(subtitle)
|
||||
.with_verb_cancel(verb_cancel)
|
||||
.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) }
|
||||
@ -408,7 +479,13 @@ 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,
|
||||
ConfirmActionMenu::new(None, false, None),
|
||||
ConfirmActionStrings::new(title, None, None, None),
|
||||
false,
|
||||
None,
|
||||
)
|
||||
};
|
||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||
}
|
||||
@ -426,14 +503,12 @@ 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,
|
||||
hold.then_some(title),
|
||||
ConfirmActionMenu::new(None, false, None),
|
||||
ConfirmActionStrings::new(title, None, None, hold.then_some(title)),
|
||||
hold,
|
||||
false,
|
||||
None,
|
||||
)
|
||||
};
|
||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||
@ -457,12 +532,15 @@ extern "C" fn new_confirm_homescreen(n_args: usize, args: *const Obj, kwargs: *m
|
||||
|
||||
new_confirm_action_simple(
|
||||
paragraphs,
|
||||
ConfirmActionMenu::new(None, false, None),
|
||||
ConfirmActionStrings::new(
|
||||
TR::homescreen__settings_title.into(),
|
||||
Some(TR::homescreen__settings_subtitle.into()),
|
||||
None,
|
||||
Some(TR::homescreen__settings_title.into()),
|
||||
),
|
||||
false,
|
||||
false,
|
||||
None,
|
||||
)
|
||||
} else {
|
||||
if !check_homescreen_format(jpeg) {
|
||||
@ -540,8 +618,9 @@ 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)
|
||||
ConfirmBlobParams::new(title, value, description, verb, None, hold, hold)
|
||||
.with_subtitle(subtitle)
|
||||
.with_verb_cancel(verb_cancel)
|
||||
.with_info_button(info_button)
|
||||
.with_chunkify(chunkify)
|
||||
.with_text_mono(text_mono)
|
||||
@ -563,14 +642,12 @@ 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,
|
||||
Some(title),
|
||||
true,
|
||||
ConfirmActionMenu::new(None, true, None),
|
||||
ConfirmActionStrings::new(title, None, None, Some(title)),
|
||||
true,
|
||||
None,
|
||||
)
|
||||
};
|
||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||
@ -849,14 +926,17 @@ 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,
|
||||
ConfirmActionMenu::new(None, false, None),
|
||||
ConfirmActionStrings::new(
|
||||
TR::coinjoin__title.into(),
|
||||
None,
|
||||
None,
|
||||
Some(TR::coinjoin__title.into()),
|
||||
),
|
||||
true,
|
||||
false,
|
||||
None,
|
||||
)
|
||||
};
|
||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||
@ -1261,12 +1341,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(),
|
||||
|
@ -2,7 +2,7 @@ use crate::{
|
||||
translations::TR,
|
||||
ui::{
|
||||
component::{Child, Component, ComponentExt, Event, EventCtx, Pad, PageMsg, Paginate},
|
||||
display::Color,
|
||||
display::{Color, Font},
|
||||
geometry::{Insets, Rect},
|
||||
shape::Renderer,
|
||||
},
|
||||
@ -13,21 +13,26 @@ use super::{
|
||||
ButtonDetails, ButtonLayout, ButtonPos,
|
||||
};
|
||||
|
||||
#[derive(PartialEq)]
|
||||
enum LastPageLayout {
|
||||
Confirm,
|
||||
ArmedConfirmPlusInfo,
|
||||
}
|
||||
|
||||
pub struct ButtonPage<T>
|
||||
where
|
||||
T: Component + Paginate,
|
||||
{
|
||||
page_count: usize,
|
||||
active_page: usize,
|
||||
page_limit: Option<usize>,
|
||||
last_page_layout: LastPageLayout,
|
||||
content: Child<T>,
|
||||
pad: Pad,
|
||||
/// Left button of the first screen
|
||||
cancel_btn_details: Option<ButtonDetails>,
|
||||
/// Right button of the last screen
|
||||
confirm_btn_details: Option<ButtonDetails>,
|
||||
/// Left button of every screen
|
||||
info_btn_details: Option<ButtonDetails>,
|
||||
back_btn_details: Option<ButtonDetails>,
|
||||
/// Right button of every screen apart the last one
|
||||
next_btn_details: Option<ButtonDetails>,
|
||||
buttons: Child<ButtonController>,
|
||||
}
|
||||
@ -40,10 +45,17 @@ where
|
||||
Self {
|
||||
page_count: 0, // will be set in place()
|
||||
active_page: 0,
|
||||
page_limit: None,
|
||||
last_page_layout: LastPageLayout::Confirm,
|
||||
content: Child::new(content),
|
||||
pad: Pad::with_background(background).with_clear(),
|
||||
cancel_btn_details: Some(ButtonDetails::cancel_icon()),
|
||||
confirm_btn_details: Some(ButtonDetails::text(TR::buttons__confirm.into())),
|
||||
info_btn_details: Some(
|
||||
ButtonDetails::text("i".into())
|
||||
.with_fixed_width(theme::BUTTON_ICON_WIDTH)
|
||||
.with_font(Font::NORMAL),
|
||||
),
|
||||
back_btn_details: Some(ButtonDetails::up_arrow_icon()),
|
||||
next_btn_details: Some(ButtonDetails::down_arrow_icon_wide()),
|
||||
// Setting empty layout for now, we do not yet know the page count.
|
||||
@ -53,6 +65,12 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_armed_confirm_plus_info(mut self) -> Self {
|
||||
self.last_page_layout = LastPageLayout::ArmedConfirmPlusInfo;
|
||||
self.confirm_btn_details = Some(ButtonDetails::armed_text(TR::words__confirm.into()));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_cancel_btn(mut self, btn_details: Option<ButtonDetails>) -> Self {
|
||||
self.cancel_btn_details = btn_details;
|
||||
self
|
||||
@ -73,6 +91,11 @@ where
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_page_limit(mut self, page_limit: Option<usize>) -> Self {
|
||||
self.page_limit = page_limit;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn has_next_page(&self) -> bool {
|
||||
self.active_page < self.page_count - 1
|
||||
}
|
||||
@ -119,11 +142,11 @@ where
|
||||
|
||||
fn get_button_layout(&self, has_prev: bool, has_next: bool) -> ButtonLayout {
|
||||
let btn_left = self.get_left_button_details(!has_prev);
|
||||
let btn_middle = self.get_middle_button_details(has_next);
|
||||
let btn_right = self.get_right_button_details(has_next);
|
||||
ButtonLayout::new(btn_left, None, btn_right)
|
||||
ButtonLayout::new(btn_left, btn_middle, btn_right)
|
||||
}
|
||||
|
||||
/// Get the left button details, depending whether the page is first or not.
|
||||
fn get_left_button_details(&self, is_first: bool) -> Option<ButtonDetails> {
|
||||
if is_first {
|
||||
self.cancel_btn_details.clone()
|
||||
@ -132,13 +155,21 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the right button details, depending on whether there is a next
|
||||
/// page.
|
||||
fn get_middle_button_details(&self, has_next_page: bool) -> Option<ButtonDetails> {
|
||||
if has_next_page || self.last_page_layout == LastPageLayout::Confirm {
|
||||
None
|
||||
} else {
|
||||
self.confirm_btn_details.clone()
|
||||
}
|
||||
}
|
||||
|
||||
fn get_right_button_details(&self, has_next_page: bool) -> Option<ButtonDetails> {
|
||||
if has_next_page {
|
||||
self.next_btn_details.clone()
|
||||
} else {
|
||||
} else if self.last_page_layout == LastPageLayout::Confirm {
|
||||
self.confirm_btn_details.clone()
|
||||
} else {
|
||||
self.info_btn_details.clone()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -172,6 +203,10 @@ where
|
||||
// Need to be called here, only after content is placed
|
||||
// and we can calculate the page count.
|
||||
self.page_count = self.content.page_count();
|
||||
if let Some(limit) = self.page_limit {
|
||||
self.page_count = self.page_count.min(limit);
|
||||
}
|
||||
|
||||
self.set_buttons_for_initial_page(self.page_count);
|
||||
self.buttons.place(button_area);
|
||||
bounds
|
||||
@ -191,17 +226,25 @@ where
|
||||
return Some(PageMsg::Cancelled);
|
||||
}
|
||||
}
|
||||
ButtonPos::Middle => {
|
||||
return Some(PageMsg::Confirmed);
|
||||
}
|
||||
ButtonPos::Right => {
|
||||
if self.has_next_page() {
|
||||
// Clicked NEXT. Scroll down.
|
||||
self.go_to_next_page();
|
||||
self.change_page(ctx);
|
||||
} else {
|
||||
// Clicked CONFIRM. Send result.
|
||||
match self.last_page_layout {
|
||||
LastPageLayout::Confirm => {
|
||||
return Some(PageMsg::Confirmed);
|
||||
}
|
||||
LastPageLayout::ArmedConfirmPlusInfo => {
|
||||
return Some(PageMsg::Info);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -94,6 +94,7 @@ where
|
||||
match msg {
|
||||
PageMsg::Confirmed => Ok(CONFIRMED.as_obj()),
|
||||
PageMsg::Cancelled => Ok(CANCELLED.as_obj()),
|
||||
PageMsg::Info => Ok(INFO.as_obj()),
|
||||
_ => Err(Error::TypeError),
|
||||
}
|
||||
}
|
||||
@ -243,7 +244,9 @@ fn content_in_button_page<T: Component + Paginate + MaybeTrace + 'static>(
|
||||
content: T,
|
||||
verb: TString<'static>,
|
||||
verb_cancel: Option<TString<'static>>,
|
||||
info: bool,
|
||||
hold: bool,
|
||||
page_limit: Option<usize>,
|
||||
) -> Result<Obj, Error> {
|
||||
// Left button - icon, text or nothing.
|
||||
let cancel_btn = verb_cancel.map(ButtonDetails::from_text_possible_icon);
|
||||
@ -259,10 +262,15 @@ fn content_in_button_page<T: Component + Paginate + MaybeTrace + 'static>(
|
||||
confirm_btn = confirm_btn.map(|btn| btn.with_default_duration());
|
||||
}
|
||||
|
||||
let content = ButtonPage::new(content, theme::BG)
|
||||
let mut content = ButtonPage::new(content, theme::BG)
|
||||
.with_cancel_btn(cancel_btn)
|
||||
.with_page_limit(page_limit)
|
||||
.with_confirm_btn(confirm_btn);
|
||||
|
||||
if info {
|
||||
content = content.with_armed_confirm_plus_info();
|
||||
}
|
||||
|
||||
let mut frame = ScrollableFrame::new(content);
|
||||
if !title.is_empty() {
|
||||
frame = frame.with_title(title);
|
||||
@ -303,7 +311,7 @@ extern "C" fn new_confirm_action(n_args: usize, args: *const Obj, kwargs: *mut M
|
||||
paragraphs.into_paragraphs()
|
||||
};
|
||||
|
||||
content_in_button_page(title, paragraphs, verb, verb_cancel, hold)
|
||||
content_in_button_page(title, paragraphs, verb, verb_cancel, false, hold, None)
|
||||
};
|
||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||
}
|
||||
@ -314,15 +322,25 @@ 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<TString> =
|
||||
kwargs.get(Qstr::MP_QSTR_description)?.try_into_option()?;
|
||||
let extra: Option<TString> = kwargs.get(Qstr::MP_QSTR_extra)?.try_into_option()?;
|
||||
let verb: TString<'static> =
|
||||
kwargs.get_or(Qstr::MP_QSTR_verb, TR::buttons__confirm.into())?;
|
||||
let verb_cancel: Option<TString<'static>> = kwargs
|
||||
let extra: Option<TString> = kwargs
|
||||
.get(Qstr::MP_QSTR_extra)
|
||||
.unwrap_or_else(|_| Obj::const_none())
|
||||
.try_into_option()?;
|
||||
let verb: Option<TString> = kwargs
|
||||
.get(Qstr::MP_QSTR_verb)
|
||||
.unwrap_or_else(|_| Obj::const_none())
|
||||
.try_into_option()?;
|
||||
let verb_cancel: Option<TString> = kwargs
|
||||
.get(Qstr::MP_QSTR_verb_cancel)
|
||||
.unwrap_or_else(|_| Obj::const_none())
|
||||
.try_into_option()?;
|
||||
let info: bool = kwargs.get_or(Qstr::MP_QSTR_info, false)?;
|
||||
let hold: bool = kwargs.get_or(Qstr::MP_QSTR_hold, false)?;
|
||||
let chunkify: bool = kwargs.get_or(Qstr::MP_QSTR_chunkify, false)?;
|
||||
let page_limit: Option<usize> = kwargs
|
||||
.get(Qstr::MP_QSTR_page_limit)
|
||||
.unwrap_or_else(|_| Obj::const_none())
|
||||
.try_into_option()?;
|
||||
|
||||
let style = if chunkify {
|
||||
// Chunkifying the address into smaller pieces when requested
|
||||
@ -341,7 +359,15 @@ extern "C" fn new_confirm_blob(n_args: usize, args: *const Obj, kwargs: *mut Map
|
||||
}
|
||||
.into_paragraphs();
|
||||
|
||||
content_in_button_page(title, paragraphs, verb, verb_cancel, hold)
|
||||
content_in_button_page(
|
||||
title,
|
||||
paragraphs,
|
||||
verb.unwrap_or(TR::buttons__confirm.into()),
|
||||
verb_cancel,
|
||||
info,
|
||||
hold,
|
||||
page_limit,
|
||||
)
|
||||
};
|
||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||
}
|
||||
@ -393,7 +419,9 @@ extern "C" fn new_confirm_properties(n_args: usize, args: *const Obj, kwargs: *m
|
||||
paragraphs.into_paragraphs(),
|
||||
button_text,
|
||||
Some("".into()),
|
||||
false,
|
||||
hold,
|
||||
None,
|
||||
)
|
||||
};
|
||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||
@ -423,7 +451,15 @@ extern "C" fn new_confirm_reset_device(n_args: usize, args: *const Obj, kwargs:
|
||||
.text_bold(TR::reset__tos_link);
|
||||
let formatted = FormattedText::new(ops).vertically_centered();
|
||||
|
||||
content_in_button_page(title, formatted, button, Some("".into()), false)
|
||||
content_in_button_page(
|
||||
title,
|
||||
formatted,
|
||||
button,
|
||||
Some("".into()),
|
||||
false,
|
||||
false,
|
||||
None,
|
||||
)
|
||||
};
|
||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||
}
|
||||
@ -505,7 +541,9 @@ extern "C" fn new_confirm_value(n_args: usize, args: *const Obj, kwargs: *mut Ma
|
||||
paragraphs,
|
||||
verb.unwrap_or(TR::buttons__confirm.into()),
|
||||
Some("".into()),
|
||||
false,
|
||||
hold,
|
||||
None,
|
||||
)
|
||||
};
|
||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||
@ -528,7 +566,9 @@ extern "C" fn new_confirm_joint_total(n_args: usize, args: *const Obj, kwargs: *
|
||||
paragraphs,
|
||||
TR::buttons__hold_to_confirm.into(),
|
||||
Some("".into()),
|
||||
false,
|
||||
true,
|
||||
None,
|
||||
)
|
||||
};
|
||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||
@ -559,6 +599,8 @@ extern "C" fn new_confirm_modify_output(n_args: usize, args: *const Obj, kwargs:
|
||||
TR::buttons__confirm.into(),
|
||||
Some("".into()),
|
||||
false,
|
||||
false,
|
||||
None,
|
||||
)
|
||||
};
|
||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||
@ -932,6 +974,8 @@ extern "C" fn new_confirm_modify_fee(n_args: usize, args: *const Obj, kwargs: *m
|
||||
TR::buttons__confirm.into(),
|
||||
Some("".into()),
|
||||
false,
|
||||
false,
|
||||
None,
|
||||
)
|
||||
};
|
||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||
@ -1215,6 +1259,8 @@ extern "C" fn new_confirm_more(n_args: usize, args: *const Obj, kwargs: *mut Map
|
||||
button,
|
||||
Some("<".into()),
|
||||
false,
|
||||
false,
|
||||
None,
|
||||
)
|
||||
};
|
||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||
@ -1240,7 +1286,9 @@ extern "C" fn new_confirm_coinjoin(n_args: usize, args: *const Obj, kwargs: *mut
|
||||
paragraphs,
|
||||
TR::buttons__hold_to_confirm.into(),
|
||||
None,
|
||||
false,
|
||||
true,
|
||||
None,
|
||||
)
|
||||
};
|
||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||
@ -1436,6 +1484,8 @@ extern "C" fn new_confirm_recovery(n_args: usize, args: *const Obj, kwargs: *mut
|
||||
button,
|
||||
Some("".into()),
|
||||
false,
|
||||
false,
|
||||
None,
|
||||
)
|
||||
};
|
||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||
@ -1488,6 +1538,8 @@ extern "C" fn new_show_group_share_success(
|
||||
TR::buttons__continue.into(),
|
||||
None,
|
||||
false,
|
||||
false,
|
||||
None,
|
||||
)
|
||||
};
|
||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||
@ -1685,12 +1737,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(),
|
||||
|
@ -30,6 +30,7 @@ pub struct ButtonPage<T> {
|
||||
/// Swipe controller.
|
||||
swipe: Swipe,
|
||||
scrollbar: ScrollBar,
|
||||
page_limit: Option<usize>,
|
||||
/// Hold-to-confirm mode whenever this is `Some(loader)`.
|
||||
loader: Option<Loader>,
|
||||
button_cancel: Option<Button>,
|
||||
@ -71,6 +72,7 @@ where
|
||||
pad: Pad::with_background(background),
|
||||
swipe: Swipe::new(),
|
||||
scrollbar: ScrollBar::vertical(),
|
||||
page_limit: None,
|
||||
loader: None,
|
||||
button_cancel: Some(Button::with_icon(theme::ICON_CANCEL)),
|
||||
button_confirm: Button::with_icon(theme::ICON_CONFIRM).styled(theme::button_confirm()),
|
||||
@ -110,6 +112,11 @@ where
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_page_limit(mut self, page_limit: Option<usize>) -> Self {
|
||||
self.page_limit = page_limit;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_back_button(mut self) -> Self {
|
||||
self.cancel_from_any_page = true;
|
||||
self.button_prev = Button::with_icon(theme::ICON_BACK).initially_enabled(false);
|
||||
@ -328,6 +335,11 @@ where
|
||||
count // Content fits on a single page.
|
||||
}
|
||||
};
|
||||
let page_count = if let Some(limit) = self.page_limit {
|
||||
page_count.min(limit)
|
||||
} else {
|
||||
page_count
|
||||
};
|
||||
|
||||
if page_count == 1 && self.button_cancel.is_none() {
|
||||
self.button_confirm.place(layout.button_both);
|
||||
|
@ -198,6 +198,7 @@ where
|
||||
PageMsg::Content(_) => Err(Error::TypeError),
|
||||
PageMsg::Confirmed => Ok(CONFIRMED.as_obj()),
|
||||
PageMsg::Cancelled => Ok(CANCELLED.as_obj()),
|
||||
PageMsg::Info => Ok(INFO.as_obj()),
|
||||
PageMsg::SwipeLeft => Ok(INFO.as_obj()),
|
||||
PageMsg::SwipeRight => Ok(CANCELLED.as_obj()),
|
||||
}
|
||||
@ -412,6 +413,7 @@ struct ConfirmBlobParams {
|
||||
hold: bool,
|
||||
chunkify: bool,
|
||||
text_mono: bool,
|
||||
page_limit: Option<usize>,
|
||||
}
|
||||
|
||||
impl ConfirmBlobParams {
|
||||
@ -435,6 +437,7 @@ impl ConfirmBlobParams {
|
||||
hold,
|
||||
chunkify: false,
|
||||
text_mono: true,
|
||||
page_limit: None,
|
||||
}
|
||||
}
|
||||
|
||||
@ -463,6 +466,11 @@ impl ConfirmBlobParams {
|
||||
self
|
||||
}
|
||||
|
||||
fn with_page_limit(mut self, page_limit: Option<usize>) -> Self {
|
||||
self.page_limit = page_limit;
|
||||
self
|
||||
}
|
||||
|
||||
fn into_layout(self) -> Result<Obj, Error> {
|
||||
let paragraphs = ConfirmBlob {
|
||||
description: self.description.unwrap_or("".into()),
|
||||
@ -488,10 +496,12 @@ impl ConfirmBlobParams {
|
||||
if self.hold {
|
||||
page = page.with_hold()?
|
||||
}
|
||||
page = page.with_page_limit(self.page_limit);
|
||||
let mut frame = Frame::left_aligned(theme::label_title(), self.title, page);
|
||||
if let Some(subtitle) = self.subtitle {
|
||||
frame = frame.with_subtitle(theme::label_subtitle(), subtitle);
|
||||
}
|
||||
|
||||
if self.info_button {
|
||||
frame = frame.with_info_button();
|
||||
}
|
||||
@ -506,7 +516,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<TString> =
|
||||
kwargs.get(Qstr::MP_QSTR_description)?.try_into_option()?;
|
||||
let extra: Option<TString> = kwargs.get(Qstr::MP_QSTR_extra)?.try_into_option()?;
|
||||
let text_mono: bool = kwargs.get_or(Qstr::MP_QSTR_text_mono, true)?;
|
||||
let extra: Option<TString> = kwargs
|
||||
.get(Qstr::MP_QSTR_extra)
|
||||
.unwrap_or_else(|_| Obj::const_none())
|
||||
.try_into_option()?;
|
||||
let verb: Option<TString> = kwargs
|
||||
.get(Qstr::MP_QSTR_verb)
|
||||
.unwrap_or_else(|_| Obj::const_none())
|
||||
@ -515,12 +529,20 @@ 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 info: bool = kwargs.get_or(Qstr::MP_QSTR_info, false)?;
|
||||
let hold: bool = kwargs.get_or(Qstr::MP_QSTR_hold, false)?;
|
||||
let chunkify: bool = kwargs.get_or(Qstr::MP_QSTR_chunkify, false)?;
|
||||
let page_limit: Option<usize> = kwargs
|
||||
.get(Qstr::MP_QSTR_page_limit)
|
||||
.unwrap_or_else(|_| Obj::const_none())
|
||||
.try_into_option()?;
|
||||
|
||||
ConfirmBlobParams::new(title, data, description, verb, verb_cancel, hold)
|
||||
.with_text_mono(text_mono)
|
||||
.with_extra(extra)
|
||||
.with_chunkify(chunkify)
|
||||
.with_info_button(info)
|
||||
.with_page_limit(page_limit)
|
||||
.into_layout()
|
||||
};
|
||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||
@ -1139,6 +1161,8 @@ extern "C" fn new_confirm_more(n_args: usize, args: *const Obj, kwargs: *mut Map
|
||||
let block = move |_args: &[Obj], kwargs: &Map| {
|
||||
let title: TString = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
|
||||
let button: TString = kwargs.get(Qstr::MP_QSTR_button)?.try_into()?;
|
||||
let button_style_confirm: bool =
|
||||
kwargs.get_or(Qstr::MP_QSTR_button_style_confirm, false)?;
|
||||
let items: Obj = kwargs.get(Qstr::MP_QSTR_items)?;
|
||||
|
||||
let mut paragraphs = ParagraphVecLong::new();
|
||||
@ -1155,7 +1179,11 @@ extern "C" fn new_confirm_more(n_args: usize, args: *const Obj, kwargs: *mut Map
|
||||
title,
|
||||
ButtonPage::new(paragraphs.into_paragraphs(), theme::BG)
|
||||
.with_cancel_confirm(None, Some(button))
|
||||
.with_confirm_style(theme::button_default())
|
||||
.with_confirm_style(if button_style_confirm {
|
||||
theme::button_confirm()
|
||||
} else {
|
||||
theme::button_default()
|
||||
})
|
||||
.with_back_button(),
|
||||
))?;
|
||||
Ok(obj.into())
|
||||
@ -1762,12 +1790,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(),
|
||||
@ -1959,6 +1994,7 @@ pub static mp_module_trezorui2: Module = obj_module! {
|
||||
/// *,
|
||||
/// title: str,
|
||||
/// button: str,
|
||||
/// button_style_confirm: bool = False,
|
||||
/// items: Iterable[tuple[int, str | bytes]],
|
||||
/// ) -> LayoutObj[UiResult]:
|
||||
/// """Confirm long content with the possibility to go back from any page.
|
||||
|
@ -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."""
|
||||
|
||||
@ -675,12 +682,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."""
|
||||
|
||||
@ -1230,12 +1244,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."""
|
||||
|
||||
@ -1444,6 +1465,7 @@ def confirm_more(
|
||||
*,
|
||||
title: str,
|
||||
button: str,
|
||||
button_style_confirm: bool = False,
|
||||
items: Iterable[tuple[int, str | bytes]],
|
||||
) -> LayoutObj[UiResult]:
|
||||
"""Confirm long content with the possibility to go back from any page.
|
||||
|
@ -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,9 +299,10 @@ 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?"
|
||||
ethereum__new_contract: str = "New contract will be deployed"
|
||||
ethereum__no_message_field: str = "No message field"
|
||||
ethereum__priority_fee: str = "Priority fee"
|
||||
ethereum__show_full_array: str = "Show full array"
|
||||
@ -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?"
|
||||
@ -396,6 +400,7 @@ class TR:
|
||||
instructions__swipe_up: str = "Swipe up"
|
||||
instructions__tap_to_confirm: str = "Tap to confirm"
|
||||
instructions__tap_to_start: str = "Tap to start"
|
||||
instructions__view_all_data: str = "View all data in the menu."
|
||||
joint__title: str = "Joint transaction"
|
||||
joint__to_the_total_amount: str = "To the total amount:"
|
||||
joint__you_are_contributing: str = "You are contributing:"
|
||||
|
@ -4,6 +4,7 @@ from trezor import TR, ui
|
||||
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,18 +36,13 @@ 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
|
||||
|
||||
if to_bytes:
|
||||
to_str = address_from_bytes(to_bytes, network)
|
||||
else:
|
||||
to_str = TR.ethereum__new_contract
|
||||
chunkify = False
|
||||
|
||||
to_str = address_from_bytes(to_bytes, network) if to_bytes else None
|
||||
total_amount = format_ethereum_amount(value, token, network)
|
||||
|
||||
account, account_path = get_account_and_path(address_n)
|
||||
|
||||
await confirm_ethereum_tx(
|
||||
@ -56,6 +52,7 @@ async def require_confirm_tx(
|
||||
account_path,
|
||||
maximum_fee,
|
||||
fee_info_items,
|
||||
is_contract_interaction,
|
||||
chunkify=chunkify,
|
||||
)
|
||||
|
||||
@ -145,17 +142,26 @@ 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, show_warning
|
||||
|
||||
await show_warning(
|
||||
"unknown_contract_warning",
|
||||
TR.ethereum__unknown_contract_address,
|
||||
default_cancel=True,
|
||||
verb_cancel=TR.send__cancel_sign,
|
||||
br_code=ButtonRequestType.Other,
|
||||
)
|
||||
|
||||
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,
|
||||
verb=TR.buttons__continue,
|
||||
br_name="unknown_token",
|
||||
br_code=ButtonRequestType.SignTx,
|
||||
)
|
||||
|
||||
@ -174,13 +180,14 @@ 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=TR.buttons__confirm,
|
||||
verb_cancel=TR.send__cancel_sign,
|
||||
br_code=ButtonRequestType.SignTx,
|
||||
ask_pagination=True,
|
||||
)
|
||||
|
||||
|
||||
@ -301,7 +308,6 @@ async def confirm_typed_value(
|
||||
title,
|
||||
data,
|
||||
description,
|
||||
ask_pagination=True,
|
||||
)
|
||||
else:
|
||||
await confirm_text(
|
||||
|
@ -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,11 @@ 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:
|
||||
is_contract_interaction = token is None and data_total_len > 0
|
||||
|
||||
if is_contract_interaction:
|
||||
await require_confirm_other_data(msg.data_initial_chunk, data_total_len)
|
||||
|
||||
await require_confirm_tx(
|
||||
@ -143,7 +145,8 @@ async def confirm_tx_data(
|
||||
fee_items,
|
||||
defs.network,
|
||||
token,
|
||||
bool(msg.chunkify),
|
||||
is_contract_interaction=is_contract_interaction,
|
||||
chunkify=bool(msg.chunkify),
|
||||
)
|
||||
|
||||
|
||||
@ -189,7 +192,7 @@ async def handle_staking(
|
||||
return False
|
||||
|
||||
|
||||
async def handle_erc20_transfer(
|
||||
async def _handle_erc20_transfer(
|
||||
msg: MsgInSignTx,
|
||||
definitions: Definitions,
|
||||
address_bytes: bytes,
|
||||
|
@ -31,10 +31,9 @@ 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),
|
||||
"",
|
||||
chunkify=False,
|
||||
)
|
||||
# END require_confirm_ecdh_session_key
|
||||
|
||||
|
@ -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,
|
||||
)
|
||||
|
@ -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
|
||||
|
@ -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",
|
||||
)
|
||||
|
@ -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,
|
||||
)
|
||||
|
||||
|
||||
|
@ -592,9 +592,26 @@ def show_warning(
|
||||
content: str,
|
||||
subheader: str | None = None,
|
||||
button: str | None = None,
|
||||
default_cancel: bool = False,
|
||||
verb_cancel: str | None = None,
|
||||
br_code: ButtonRequestType = ButtonRequestType.Warning,
|
||||
) -> Awaitable[None]:
|
||||
button = button or TR.buttons__continue # def_arg
|
||||
if default_cancel:
|
||||
# a kind of warning which makes it easy (swipe up) to cancel
|
||||
# and makes it harder to continue
|
||||
return confirm_blob(
|
||||
br_name,
|
||||
TR.words__warning,
|
||||
content,
|
||||
text_mono=False,
|
||||
verb_cancel=verb_cancel,
|
||||
default_cancel=True,
|
||||
prompt_screen=False,
|
||||
br_code=br_code,
|
||||
)
|
||||
else:
|
||||
# traditional warning
|
||||
return raise_if_not_confirmed(
|
||||
interact(
|
||||
RustLayout(
|
||||
@ -750,43 +767,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)],
|
||||
br_name=br_name,
|
||||
br_code=br_code,
|
||||
subtitle: str | None = None,
|
||||
verb: str | None = None,
|
||||
verb_cancel: str | None = None,
|
||||
br_code: ButtonRequestType = BR_CODE_OTHER,
|
||||
chunkify: bool = False,
|
||||
):
|
||||
return
|
||||
|
||||
if paginated is None:
|
||||
paginated = RustLayout(
|
||||
trezorui2.confirm_more(
|
||||
# show first page first
|
||||
layout = RustLayout(
|
||||
trezorui2.confirm_blob(
|
||||
title=title,
|
||||
button=TR.buttons__close,
|
||||
items=[(ui.MONO, data)],
|
||||
data=data,
|
||||
description=TR.instructions__view_all_data,
|
||||
description_font_green=True,
|
||||
subtitle=subtitle,
|
||||
verb=verb,
|
||||
verb_cancel=verb_cancel,
|
||||
verb_info=TR.buttons__view_all_data,
|
||||
info=True,
|
||||
hold=False,
|
||||
chunkify=chunkify,
|
||||
prompt_screen=False,
|
||||
page_limit=1,
|
||||
)
|
||||
)
|
||||
else:
|
||||
paginated.request_complete_repaint()
|
||||
|
||||
result = await interact(paginated, br_name, br_code)
|
||||
assert result in (CONFIRMED, CANCELLED)
|
||||
|
||||
assert False
|
||||
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=None,
|
||||
verb_cancel=verb_cancel,
|
||||
info=False,
|
||||
hold=False,
|
||||
br_code=br_code,
|
||||
chunkify=chunkify,
|
||||
prompt_screen=False,
|
||||
)
|
||||
elif result is not CONFIRMED:
|
||||
raise ActionCancelled
|
||||
|
||||
|
||||
def confirm_blob(
|
||||
@ -794,33 +824,33 @@ 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,
|
||||
extra=None,
|
||||
hold=hold,
|
||||
description=description,
|
||||
text_mono=text_mono,
|
||||
subtitle=subtitle,
|
||||
verb=verb,
|
||||
verb_cancel=verb_cancel,
|
||||
info=info,
|
||||
hold=hold,
|
||||
chunkify=chunkify,
|
||||
prompt_screen=prompt_screen,
|
||||
default_cancel=default_cancel,
|
||||
)
|
||||
)
|
||||
|
||||
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,
|
||||
@ -833,7 +863,10 @@ def confirm_blob(
|
||||
def confirm_address(
|
||||
title: str,
|
||||
address: str,
|
||||
subtitle: str | None = None,
|
||||
description: str | None = None,
|
||||
verb: str | None = None,
|
||||
chunkify: bool = True,
|
||||
br_name: str = "confirm_address",
|
||||
br_code: ButtonRequestType = BR_CODE_OTHER,
|
||||
) -> Awaitable[None]:
|
||||
@ -843,7 +876,9 @@ def confirm_address(
|
||||
description or "",
|
||||
br_name,
|
||||
br_code,
|
||||
verb=TR.buttons__confirm,
|
||||
subtitle=subtitle,
|
||||
verb=(verb or TR.buttons__confirm),
|
||||
chunkify=chunkify,
|
||||
)
|
||||
|
||||
|
||||
@ -893,6 +928,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 +958,7 @@ def confirm_value(
|
||||
verb=verb,
|
||||
hold=hold,
|
||||
info_button=bool(info_items),
|
||||
chunkify=chunkify,
|
||||
text_mono=value_text_mono,
|
||||
)
|
||||
),
|
||||
@ -1033,12 +1070,13 @@ def _confirm_summary(
|
||||
if not utils.BITCOIN_ONLY:
|
||||
|
||||
async def confirm_ethereum_tx(
|
||||
recipient: str,
|
||||
recipient: str | None,
|
||||
total_amount: str,
|
||||
account: str | None,
|
||||
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,
|
||||
@ -1047,10 +1085,14 @@ if not utils.BITCOIN_ONLY:
|
||||
RustLayout(
|
||||
trezorui2.flow_confirm_output(
|
||||
title=TR.words__address,
|
||||
subtitle=TR.words__recipient,
|
||||
message=recipient,
|
||||
subtitle=(
|
||||
TR.words__recipient
|
||||
if not is_contract_interaction
|
||||
else TR.ethereum__interaction_contract
|
||||
),
|
||||
message=(recipient or TR.ethereum__new_contract),
|
||||
amount=None,
|
||||
chunkify=chunkify,
|
||||
chunkify=(chunkify if recipient else False),
|
||||
text_mono=True,
|
||||
account=account,
|
||||
account_path=account_path,
|
||||
@ -1197,7 +1239,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,
|
||||
)
|
||||
|
||||
@ -1215,7 +1257,6 @@ async def confirm_modify_output(
|
||||
verb=TR.buttons__continue,
|
||||
verb_cancel=None,
|
||||
description=f"{TR.words__address}:",
|
||||
extra=None,
|
||||
)
|
||||
)
|
||||
modify_layout = RustLayout(
|
||||
@ -1381,7 +1422,6 @@ async def confirm_signverify(
|
||||
title=TR.sign_message__confirm_message,
|
||||
description=None,
|
||||
data=message,
|
||||
extra=None,
|
||||
hold=not verify,
|
||||
verb=TR.buttons__confirm if verify else None,
|
||||
)
|
||||
|
@ -725,6 +725,8 @@ def show_warning(
|
||||
content: str,
|
||||
subheader: str | None = None,
|
||||
button: str | None = None,
|
||||
default_cancel: bool = False, # NB: model R does not implement "default_cancel"-style warnings
|
||||
verb_cancel: str | None = None,
|
||||
br_code: ButtonRequestType = ButtonRequestType.Warning,
|
||||
) -> Awaitable[None]:
|
||||
from trezor import translations
|
||||
@ -901,18 +903,86 @@ async def should_show_more(
|
||||
raise ActionCancelled
|
||||
|
||||
|
||||
async def confirm_blob_with_optional_pagination(
|
||||
br_name: str,
|
||||
title: str,
|
||||
data: bytes | str,
|
||||
subtitle: str | None = None,
|
||||
verb: str | None = None,
|
||||
verb_cancel: str | None = None,
|
||||
br_code: ButtonRequestType = BR_CODE_OTHER,
|
||||
chunkify: bool = False,
|
||||
):
|
||||
while True:
|
||||
# show first page first
|
||||
layout = RustLayout(
|
||||
trezorui2.confirm_blob(
|
||||
title=title,
|
||||
data=data,
|
||||
description=None,
|
||||
subtitle=subtitle,
|
||||
verb=verb,
|
||||
verb_cancel="",
|
||||
verb_info=TR.buttons__view_all_data,
|
||||
info=True,
|
||||
hold=False,
|
||||
chunkify=chunkify,
|
||||
prompt_screen=False,
|
||||
page_limit=1,
|
||||
)
|
||||
)
|
||||
result = await interact(
|
||||
layout,
|
||||
br_name,
|
||||
br_code,
|
||||
)
|
||||
if result is CONFIRMED:
|
||||
break
|
||||
elif result is INFO:
|
||||
# user requested to view the whole blob
|
||||
layout_whole = RustLayout(
|
||||
trezorui2.confirm_blob(
|
||||
title=title,
|
||||
description=None,
|
||||
data=data,
|
||||
verb=verb,
|
||||
verb_cancel="<",
|
||||
hold=False,
|
||||
chunkify=chunkify,
|
||||
)
|
||||
)
|
||||
result_whole = await interact(
|
||||
layout_whole,
|
||||
br_name,
|
||||
br_code,
|
||||
)
|
||||
if result_whole is CONFIRMED:
|
||||
break
|
||||
elif result_whole is CANCELLED:
|
||||
# CANCELED seeing all pages => back to showing only the first page
|
||||
continue
|
||||
else:
|
||||
raise ActionCancelled
|
||||
else:
|
||||
raise ActionCancelled
|
||||
|
||||
|
||||
def confirm_blob(
|
||||
br_name: str,
|
||||
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 = "", # icon
|
||||
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,
|
||||
ask_pagination: bool = False,
|
||||
) -> Awaitable[None]:
|
||||
verb = verb or TR.buttons__confirm # def_arg
|
||||
layout = RustLayout(
|
||||
@ -920,7 +990,6 @@ def confirm_blob(
|
||||
title=title,
|
||||
description=description,
|
||||
data=data,
|
||||
extra=None,
|
||||
verb=verb,
|
||||
verb_cancel=verb_cancel,
|
||||
hold=hold,
|
||||
@ -991,13 +1060,16 @@ async def _confirm_ask_pagination(
|
||||
def confirm_address(
|
||||
title: str,
|
||||
address: str,
|
||||
subtitle: str | None = None,
|
||||
description: str | None = None,
|
||||
verb: str | None = None,
|
||||
chunkify: bool = True,
|
||||
br_name: str = "confirm_address",
|
||||
br_code: ButtonRequestType = BR_CODE_OTHER,
|
||||
) -> Awaitable[None]:
|
||||
return confirm_blob(
|
||||
br_name,
|
||||
title,
|
||||
subtitle or title,
|
||||
address,
|
||||
description,
|
||||
br_code=br_code,
|
||||
@ -1139,7 +1211,6 @@ async def confirm_value(
|
||||
title=info_title,
|
||||
data=info_value,
|
||||
description=description,
|
||||
extra=None,
|
||||
verb="",
|
||||
verb_cancel="<",
|
||||
hold=False,
|
||||
@ -1298,12 +1369,13 @@ if not utils.BITCOIN_ONLY:
|
||||
)
|
||||
|
||||
async def confirm_ethereum_tx(
|
||||
recipient: str,
|
||||
recipient: str | None,
|
||||
total_amount: str,
|
||||
_account: str | None,
|
||||
_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,
|
||||
@ -1319,14 +1391,19 @@ if not utils.BITCOIN_ONLY:
|
||||
)
|
||||
)
|
||||
|
||||
if not is_contract_interaction:
|
||||
title = TR.words__recipient
|
||||
else:
|
||||
title = TR.ethereum__interaction_contract if recipient else ""
|
||||
|
||||
while True:
|
||||
# Allowing going back and forth between recipient and summary/details
|
||||
await confirm_blob(
|
||||
br_name,
|
||||
TR.words__recipient,
|
||||
recipient,
|
||||
title,
|
||||
recipient or TR.ethereum__new_contract,
|
||||
verb=TR.buttons__continue,
|
||||
chunkify=chunkify,
|
||||
chunkify=(chunkify if recipient else False),
|
||||
)
|
||||
|
||||
try:
|
||||
@ -1397,9 +1474,7 @@ async def confirm_modify_output(
|
||||
title=TR.modify_amount__title,
|
||||
data=address,
|
||||
verb=TR.buttons__continue,
|
||||
verb_cancel=None,
|
||||
description=f"{TR.words__address}:",
|
||||
extra=None,
|
||||
)
|
||||
)
|
||||
modify_layout = RustLayout(
|
||||
@ -1510,7 +1585,6 @@ async def confirm_signverify(
|
||||
verb=TR.buttons__continue,
|
||||
br_code=BR_CODE_OTHER,
|
||||
)
|
||||
|
||||
try:
|
||||
await confirm_blob(
|
||||
br_name,
|
||||
@ -1658,7 +1732,7 @@ def pin_mismatch_popup(is_wipe_code: bool = False) -> Awaitable[None]:
|
||||
description,
|
||||
TR.pin__please_check_again,
|
||||
TR.buttons__check_again,
|
||||
BR_CODE_OTHER,
|
||||
br_code=BR_CODE_OTHER,
|
||||
)
|
||||
|
||||
|
||||
|
@ -153,4 +153,4 @@ async def show_recovery_warning(
|
||||
br_code: ButtonRequestType = ButtonRequestType.Warning,
|
||||
) -> None:
|
||||
button = button or TR.buttons__try_again # def_arg
|
||||
await show_warning(br_name, content, subheader, button, br_code)
|
||||
await show_warning(br_name, content, subheader, button, br_code=br_code)
|
||||
|
@ -276,7 +276,7 @@ async def show_warning_backup() -> None:
|
||||
TR.words__title_remember,
|
||||
TR.reset__never_make_digital_copy,
|
||||
TR.buttons__ok_i_understand,
|
||||
ButtonRequestType.ResetDevice,
|
||||
br_code=ButtonRequestType.ResetDevice,
|
||||
)
|
||||
|
||||
|
||||
|
@ -644,6 +644,8 @@ def show_warning(
|
||||
content: str,
|
||||
subheader: str | None = None,
|
||||
button: str | None = None,
|
||||
default_cancel: bool = False, # NB: model T does not implement "default_cancel"-style warnings
|
||||
verb_cancel: str | None = None,
|
||||
br_code: ButtonRequestType = ButtonRequestType.Warning,
|
||||
) -> Awaitable[None]:
|
||||
button = button or TR.buttons__continue # def_arg
|
||||
@ -821,6 +823,42 @@ async def should_show_more(
|
||||
raise ActionCancelled
|
||||
|
||||
|
||||
async def confirm_blob_with_optional_pagination(
|
||||
br_name: str,
|
||||
title: str,
|
||||
data: bytes | str,
|
||||
subtitle: str | None = None,
|
||||
verb: str | None = None,
|
||||
verb_cancel: str | None = None,
|
||||
br_code: ButtonRequestType = BR_CODE_OTHER,
|
||||
chunkify: bool = False,
|
||||
):
|
||||
verb = verb or TR.buttons__confirm # def_arg
|
||||
layout = RustLayout(
|
||||
trezorui2.confirm_blob(
|
||||
title=title,
|
||||
description=None,
|
||||
data=data,
|
||||
verb=verb,
|
||||
verb_cancel=verb_cancel,
|
||||
chunkify=chunkify,
|
||||
)
|
||||
)
|
||||
|
||||
if layout.page_count() > 1:
|
||||
return await _confirm_ask_pagination(
|
||||
br_name, title, data, subtitle or "", br_code
|
||||
)
|
||||
else:
|
||||
return raise_if_not_confirmed(
|
||||
interact(
|
||||
layout,
|
||||
br_name,
|
||||
br_code,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
async def _confirm_ask_pagination(
|
||||
br_name: str,
|
||||
title: str,
|
||||
@ -847,7 +885,8 @@ async def _confirm_ask_pagination(
|
||||
paginated = RustLayout(
|
||||
trezorui2.confirm_more(
|
||||
title=title,
|
||||
button=TR.buttons__close,
|
||||
button=TR.buttons__confirm,
|
||||
button_style_confirm=True,
|
||||
items=[(ui.MONO, data)],
|
||||
)
|
||||
)
|
||||
@ -856,6 +895,8 @@ async def _confirm_ask_pagination(
|
||||
|
||||
result = await interact(paginated, br_name, br_code)
|
||||
assert result in (CONFIRMED, CANCELLED)
|
||||
if result is CONFIRMED:
|
||||
return
|
||||
|
||||
assert False
|
||||
|
||||
@ -865,12 +906,15 @@ 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]:
|
||||
verb = verb or TR.buttons__confirm # def_arg
|
||||
@ -878,8 +922,8 @@ def confirm_blob(
|
||||
trezorui2.confirm_blob(
|
||||
title=title,
|
||||
description=description,
|
||||
text_mono=text_mono,
|
||||
data=data,
|
||||
extra=None,
|
||||
hold=hold,
|
||||
verb=verb,
|
||||
verb_cancel=verb_cancel,
|
||||
@ -887,11 +931,6 @@ def confirm_blob(
|
||||
)
|
||||
)
|
||||
|
||||
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,
|
||||
@ -904,17 +943,21 @@ def confirm_blob(
|
||||
def confirm_address(
|
||||
title: str,
|
||||
address: str,
|
||||
subtitle: str | None = None,
|
||||
description: str | None = None,
|
||||
verb: str | None = None,
|
||||
chunkify: bool = True,
|
||||
br_name: str = "confirm_address",
|
||||
br_code: ButtonRequestType = BR_CODE_OTHER,
|
||||
) -> Awaitable[None]:
|
||||
return confirm_value(
|
||||
title,
|
||||
address,
|
||||
description or "",
|
||||
description or subtitle or "",
|
||||
br_name,
|
||||
br_code,
|
||||
verb=TR.buttons__confirm,
|
||||
subtitle=None,
|
||||
verb=(verb or TR.buttons__confirm),
|
||||
)
|
||||
|
||||
|
||||
@ -1095,12 +1138,13 @@ def _confirm_summary(
|
||||
if not utils.BITCOIN_ONLY:
|
||||
|
||||
async def confirm_ethereum_tx(
|
||||
recipient: str,
|
||||
recipient: str | None,
|
||||
total_amount: str,
|
||||
_account: str | None,
|
||||
_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,
|
||||
@ -1123,14 +1167,20 @@ if not utils.BITCOIN_ONLY:
|
||||
)
|
||||
)
|
||||
|
||||
if not is_contract_interaction:
|
||||
description = TR.words__recipient
|
||||
else:
|
||||
description = TR.ethereum__interaction_contract if recipient else None
|
||||
|
||||
while True:
|
||||
# Allowing going back and forth between recipient and summary/details
|
||||
await confirm_blob(
|
||||
br_name,
|
||||
TR.words__recipient,
|
||||
recipient,
|
||||
TR.words__address,
|
||||
recipient or TR.ethereum__new_contract,
|
||||
description=description,
|
||||
verb=TR.buttons__continue,
|
||||
chunkify=chunkify,
|
||||
chunkify=(chunkify if recipient else False),
|
||||
)
|
||||
|
||||
try:
|
||||
@ -1269,7 +1319,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,
|
||||
)
|
||||
|
||||
@ -1287,7 +1337,6 @@ async def confirm_modify_output(
|
||||
verb=TR.buttons__continue,
|
||||
verb_cancel=None,
|
||||
description=f"{TR.words__address}:",
|
||||
extra=None,
|
||||
)
|
||||
)
|
||||
modify_layout = RustLayout(
|
||||
@ -1453,7 +1502,6 @@ async def confirm_signverify(
|
||||
title=TR.sign_message__confirm_message,
|
||||
description=None,
|
||||
data=message,
|
||||
extra=None,
|
||||
hold=not verify,
|
||||
verb=TR.buttons__confirm if verify else None,
|
||||
)
|
||||
|
@ -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",
|
||||
|
@ -344,7 +344,6 @@
|
||||
"ethereum__gas_price": "Cena gasu",
|
||||
"ethereum__max_gas_price": "Maximální cena gasu",
|
||||
"ethereum__name_and_version": "Název a verze",
|
||||
"ethereum__new_contract": "nový kontrakt?",
|
||||
"ethereum__no_message_field": "Žádné pole zprávy",
|
||||
"ethereum__priority_fee": "Přednostní poplatek",
|
||||
"ethereum__show_full_array": "Zobrazit celé pole",
|
||||
@ -360,11 +359,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",
|
||||
|
@ -344,7 +344,6 @@
|
||||
"ethereum__gas_price": "Gas-Preis",
|
||||
"ethereum__max_gas_price": "Max. Gas-Preis",
|
||||
"ethereum__name_and_version": "Name und Version",
|
||||
"ethereum__new_contract": "neuer Kontrakt?",
|
||||
"ethereum__no_message_field": "Kein Nachrichtenfeld",
|
||||
"ethereum__priority_fee": "Prioritätsgebühr",
|
||||
"ethereum__show_full_array": "Ganzes anzeigen",
|
||||
@ -360,11 +359,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",
|
||||
@ -966,8 +965,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",
|
||||
|
@ -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,9 +301,10 @@
|
||||
"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?",
|
||||
"ethereum__new_contract": "New contract will be deployed",
|
||||
"ethereum__no_message_field": "No message field",
|
||||
"ethereum__priority_fee": "Priority fee",
|
||||
"ethereum__show_full_array": "Show full array",
|
||||
@ -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?",
|
||||
@ -398,6 +402,7 @@
|
||||
"instructions__swipe_up": "Swipe up",
|
||||
"instructions__tap_to_confirm": "Tap to confirm",
|
||||
"instructions__tap_to_start": "Tap to start",
|
||||
"instructions__view_all_data": "View all data in the menu.",
|
||||
"joint__title": "Joint transaction",
|
||||
"joint__to_the_total_amount": "To the total amount:",
|
||||
"joint__you_are_contributing": "You are contributing:",
|
||||
|
@ -344,7 +344,6 @@
|
||||
"ethereum__gas_price": "Precio de gas",
|
||||
"ethereum__max_gas_price": "Precio máximo de gas",
|
||||
"ethereum__name_and_version": "Nombre y versión",
|
||||
"ethereum__new_contract": "¿Nuevo contrato?",
|
||||
"ethereum__no_message_field": "Sin campo de mensaje.",
|
||||
"ethereum__priority_fee": "Comisión de prioridad",
|
||||
"ethereum__show_full_array": "Ver matriz íntegra.",
|
||||
@ -360,11 +359,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",
|
||||
|
@ -344,7 +344,6 @@
|
||||
"ethereum__gas_price": "Prix du gaz",
|
||||
"ethereum__max_gas_price": "Prix max du gaz",
|
||||
"ethereum__name_and_version": "Nom et version",
|
||||
"ethereum__new_contract": "nouveau contrat ?",
|
||||
"ethereum__no_message_field": "0 champ de mess.",
|
||||
"ethereum__priority_fee": "Frais de priorité",
|
||||
"ethereum__show_full_array": "Aff. liste complète",
|
||||
@ -360,11 +359,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",
|
||||
|
@ -344,7 +344,6 @@
|
||||
"ethereum__gas_price": "Prezzo gas:",
|
||||
"ethereum__max_gas_price": "Prezzo max gas:",
|
||||
"ethereum__name_and_version": "Nome e versione",
|
||||
"ethereum__new_contract": "nuovo contratto?",
|
||||
"ethereum__no_message_field": "Nessun campo mess.",
|
||||
"ethereum__priority_fee": "Commiss. di priorità:",
|
||||
"ethereum__show_full_array": "Mostra matrice compl.",
|
||||
@ -360,11 +359,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",
|
||||
|
@ -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": "instructions__view_all_data",
|
||||
"973": "ethereum__interaction_contract"
|
||||
}
|
||||
|
@ -344,7 +344,6 @@
|
||||
"ethereum__gas_price": "Preço do gás:",
|
||||
"ethereum__max_gas_price": "Preço máximo do gás:",
|
||||
"ethereum__name_and_version": "Nome e versão",
|
||||
"ethereum__new_contract": "novo contrato?",
|
||||
"ethereum__no_message_field": "Sem campo mens.",
|
||||
"ethereum__priority_fee": "Taxa de prioridade:",
|
||||
"ethereum__show_full_array": "Exibir array completo",
|
||||
@ -360,11 +359,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",
|
||||
|
@ -1,8 +1,8 @@
|
||||
{
|
||||
"current": {
|
||||
"merkle_root": "a49211266cbf110206e919cb605c8f0765c84aae5c89fd21d448226fc96e1686",
|
||||
"datetime": "2024-10-29T22:34:40.907782",
|
||||
"commit": "13e0d61365a9c949023f3c3903dbd9faaf971193"
|
||||
"merkle_root": "fe166db67e7829b73a6af9368005a8dedc215de811adda1c403a8a68288a13ca",
|
||||
"datetime": "2024-10-30T07:52:31.990241",
|
||||
"commit": "91c5edd60ef1d34436afc99a19b68617f31bf672"
|
||||
},
|
||||
"history": [
|
||||
{
|
||||
|
@ -332,7 +332,6 @@
|
||||
"ethereum__gas_price": "Gaz fiyatı:",
|
||||
"ethereum__max_gas_price": "Maksimum gaz fiyatı:",
|
||||
"ethereum__name_and_version": "Ad ve sürüm",
|
||||
"ethereum__new_contract": "yeni sözleşme mi?",
|
||||
"ethereum__no_message_field": "Mesaj alanı yok",
|
||||
"ethereum__priority_fee": "Öncelik ücreti:",
|
||||
"ethereum__show_full_array": "Tüm diziyi göster",
|
||||
@ -348,11 +347,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",
|
||||
|
@ -9,6 +9,7 @@ from trezorlib.debuglink import TrezorClientDebugLink as Client
|
||||
from trezorlib.exceptions import TrezorFailure
|
||||
from trezorlib.tools import parse_path
|
||||
|
||||
from ...input_flows import InputFlowConfirmAllWarnings
|
||||
from . import common
|
||||
from .test_sign_typed_data import DATA as TYPED_DATA
|
||||
|
||||
@ -122,6 +123,9 @@ def test_external_token(client: Client) -> None:
|
||||
|
||||
|
||||
def test_external_chain_without_token(client: Client) -> None:
|
||||
with client:
|
||||
if not client.debug.legacy_debug:
|
||||
client.set_input_flow(InputFlowConfirmAllWarnings(client).get())
|
||||
# when using an external chains, unknown tokens are allowed
|
||||
network = common.encode_network(chain_id=66666, slip44=60)
|
||||
params = DEFAULT_ERC20_PARAMS.copy()
|
||||
@ -141,10 +145,15 @@ def test_external_chain_token_ok(client: Client) -> None:
|
||||
|
||||
|
||||
def test_external_chain_token_mismatch(client: Client) -> None:
|
||||
with client:
|
||||
if not client.debug.legacy_debug:
|
||||
client.set_input_flow(InputFlowConfirmAllWarnings(client).get())
|
||||
# when providing external defs, we explicitly allow, but not use, tokens
|
||||
# from other chains
|
||||
network = common.encode_network(chain_id=66666, slip44=60)
|
||||
token = common.encode_token(address=ERC20_FAKE_ADDRESS, chain_id=55555, decimals=8)
|
||||
token = common.encode_token(
|
||||
address=ERC20_FAKE_ADDRESS, chain_id=55555, decimals=8
|
||||
)
|
||||
params = DEFAULT_ERC20_PARAMS.copy()
|
||||
params.update(to=ERC20_FAKE_ADDRESS, chain_id=66666)
|
||||
ethereum.sign_tx(client, **params, definitions=common.make_defs(network, token))
|
||||
|
@ -24,6 +24,7 @@ from trezorlib.tools import parse_path, unharden
|
||||
|
||||
from ...common import parametrize_using_common_fixtures
|
||||
from ...input_flows import (
|
||||
InputFlowConfirmAllWarnings,
|
||||
InputFlowEthereumSignTxDataGoBack,
|
||||
InputFlowEthereumSignTxDataScrollDown,
|
||||
InputFlowEthereumSignTxDataSkip,
|
||||
@ -56,7 +57,12 @@ def make_defs(parameters: dict) -> messages.EthereumDefinitions:
|
||||
)
|
||||
@pytest.mark.parametrize("chunkify", (True, False))
|
||||
def test_signtx(client: Client, chunkify: bool, parameters: dict, result: dict):
|
||||
_do_test_signtx(client, parameters, result, chunkify=chunkify)
|
||||
input_flow = (
|
||||
InputFlowConfirmAllWarnings(client).get()
|
||||
if not client.debug.legacy_debug
|
||||
else None
|
||||
)
|
||||
_do_test_signtx(client, parameters, result, input_flow, chunkify=chunkify)
|
||||
|
||||
|
||||
def _do_test_signtx(
|
||||
@ -143,6 +149,8 @@ def test_signtx_go_back_from_summary(client: Client):
|
||||
@pytest.mark.parametrize("chunkify", (True, False))
|
||||
def test_signtx_eip1559(client: Client, chunkify: bool, parameters: dict, result: dict):
|
||||
with client:
|
||||
if not client.debug.legacy_debug:
|
||||
client.set_input_flow(InputFlowConfirmAllWarnings(client).get())
|
||||
sig_v, sig_r, sig_s = ethereum.sign_tx_eip1559(
|
||||
client,
|
||||
n=parse_path(parameters["path"]),
|
||||
@ -455,6 +463,7 @@ def test_signtx_data_pagination(client: Client, flow):
|
||||
client.set_input_flow(flow(client))
|
||||
_sign_tx_call()
|
||||
|
||||
if flow is not input_flow_data_scroll_down:
|
||||
with client, pytest.raises(exceptions.Cancelled):
|
||||
client.watch_layout()
|
||||
client.set_input_flow(flow(client, cancel=True))
|
||||
|
@ -1216,12 +1216,21 @@ class InputFlowEthereumSignTxDataScrollDown(InputFlowBase):
|
||||
self.cancel = cancel
|
||||
|
||||
def input_flow_common(self) -> BRGeneratorType:
|
||||
# this flow will not test for the cancel case,
|
||||
# because once we enter the "view all data",
|
||||
# the only way to cancel is by going back to the 1st page view
|
||||
# but that case would be covered by InputFlowEthereumSignTxDataGoBack
|
||||
assert not self.cancel
|
||||
|
||||
yield from self.ETH.confirm_data(info=True)
|
||||
yield from self.ETH.paginate_data()
|
||||
if self.cancel:
|
||||
yield from self.ETH.confirm_data(cancel=True)
|
||||
|
||||
self.debug.wait_layout()
|
||||
if self.debug.layout_type is LayoutType.TR:
|
||||
self.debug.press_right()
|
||||
else:
|
||||
yield from self.ETH.confirm_data()
|
||||
self.debug.click(buttons.OK)
|
||||
|
||||
yield from self.ETH.confirm_tx()
|
||||
|
||||
|
||||
@ -2288,7 +2297,8 @@ class InputFlowConfirmAllWarnings(InputFlowBase):
|
||||
text = layout.text_content().lower()
|
||||
# hi priority warning
|
||||
hi_prio = (
|
||||
TR.translate("addr_mismatch__wrong_derivation_path")
|
||||
TR.translate("ethereum__unknown_contract_address")
|
||||
+ TR.translate("addr_mismatch__wrong_derivation_path")
|
||||
+ TR.translate("send__receiving_to_multisig")
|
||||
+ [
|
||||
"witness path",
|
||||
|
@ -343,9 +343,7 @@ class EthereumFlow:
|
||||
|
||||
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,21 +353,21 @@ class EthereumFlow:
|
||||
|
||||
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
|
||||
if self.client.layout_type is LayoutType.TR:
|
||||
for i in range(br.pages):
|
||||
if i < br.pages - 1:
|
||||
self.debug.press_right()
|
||||
else:
|
||||
for i in range(br.pages):
|
||||
self.debug.wait_layout()
|
||||
if i < br.pages - 1:
|
||||
self.debug.swipe_up()
|
||||
self.debug.press_yes()
|
||||
self.debug.click(buttons.OK)
|
||||
|
||||
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:
|
||||
@ -395,7 +393,7 @@ class EthereumFlow:
|
||||
yield
|
||||
|
||||
if self.client.layout_type is LayoutType.TT:
|
||||
TR.assert_equals(self.debug.wait_layout().title(), "words__recipient")
|
||||
TR.assert_equals(self.debug.wait_layout().title(), "words__address")
|
||||
if cancel:
|
||||
self.debug.press_no()
|
||||
else:
|
||||
@ -425,7 +423,10 @@ class EthereumFlow:
|
||||
|
||||
yield
|
||||
elif self.client.layout_type is LayoutType.TR:
|
||||
TR.assert_equals(self.debug.wait_layout().title(), "words__recipient")
|
||||
TR.assert_in_multiple(
|
||||
self.debug.wait_layout().title(),
|
||||
["ethereum__interaction_contract", "words__recipient"],
|
||||
)
|
||||
if cancel:
|
||||
self.debug.press_left()
|
||||
else:
|
||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user