mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-03-12 22:26:08 +00:00
feat(core): support optional chunkification of addresses in receive and send flows
This commit is contained in:
parent
da3cab22fd
commit
bcb353a4a1
@ -36,6 +36,7 @@ static void _librust_qstrs(void) {
|
|||||||
MP_QSTR_button_event;
|
MP_QSTR_button_event;
|
||||||
MP_QSTR_cancel_arrow;
|
MP_QSTR_cancel_arrow;
|
||||||
MP_QSTR_case_sensitive;
|
MP_QSTR_case_sensitive;
|
||||||
|
MP_QSTR_chunkify;
|
||||||
MP_QSTR_confirm_action;
|
MP_QSTR_confirm_action;
|
||||||
MP_QSTR_confirm_address;
|
MP_QSTR_confirm_address;
|
||||||
MP_QSTR_confirm_backup;
|
MP_QSTR_confirm_backup;
|
||||||
|
@ -52,6 +52,40 @@ pub struct TextLayout {
|
|||||||
pub continues_from_prev_page: bool,
|
pub continues_from_prev_page: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Configuration for chunkifying the text into smaller parts.
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub struct Chunks {
|
||||||
|
/// How many characters will be grouped in one chunk.
|
||||||
|
pub chunk_size: usize,
|
||||||
|
/// How big will be the space between chunks (in pixels).
|
||||||
|
pub x_offset: i16,
|
||||||
|
/// Optional characters that are wider and should be accounted for
|
||||||
|
pub wider_chars: Option<&'static str>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Chunks {
|
||||||
|
pub const fn new(chunk_size: usize, x_offset: i16) -> Self {
|
||||||
|
Chunks {
|
||||||
|
chunk_size,
|
||||||
|
x_offset,
|
||||||
|
wider_chars: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn with_wider_chars(mut self, wider_chars: &'static str) -> Self {
|
||||||
|
self.wider_chars = Some(wider_chars);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_char_wider(self, ch: char) -> bool {
|
||||||
|
if let Some(wider_chars) = self.wider_chars {
|
||||||
|
wider_chars.contains(ch)
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
pub struct TextStyle {
|
pub struct TextStyle {
|
||||||
/// Text font ID.
|
/// Text font ID.
|
||||||
@ -76,6 +110,14 @@ pub struct TextStyle {
|
|||||||
pub line_breaking: LineBreaking,
|
pub line_breaking: LineBreaking,
|
||||||
/// Specifies what to do at the end of the page.
|
/// Specifies what to do at the end of the page.
|
||||||
pub page_breaking: PageBreaking,
|
pub page_breaking: PageBreaking,
|
||||||
|
|
||||||
|
/// Optionally chunkify all the text with a specified chunk
|
||||||
|
/// size and pixel offset for the next chunk.
|
||||||
|
pub chunks: Option<Chunks>,
|
||||||
|
|
||||||
|
/// Optionally increase the vertical space between text lines
|
||||||
|
/// (can be even negative, in which case it will decrease it).
|
||||||
|
pub line_spacing: i16,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TextStyle {
|
impl TextStyle {
|
||||||
@ -96,6 +138,8 @@ impl TextStyle {
|
|||||||
prev_page_ellipsis_icon: None,
|
prev_page_ellipsis_icon: None,
|
||||||
line_breaking: LineBreaking::BreakAtWhitespace,
|
line_breaking: LineBreaking::BreakAtWhitespace,
|
||||||
page_breaking: PageBreaking::CutAndInsertEllipsis,
|
page_breaking: PageBreaking::CutAndInsertEllipsis,
|
||||||
|
chunks: None,
|
||||||
|
line_spacing: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,6 +165,18 @@ impl TextStyle {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Adding optional chunkification to the text.
|
||||||
|
pub const fn with_chunks(mut self, chunks: Chunks) -> Self {
|
||||||
|
self.chunks = Some(chunks);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adding optional change of vertical line spacing.
|
||||||
|
pub const fn with_line_spacing(mut self, line_spacing: i16) -> Self {
|
||||||
|
self.line_spacing = line_spacing;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
fn ellipsis_width(&self) -> i16 {
|
fn ellipsis_width(&self) -> i16 {
|
||||||
if let Some((icon, margin)) = self.ellipsis_icon {
|
if let Some((icon, margin)) = self.ellipsis_icon {
|
||||||
icon.toif.width() + margin
|
icon.toif.width() + margin
|
||||||
@ -220,12 +276,13 @@ impl TextLayout {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let remaining_width = self.bounds.x1 - cursor.x;
|
let remaining_width = self.bounds.x1 - cursor.x;
|
||||||
let span = Span::fit_horizontally(
|
let mut span = Span::fit_horizontally(
|
||||||
remaining_text,
|
remaining_text,
|
||||||
remaining_width,
|
remaining_width,
|
||||||
self.style.text_font,
|
self.style.text_font,
|
||||||
self.style.line_breaking,
|
self.style.line_breaking,
|
||||||
line_ending_space,
|
line_ending_space,
|
||||||
|
self.style.chunks,
|
||||||
);
|
);
|
||||||
|
|
||||||
cursor.x += match self.align {
|
cursor.x += match self.align {
|
||||||
@ -251,6 +308,9 @@ impl TextLayout {
|
|||||||
if span.advance.y > 0 {
|
if span.advance.y > 0 {
|
||||||
// We're advancing to the next line.
|
// We're advancing to the next line.
|
||||||
|
|
||||||
|
// Possibly making a bigger/smaller vertical jump
|
||||||
|
span.advance.y += self.style.line_spacing;
|
||||||
|
|
||||||
// Check if we should be appending a hyphen at this point.
|
// Check if we should be appending a hyphen at this point.
|
||||||
if span.insert_hyphen_before_line_break {
|
if span.insert_hyphen_before_line_break {
|
||||||
sink.hyphen(*cursor, self);
|
sink.hyphen(*cursor, self);
|
||||||
@ -488,6 +548,7 @@ impl Span {
|
|||||||
text_font: impl GlyphMetrics,
|
text_font: impl GlyphMetrics,
|
||||||
breaking: LineBreaking,
|
breaking: LineBreaking,
|
||||||
line_ending_space: i16,
|
line_ending_space: i16,
|
||||||
|
chunks: Option<Chunks>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
const ASCII_LF: char = '\n';
|
const ASCII_LF: char = '\n';
|
||||||
const ASCII_CR: char = '\r';
|
const ASCII_CR: char = '\r';
|
||||||
@ -537,6 +598,7 @@ impl Span {
|
|||||||
|
|
||||||
let mut span_width = 0;
|
let mut span_width = 0;
|
||||||
let mut found_any_whitespace = false;
|
let mut found_any_whitespace = false;
|
||||||
|
let mut chunks_wider_chars = 0;
|
||||||
|
|
||||||
let mut char_indices_iter = text.char_indices().peekable();
|
let mut char_indices_iter = text.char_indices().peekable();
|
||||||
// Iterating manually because we need a reference to the iterator inside the
|
// Iterating manually because we need a reference to the iterator inside the
|
||||||
@ -544,6 +606,22 @@ impl Span {
|
|||||||
while let Some((i, ch)) = char_indices_iter.next() {
|
while let Some((i, ch)) = char_indices_iter.next() {
|
||||||
let char_width = text_font.char_width(ch);
|
let char_width = text_font.char_width(ch);
|
||||||
|
|
||||||
|
// When there is a set chunk size and we reach it,
|
||||||
|
// adjust the line advances and return the line.
|
||||||
|
if let Some(chunkify_config) = chunks {
|
||||||
|
if i == chunkify_config.chunk_size {
|
||||||
|
line.advance.y = 0;
|
||||||
|
// Decreasing the offset for each wider character in the chunk
|
||||||
|
line.advance.x += chunkify_config.x_offset - chunks_wider_chars;
|
||||||
|
return line;
|
||||||
|
} else {
|
||||||
|
// Counting all the wider characters in the chunk
|
||||||
|
if chunkify_config.is_char_wider(ch) {
|
||||||
|
chunks_wider_chars += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Consider if we could be breaking the line at this position.
|
// Consider if we could be breaking the line at this position.
|
||||||
if is_whitespace(ch) && span_width + complete_word_end_width <= max_width {
|
if is_whitespace(ch) && span_width + complete_word_end_width <= max_width {
|
||||||
// Break before the whitespace, without hyphen.
|
// Break before the whitespace, without hyphen.
|
||||||
@ -679,6 +757,7 @@ mod tests {
|
|||||||
FIXED_FONT,
|
FIXED_FONT,
|
||||||
LineBreaking::BreakAtWhitespace,
|
LineBreaking::BreakAtWhitespace,
|
||||||
0,
|
0,
|
||||||
|
None,
|
||||||
);
|
);
|
||||||
spans.push((
|
spans.push((
|
||||||
&remaining_text[..span.length],
|
&remaining_text[..span.length],
|
||||||
|
@ -8,7 +8,7 @@ use crate::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
layout::{LayoutFit, LayoutSink, TextLayout},
|
layout::{Chunks, LayoutFit, LayoutSink, TextLayout},
|
||||||
LineBreaking, TextStyle,
|
LineBreaking, TextStyle,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -90,6 +90,12 @@ impl<'a, T: StringType + Clone + 'a> OpTextLayout<T> {
|
|||||||
cursor.x += offset.x;
|
cursor.x += offset.x;
|
||||||
cursor.y += offset.y;
|
cursor.y += offset.y;
|
||||||
}
|
}
|
||||||
|
Op::Chunkify(chunks) => {
|
||||||
|
self.layout.style.chunks = chunks;
|
||||||
|
}
|
||||||
|
Op::LineSpacing(line_spacing) => {
|
||||||
|
self.layout.style.line_spacing = line_spacing;
|
||||||
|
}
|
||||||
// Moving to the next page
|
// Moving to the next page
|
||||||
Op::NextPage => {
|
Op::NextPage => {
|
||||||
// Pretending that nothing more fits on current page to force
|
// Pretending that nothing more fits on current page to force
|
||||||
@ -219,6 +225,14 @@ impl<T: StringType + Clone> OpTextLayout<T> {
|
|||||||
pub fn line_breaking(self, line_breaking: LineBreaking) -> Self {
|
pub fn line_breaking(self, line_breaking: LineBreaking) -> Self {
|
||||||
self.with_new_item(Op::LineBreaking(line_breaking))
|
self.with_new_item(Op::LineBreaking(line_breaking))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn chunks(self, chunks: Option<Chunks>) -> Self {
|
||||||
|
self.with_new_item(Op::Chunkify(chunks))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn line_spacing(self, spacing: i16) -> Self {
|
||||||
|
self.with_new_item(Op::LineSpacing(spacing))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Op-adding aggregation operations
|
// Op-adding aggregation operations
|
||||||
@ -238,6 +252,14 @@ impl<T: StringType + Clone> OpTextLayout<T> {
|
|||||||
pub fn text_demibold(self, text: T) -> Self {
|
pub fn text_demibold(self, text: T) -> Self {
|
||||||
self.font(Font::DEMIBOLD).text(text)
|
self.font(Font::DEMIBOLD).text(text)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn chunkify_text(self, chunks: Option<(Chunks, i16)>) -> Self {
|
||||||
|
if let Some(chunks) = chunks {
|
||||||
|
self.chunks(Some(chunks.0)).line_spacing(chunks.1)
|
||||||
|
} else {
|
||||||
|
self.chunks(None).line_spacing(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -258,4 +280,8 @@ pub enum Op<T: StringType> {
|
|||||||
CursorOffset(Offset),
|
CursorOffset(Offset),
|
||||||
/// Force continuing on the next page.
|
/// Force continuing on the next page.
|
||||||
NextPage,
|
NextPage,
|
||||||
|
/// Render the following text in a chunkified way. None will disable that.
|
||||||
|
Chunkify(Option<Chunks>),
|
||||||
|
/// Change the line vertical line spacing.
|
||||||
|
LineSpacing(i16),
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@ use crate::{
|
|||||||
},
|
},
|
||||||
TextStyle,
|
TextStyle,
|
||||||
},
|
},
|
||||||
ComponentExt, FormattedText, LineBreaking, Timeout,
|
ComponentExt, FormattedText, Timeout,
|
||||||
},
|
},
|
||||||
display, geometry,
|
display, geometry,
|
||||||
layout::{
|
layout::{
|
||||||
@ -557,6 +557,7 @@ extern "C" fn new_confirm_output(n_args: usize, args: *const Obj, kwargs: *mut M
|
|||||||
let amount: StrBuffer = kwargs.get(Qstr::MP_QSTR_amount)?.try_into()?;
|
let amount: StrBuffer = kwargs.get(Qstr::MP_QSTR_amount)?.try_into()?;
|
||||||
let address_title: StrBuffer = kwargs.get(Qstr::MP_QSTR_address_title)?.try_into()?;
|
let address_title: StrBuffer = kwargs.get(Qstr::MP_QSTR_address_title)?.try_into()?;
|
||||||
let amount_title: StrBuffer = kwargs.get(Qstr::MP_QSTR_amount_title)?.try_into()?;
|
let amount_title: StrBuffer = kwargs.get(Qstr::MP_QSTR_amount_title)?.try_into()?;
|
||||||
|
let chunkify: bool = kwargs.get_or(Qstr::MP_QSTR_chunkify, false)?;
|
||||||
|
|
||||||
let get_page = move |page_index| {
|
let get_page = move |page_index| {
|
||||||
// Showing two screens - the recipient address and summary confirmation
|
// Showing two screens - the recipient address and summary confirmation
|
||||||
@ -567,11 +568,20 @@ extern "C" fn new_confirm_output(n_args: usize, args: *const Obj, kwargs: *mut M
|
|||||||
let btn_actions = ButtonActions::cancel_none_next();
|
let btn_actions = ButtonActions::cancel_none_next();
|
||||||
// Not putting hyphens in the address.
|
// Not putting hyphens in the address.
|
||||||
// Potentially adding address label in different font.
|
// Potentially adding address label in different font.
|
||||||
let mut ops = OpTextLayout::new(theme::TEXT_MONO)
|
let mut ops = OpTextLayout::new(theme::TEXT_MONO_DATA);
|
||||||
.line_breaking(LineBreaking::BreakWordsNoHyphen);
|
|
||||||
if !address_label.is_empty() {
|
if !address_label.is_empty() {
|
||||||
|
// NOTE: need to explicitly turn off the chunkification before rendering the
|
||||||
|
// address label (for some reason it does not help to turn it off after
|
||||||
|
// rendering the chunks)
|
||||||
|
if chunkify {
|
||||||
|
ops = ops.chunkify_text(None);
|
||||||
|
}
|
||||||
ops = ops.text_normal(address_label.clone()).newline();
|
ops = ops.text_normal(address_label.clone()).newline();
|
||||||
}
|
}
|
||||||
|
if chunkify {
|
||||||
|
// Chunkifying the address into smaller pieces when requested
|
||||||
|
ops = ops.chunkify_text(Some((theme::MONO_CHUNKS, 2)));
|
||||||
|
}
|
||||||
ops = ops.text_mono(address.clone());
|
ops = ops.text_mono(address.clone());
|
||||||
let formatted = FormattedText::new(ops).vertically_centered();
|
let formatted = FormattedText::new(ops).vertically_centered();
|
||||||
Page::new(btn_layout, btn_actions, formatted).with_title(address_title.clone())
|
Page::new(btn_layout, btn_actions, formatted).with_title(address_title.clone())
|
||||||
@ -752,15 +762,20 @@ extern "C" fn new_confirm_address(n_args: usize, args: *const Obj, kwargs: *mut
|
|||||||
let block = move |_args: &[Obj], kwargs: &Map| {
|
let block = move |_args: &[Obj], kwargs: &Map| {
|
||||||
let title: StrBuffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
|
let title: StrBuffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
|
||||||
let address: StrBuffer = kwargs.get(Qstr::MP_QSTR_data)?.try_into()?;
|
let address: StrBuffer = kwargs.get(Qstr::MP_QSTR_data)?.try_into()?;
|
||||||
|
let chunkify: bool = kwargs.get_or(Qstr::MP_QSTR_chunkify, false)?;
|
||||||
|
|
||||||
let get_page = move |page_index| {
|
let get_page = move |page_index| {
|
||||||
assert!(page_index == 0);
|
assert!(page_index == 0);
|
||||||
|
|
||||||
let btn_layout = ButtonLayout::cancel_armed_info("CONFIRM".into());
|
let btn_layout = ButtonLayout::cancel_armed_info("CONFIRM".into());
|
||||||
let btn_actions = ButtonActions::cancel_confirm_info();
|
let btn_actions = ButtonActions::cancel_confirm_info();
|
||||||
let ops = OpTextLayout::new(theme::TEXT_MONO)
|
let style = if chunkify {
|
||||||
.line_breaking(LineBreaking::BreakWordsNoHyphen)
|
// Chunkifying the address into smaller pieces when requested
|
||||||
.text_mono(address.clone());
|
theme::TEXT_MONO_ADDRESS_CHUNKS
|
||||||
|
} else {
|
||||||
|
theme::TEXT_MONO_DATA
|
||||||
|
};
|
||||||
|
let ops = OpTextLayout::new(style).text_mono(address.clone());
|
||||||
let formatted = FormattedText::new(ops).vertically_centered();
|
let formatted = FormattedText::new(ops).vertically_centered();
|
||||||
Page::new(btn_layout, btn_actions, formatted).with_title(title.clone())
|
Page::new(btn_layout, btn_actions, formatted).with_title(title.clone())
|
||||||
};
|
};
|
||||||
@ -1584,6 +1599,7 @@ pub static mp_module_trezorui2: Module = obj_module! {
|
|||||||
/// data: str,
|
/// data: str,
|
||||||
/// description: str | None, # unused on TR
|
/// description: str | None, # unused on TR
|
||||||
/// extra: str | None, # unused on TR
|
/// extra: str | None, # unused on TR
|
||||||
|
/// chunkify: bool = False,
|
||||||
/// ) -> object:
|
/// ) -> object:
|
||||||
/// """Confirm address."""
|
/// """Confirm address."""
|
||||||
Qstr::MP_QSTR_confirm_address => obj_fn_kw!(0, new_confirm_address).as_obj(),
|
Qstr::MP_QSTR_confirm_address => obj_fn_kw!(0, new_confirm_address).as_obj(),
|
||||||
@ -1659,6 +1675,7 @@ pub static mp_module_trezorui2: Module = obj_module! {
|
|||||||
/// amount: str,
|
/// amount: str,
|
||||||
/// address_title: str,
|
/// address_title: str,
|
||||||
/// amount_title: str,
|
/// amount_title: str,
|
||||||
|
/// chunkify: bool = False,
|
||||||
/// ) -> object:
|
/// ) -> object:
|
||||||
/// """Confirm output."""
|
/// """Confirm output."""
|
||||||
Qstr::MP_QSTR_confirm_output => obj_fn_kw!(0, new_confirm_output).as_obj(),
|
Qstr::MP_QSTR_confirm_output => obj_fn_kw!(0, new_confirm_output).as_obj(),
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
use crate::ui::{
|
use crate::ui::{
|
||||||
component::{text::TextStyle, LineBreaking, PageBreaking},
|
component::{
|
||||||
|
text::{layout::Chunks, TextStyle},
|
||||||
|
LineBreaking, PageBreaking,
|
||||||
|
},
|
||||||
display::{toif::Icon, Color, Font},
|
display::{toif::Icon, Color, Font},
|
||||||
geometry::Offset,
|
geometry::Offset,
|
||||||
};
|
};
|
||||||
@ -35,6 +38,13 @@ pub const TEXT_MONO: TextStyle = TextStyle::new(Font::MONO, FG, BG, FG, FG)
|
|||||||
/// Mono data text does not have hyphens
|
/// Mono data text does not have hyphens
|
||||||
pub const TEXT_MONO_DATA: TextStyle =
|
pub const TEXT_MONO_DATA: TextStyle =
|
||||||
TEXT_MONO.with_line_breaking(LineBreaking::BreakWordsNoHyphen);
|
TEXT_MONO.with_line_breaking(LineBreaking::BreakWordsNoHyphen);
|
||||||
|
pub const TEXT_MONO_ADDRESS_CHUNKS: TextStyle = TEXT_MONO_DATA
|
||||||
|
.with_chunks(MONO_CHUNKS)
|
||||||
|
.with_line_spacing(2)
|
||||||
|
.with_ellipsis_icon(ICON_NEXT_PAGE, -2);
|
||||||
|
|
||||||
|
// Chunks for this model, with accounting for some wider characters in MONO font
|
||||||
|
pub const MONO_CHUNKS: Chunks = Chunks::new(4, 4).with_wider_chars("mMwW");
|
||||||
|
|
||||||
/// Convert Python-side numeric id to a `TextStyle`.
|
/// Convert Python-side numeric id to a `TextStyle`.
|
||||||
pub fn textstyle_number(num: i32) -> &'static TextStyle {
|
pub fn textstyle_number(num: i32) -> &'static TextStyle {
|
||||||
|
@ -496,6 +496,7 @@ struct ConfirmBlobParams {
|
|||||||
verb_cancel: Option<StrBuffer>,
|
verb_cancel: Option<StrBuffer>,
|
||||||
info_button: bool,
|
info_button: bool,
|
||||||
hold: bool,
|
hold: bool,
|
||||||
|
chunkify: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ConfirmBlobParams {
|
impl ConfirmBlobParams {
|
||||||
@ -517,6 +518,7 @@ impl ConfirmBlobParams {
|
|||||||
verb_cancel,
|
verb_cancel,
|
||||||
info_button: false,
|
info_button: false,
|
||||||
hold,
|
hold,
|
||||||
|
chunkify: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -535,6 +537,11 @@ impl ConfirmBlobParams {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn with_chunkify(mut self, chunkify: bool) -> Self {
|
||||||
|
self.chunkify = chunkify;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
fn into_layout(self) -> Result<Obj, Error> {
|
fn into_layout(self) -> Result<Obj, Error> {
|
||||||
let paragraphs = ConfirmBlob {
|
let paragraphs = ConfirmBlob {
|
||||||
description: self.description.unwrap_or_else(StrBuffer::empty),
|
description: self.description.unwrap_or_else(StrBuffer::empty),
|
||||||
@ -542,7 +549,11 @@ impl ConfirmBlobParams {
|
|||||||
data: self.data.try_into()?,
|
data: self.data.try_into()?,
|
||||||
description_font: &theme::TEXT_NORMAL,
|
description_font: &theme::TEXT_NORMAL,
|
||||||
extra_font: &theme::TEXT_DEMIBOLD,
|
extra_font: &theme::TEXT_DEMIBOLD,
|
||||||
data_font: &theme::TEXT_MONO,
|
data_font: if self.chunkify {
|
||||||
|
&theme::TEXT_MONO_ADDRESS_CHUNKS
|
||||||
|
} else {
|
||||||
|
&theme::TEXT_MONO
|
||||||
|
},
|
||||||
}
|
}
|
||||||
.into_paragraphs();
|
.into_paragraphs();
|
||||||
|
|
||||||
@ -611,6 +622,21 @@ extern "C" fn new_confirm_address(n_args: usize, args: *const Obj, kwargs: *mut
|
|||||||
kwargs.get(Qstr::MP_QSTR_description)?.try_into_option()?;
|
kwargs.get(Qstr::MP_QSTR_description)?.try_into_option()?;
|
||||||
let extra: Option<StrBuffer> = kwargs.get(Qstr::MP_QSTR_extra)?.try_into_option()?;
|
let extra: Option<StrBuffer> = kwargs.get(Qstr::MP_QSTR_extra)?.try_into_option()?;
|
||||||
let data: Obj = kwargs.get(Qstr::MP_QSTR_data)?;
|
let data: Obj = kwargs.get(Qstr::MP_QSTR_data)?;
|
||||||
|
let chunkify: bool = kwargs.get_or(Qstr::MP_QSTR_chunkify, false)?;
|
||||||
|
|
||||||
|
let data_style = if chunkify {
|
||||||
|
// Longer addresses have smaller x_offset so they fit even with scrollbar
|
||||||
|
// (as they will be shown on more than one page)
|
||||||
|
const FITS_ON_ONE_PAGE: usize = 16 * 4;
|
||||||
|
let address: StrBuffer = data.try_into()?;
|
||||||
|
if address.len() <= FITS_ON_ONE_PAGE {
|
||||||
|
&theme::TEXT_MONO_ADDRESS_CHUNKS
|
||||||
|
} else {
|
||||||
|
&theme::TEXT_MONO_ADDRESS_CHUNKS_SMALLER_X_OFFSET
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
&theme::TEXT_MONO
|
||||||
|
};
|
||||||
|
|
||||||
let paragraphs = ConfirmBlob {
|
let paragraphs = ConfirmBlob {
|
||||||
description: description.unwrap_or_else(StrBuffer::empty),
|
description: description.unwrap_or_else(StrBuffer::empty),
|
||||||
@ -618,7 +644,7 @@ extern "C" fn new_confirm_address(n_args: usize, args: *const Obj, kwargs: *mut
|
|||||||
data: data.try_into()?,
|
data: data.try_into()?,
|
||||||
description_font: &theme::TEXT_NORMAL,
|
description_font: &theme::TEXT_NORMAL,
|
||||||
extra_font: &theme::TEXT_DEMIBOLD,
|
extra_font: &theme::TEXT_DEMIBOLD,
|
||||||
data_font: &theme::TEXT_MONO,
|
data_font: data_style,
|
||||||
}
|
}
|
||||||
.into_paragraphs();
|
.into_paragraphs();
|
||||||
|
|
||||||
@ -805,10 +831,12 @@ extern "C" fn new_confirm_value(n_args: usize, args: *const Obj, kwargs: *mut Ma
|
|||||||
.unwrap_or_else(|_| Obj::const_none())
|
.unwrap_or_else(|_| Obj::const_none())
|
||||||
.try_into_option()?;
|
.try_into_option()?;
|
||||||
let hold: bool = kwargs.get_or(Qstr::MP_QSTR_hold, false)?;
|
let hold: bool = kwargs.get_or(Qstr::MP_QSTR_hold, false)?;
|
||||||
|
let chunkify: bool = kwargs.get_or(Qstr::MP_QSTR_chunkify, false)?;
|
||||||
|
|
||||||
ConfirmBlobParams::new(title, value, description, verb, verb_cancel, hold)
|
ConfirmBlobParams::new(title, value, description, verb, verb_cancel, hold)
|
||||||
.with_subtitle(subtitle)
|
.with_subtitle(subtitle)
|
||||||
.with_info_button(info_button)
|
.with_info_button(info_button)
|
||||||
|
.with_chunkify(chunkify)
|
||||||
.into_layout()
|
.into_layout()
|
||||||
};
|
};
|
||||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||||
@ -1680,6 +1708,7 @@ pub static mp_module_trezorui2: Module = obj_module! {
|
|||||||
/// data: str | bytes,
|
/// data: str | bytes,
|
||||||
/// description: str | None,
|
/// description: str | None,
|
||||||
/// extra: str | None,
|
/// extra: str | None,
|
||||||
|
/// chunkify: bool = False,
|
||||||
/// ) -> object:
|
/// ) -> object:
|
||||||
/// """Confirm address. Similar to `confirm_blob` but has corner info button
|
/// """Confirm address. Similar to `confirm_blob` but has corner info button
|
||||||
/// and allows left swipe which does the same thing as the button."""
|
/// and allows left swipe which does the same thing as the button."""
|
||||||
@ -1734,6 +1763,7 @@ pub static mp_module_trezorui2: Module = obj_module! {
|
|||||||
/// verb_cancel: str | None = None,
|
/// verb_cancel: str | None = None,
|
||||||
/// info_button: bool = False,
|
/// info_button: bool = False,
|
||||||
/// hold: bool = False,
|
/// hold: bool = False,
|
||||||
|
/// chunkify: bool = False,
|
||||||
/// ) -> object:
|
/// ) -> object:
|
||||||
/// """Confirm value. Merge of confirm_total and confirm_output."""
|
/// """Confirm value. Merge of confirm_total and confirm_output."""
|
||||||
Qstr::MP_QSTR_confirm_value => obj_fn_kw!(0, new_confirm_value).as_obj(),
|
Qstr::MP_QSTR_confirm_value => obj_fn_kw!(0, new_confirm_value).as_obj(),
|
||||||
|
@ -2,7 +2,7 @@ use crate::{
|
|||||||
time::Duration,
|
time::Duration,
|
||||||
ui::{
|
ui::{
|
||||||
component::{
|
component::{
|
||||||
text::{LineBreaking, PageBreaking, TextStyle},
|
text::{layout::Chunks, LineBreaking, PageBreaking, TextStyle},
|
||||||
FixedHeightBar,
|
FixedHeightBar,
|
||||||
},
|
},
|
||||||
display::{Color, Font, Icon},
|
display::{Color, Font, Icon},
|
||||||
@ -517,6 +517,18 @@ pub const TEXT_MONO: TextStyle = TextStyle::new(Font::MONO, FG, BG, GREY_LIGHT,
|
|||||||
.with_page_breaking(PageBreaking::CutAndInsertEllipsisBoth)
|
.with_page_breaking(PageBreaking::CutAndInsertEllipsisBoth)
|
||||||
.with_ellipsis_icon(ICON_PAGE_NEXT, 0)
|
.with_ellipsis_icon(ICON_PAGE_NEXT, 0)
|
||||||
.with_prev_page_icon(ICON_PAGE_PREV, 0);
|
.with_prev_page_icon(ICON_PAGE_PREV, 0);
|
||||||
|
/// Makes sure that the displayed text (usually address) will get divided into
|
||||||
|
/// smaller chunks.
|
||||||
|
pub const TEXT_MONO_ADDRESS_CHUNKS: TextStyle = TEXT_MONO
|
||||||
|
.with_chunks(Chunks::new(4, 9))
|
||||||
|
.with_line_spacing(5);
|
||||||
|
/// Smaller horizontal chunk offset, used e.g. for long Cardano addresses.
|
||||||
|
/// Also moving the next page ellipsis to the left (as there is a space on the
|
||||||
|
/// left).
|
||||||
|
pub const TEXT_MONO_ADDRESS_CHUNKS_SMALLER_X_OFFSET: TextStyle = TEXT_MONO
|
||||||
|
.with_chunks(Chunks::new(4, 7))
|
||||||
|
.with_line_spacing(5)
|
||||||
|
.with_ellipsis_icon(ICON_PAGE_NEXT, -12);
|
||||||
|
|
||||||
/// Convert Python-side numeric id to a `TextStyle`.
|
/// Convert Python-side numeric id to a `TextStyle`.
|
||||||
pub fn textstyle_number(num: i32) -> &'static TextStyle {
|
pub fn textstyle_number(num: i32) -> &'static TextStyle {
|
||||||
|
@ -50,6 +50,7 @@ def confirm_address(
|
|||||||
data: str,
|
data: str,
|
||||||
description: str | None, # unused on TR
|
description: str | None, # unused on TR
|
||||||
extra: str | None, # unused on TR
|
extra: str | None, # unused on TR
|
||||||
|
chunkify: bool = False,
|
||||||
) -> object:
|
) -> object:
|
||||||
"""Confirm address."""
|
"""Confirm address."""
|
||||||
|
|
||||||
@ -132,6 +133,7 @@ def confirm_output(
|
|||||||
amount: str,
|
amount: str,
|
||||||
address_title: str,
|
address_title: str,
|
||||||
amount_title: str,
|
amount_title: str,
|
||||||
|
chunkify: bool = False,
|
||||||
) -> object:
|
) -> object:
|
||||||
"""Confirm output."""
|
"""Confirm output."""
|
||||||
|
|
||||||
@ -496,6 +498,7 @@ def confirm_address(
|
|||||||
data: str | bytes,
|
data: str | bytes,
|
||||||
description: str | None,
|
description: str | None,
|
||||||
extra: str | None,
|
extra: str | None,
|
||||||
|
chunkify: bool = False,
|
||||||
) -> object:
|
) -> object:
|
||||||
"""Confirm address. Similar to `confirm_blob` but has corner info button
|
"""Confirm address. Similar to `confirm_blob` but has corner info button
|
||||||
and allows left swipe which does the same thing as the button."""
|
and allows left swipe which does the same thing as the button."""
|
||||||
@ -555,6 +558,7 @@ def confirm_value(
|
|||||||
verb_cancel: str | None = None,
|
verb_cancel: str | None = None,
|
||||||
info_button: bool = False,
|
info_button: bool = False,
|
||||||
hold: bool = False,
|
hold: bool = False,
|
||||||
|
chunkify: bool = False,
|
||||||
) -> object:
|
) -> object:
|
||||||
"""Confirm value. Merge of confirm_total and confirm_output."""
|
"""Confirm value. Merge of confirm_total and confirm_output."""
|
||||||
|
|
||||||
|
@ -26,6 +26,8 @@ async def get_address(msg: BinanceGetAddress, keychain: Keychain) -> BinanceAddr
|
|||||||
pubkey = node.public_key()
|
pubkey = node.public_key()
|
||||||
address = address_from_public_key(pubkey, HRP)
|
address = address_from_public_key(pubkey, HRP)
|
||||||
if msg.show_display:
|
if msg.show_display:
|
||||||
await show_address(address, path=paths.address_n_to_str(address_n))
|
await show_address(
|
||||||
|
address, path=paths.address_n_to_str(address_n), chunkify=bool(msg.chunkify)
|
||||||
|
)
|
||||||
|
|
||||||
return BinanceAddress(address=address)
|
return BinanceAddress(address=address)
|
||||||
|
@ -34,21 +34,18 @@ async def require_confirm_transfer(msg: BinanceTransferMsg) -> None:
|
|||||||
for txoutput in msg.outputs:
|
for txoutput in msg.outputs:
|
||||||
make_input_output_pages(txoutput, "Confirm output")
|
make_input_output_pages(txoutput, "Confirm output")
|
||||||
|
|
||||||
await _confirm_transfer(items)
|
await _confirm_transfer(items, chunkify=bool(msg.chunkify))
|
||||||
|
|
||||||
|
|
||||||
async def _confirm_transfer(inputs_outputs: Sequence[tuple[str, str, str]]) -> None:
|
async def _confirm_transfer(
|
||||||
|
inputs_outputs: Sequence[tuple[str, str, str]], chunkify: bool
|
||||||
|
) -> None:
|
||||||
from trezor.ui.layouts import confirm_output
|
from trezor.ui.layouts import confirm_output
|
||||||
|
|
||||||
for index, (title, amount, address) in enumerate(inputs_outputs):
|
for index, (title, amount, address) in enumerate(inputs_outputs):
|
||||||
# Having hold=True on the last item
|
# Having hold=True on the last item
|
||||||
hold = index == len(inputs_outputs) - 1
|
hold = index == len(inputs_outputs) - 1
|
||||||
await confirm_output(
|
await confirm_output(address, amount, title, hold=hold, chunkify=chunkify)
|
||||||
address,
|
|
||||||
amount,
|
|
||||||
title,
|
|
||||||
hold=hold,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
async def require_confirm_cancel(msg: BinanceCancelMsg) -> None:
|
async def require_confirm_cancel(msg: BinanceCancelMsg) -> None:
|
||||||
|
@ -113,6 +113,7 @@ async def get_address(msg: GetAddress, keychain: Keychain, coin: CoinInfo) -> Ad
|
|||||||
multisig_index=multisig_index,
|
multisig_index=multisig_index,
|
||||||
xpubs=_get_xpubs(coin, multisig_xpub_magic, pubnodes),
|
xpubs=_get_xpubs(coin, multisig_xpub_magic, pubnodes),
|
||||||
account=f"Multisig {multisig.m} of {len(pubnodes)}",
|
account=f"Multisig {multisig.m} of {len(pubnodes)}",
|
||||||
|
chunkify=bool(msg.chunkify),
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
account_name = address_n_to_name(coin, address_n, script_type)
|
account_name = address_n_to_name(coin, address_n, script_type)
|
||||||
@ -128,6 +129,7 @@ async def get_address(msg: GetAddress, keychain: Keychain, coin: CoinInfo) -> Ad
|
|||||||
case_sensitive=address_case_sensitive,
|
case_sensitive=address_case_sensitive,
|
||||||
path=path,
|
path=path,
|
||||||
account=account,
|
account=account,
|
||||||
|
chunkify=bool(msg.chunkify),
|
||||||
)
|
)
|
||||||
|
|
||||||
return Address(address=address, mac=mac)
|
return Address(address=address, mac=mac)
|
||||||
|
@ -356,6 +356,7 @@ class AccountType:
|
|||||||
coin: coininfo.CoinInfo,
|
coin: coininfo.CoinInfo,
|
||||||
address_n: Bip32Path,
|
address_n: Bip32Path,
|
||||||
script_type: InputScriptType | None,
|
script_type: InputScriptType | None,
|
||||||
|
show_account_str: bool,
|
||||||
) -> str | None:
|
) -> str | None:
|
||||||
pattern = self.pattern
|
pattern = self.pattern
|
||||||
if self.account_level:
|
if self.account_level:
|
||||||
@ -373,6 +374,8 @@ class AccountType:
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
name = self.account_name
|
name = self.account_name
|
||||||
|
if show_account_str:
|
||||||
|
name = f"{self.account_name} account"
|
||||||
account_pos = pattern.find("/account'")
|
account_pos = pattern.find("/account'")
|
||||||
if account_pos >= 0:
|
if account_pos >= 0:
|
||||||
i = pattern.count("/", 0, account_pos)
|
i = pattern.count("/", 0, account_pos)
|
||||||
@ -387,6 +390,7 @@ def address_n_to_name(
|
|||||||
address_n: Bip32Path,
|
address_n: Bip32Path,
|
||||||
script_type: InputScriptType | None = None,
|
script_type: InputScriptType | None = None,
|
||||||
account_level: bool = False,
|
account_level: bool = False,
|
||||||
|
show_account_str: bool = False,
|
||||||
) -> str | None:
|
) -> str | None:
|
||||||
ACCOUNT_TYPES = (
|
ACCOUNT_TYPES = (
|
||||||
AccountType(
|
AccountType(
|
||||||
@ -446,7 +450,7 @@ def address_n_to_name(
|
|||||||
)
|
)
|
||||||
|
|
||||||
for account in ACCOUNT_TYPES:
|
for account in ACCOUNT_TYPES:
|
||||||
name = account.get_name(coin, address_n, script_type)
|
name = account.get_name(coin, address_n, script_type, show_account_str)
|
||||||
if name:
|
if name:
|
||||||
return name
|
return name
|
||||||
|
|
||||||
|
@ -144,6 +144,7 @@ class BasicApprover(Approver):
|
|||||||
super().__init__(tx, coin)
|
super().__init__(tx, coin)
|
||||||
self.change_count = 0 # the number of change-outputs
|
self.change_count = 0 # the number of change-outputs
|
||||||
self.foreign_address_confirmed = False
|
self.foreign_address_confirmed = False
|
||||||
|
self.chunkify = bool(tx.chunkify)
|
||||||
|
|
||||||
async def add_internal_input(self, txi: TxInput, node: bip32.HDNode) -> None:
|
async def add_internal_input(self, txi: TxInput, node: bip32.HDNode) -> None:
|
||||||
if not validate_path_against_script_type(self.coin, txi):
|
if not validate_path_against_script_type(self.coin, txi):
|
||||||
@ -224,7 +225,11 @@ class BasicApprover(Approver):
|
|||||||
# Ask user to confirm output, unless it is part of a payment
|
# Ask user to confirm output, unless it is part of a payment
|
||||||
# request, which gets confirmed separately.
|
# request, which gets confirmed separately.
|
||||||
await helpers.confirm_output(
|
await helpers.confirm_output(
|
||||||
txo, self.coin, self.amount_unit, self.external_output_index
|
txo,
|
||||||
|
self.coin,
|
||||||
|
self.amount_unit,
|
||||||
|
self.external_output_index,
|
||||||
|
self.chunkify,
|
||||||
)
|
)
|
||||||
self.external_output_index += 1
|
self.external_output_index += 1
|
||||||
|
|
||||||
|
@ -44,11 +44,13 @@ class UiConfirmOutput(UiConfirm):
|
|||||||
coin: CoinInfo,
|
coin: CoinInfo,
|
||||||
amount_unit: AmountUnit,
|
amount_unit: AmountUnit,
|
||||||
output_index: int,
|
output_index: int,
|
||||||
|
chunkify: bool,
|
||||||
):
|
):
|
||||||
self.output = output
|
self.output = output
|
||||||
self.coin = coin
|
self.coin = coin
|
||||||
self.amount_unit = amount_unit
|
self.amount_unit = amount_unit
|
||||||
self.output_index = output_index
|
self.output_index = output_index
|
||||||
|
self.chunkify = chunkify
|
||||||
|
|
||||||
def confirm_dialog(self) -> Awaitable[Any]:
|
def confirm_dialog(self) -> Awaitable[Any]:
|
||||||
return layout.confirm_output(
|
return layout.confirm_output(
|
||||||
@ -56,6 +58,7 @@ class UiConfirmOutput(UiConfirm):
|
|||||||
self.coin,
|
self.coin,
|
||||||
self.amount_unit,
|
self.amount_unit,
|
||||||
self.output_index,
|
self.output_index,
|
||||||
|
self.chunkify,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -238,8 +241,8 @@ class UiConfirmMultipleAccounts(UiConfirm):
|
|||||||
return layout.confirm_multiple_accounts()
|
return layout.confirm_multiple_accounts()
|
||||||
|
|
||||||
|
|
||||||
def confirm_output(output: TxOutput, coin: CoinInfo, amount_unit: AmountUnit, output_index: int) -> Awaitable[None]: # type: ignore [awaitable-is-generator]
|
def confirm_output(output: TxOutput, coin: CoinInfo, amount_unit: AmountUnit, output_index: int, chunkify: bool) -> Awaitable[None]: # type: ignore [awaitable-is-generator]
|
||||||
return (yield UiConfirmOutput(output, coin, amount_unit, output_index))
|
return (yield UiConfirmOutput(output, coin, amount_unit, output_index, chunkify))
|
||||||
|
|
||||||
|
|
||||||
def confirm_decred_sstx_submission(output: TxOutput, coin: CoinInfo, amount_unit: AmountUnit) -> Awaitable[None]: # type: ignore [awaitable-is-generator]
|
def confirm_decred_sstx_submission(output: TxOutput, coin: CoinInfo, amount_unit: AmountUnit) -> Awaitable[None]: # type: ignore [awaitable-is-generator]
|
||||||
|
@ -62,6 +62,7 @@ async def confirm_output(
|
|||||||
coin: CoinInfo,
|
coin: CoinInfo,
|
||||||
amount_unit: AmountUnit,
|
amount_unit: AmountUnit,
|
||||||
output_index: int,
|
output_index: int,
|
||||||
|
chunkify: bool,
|
||||||
) -> None:
|
) -> None:
|
||||||
from trezor.enums import OutputScriptType
|
from trezor.enums import OutputScriptType
|
||||||
|
|
||||||
@ -97,9 +98,18 @@ async def confirm_output(
|
|||||||
|
|
||||||
address_label = None
|
address_label = None
|
||||||
if output.address_n and not output.multisig:
|
if output.address_n and not output.multisig:
|
||||||
|
from trezor import utils
|
||||||
|
|
||||||
|
# Showing the account string only for T2B1 model
|
||||||
|
show_account_str = utils.INTERNAL_MODEL == "T2B1"
|
||||||
script_type = CHANGE_OUTPUT_TO_INPUT_SCRIPT_TYPES[output.script_type]
|
script_type = CHANGE_OUTPUT_TO_INPUT_SCRIPT_TYPES[output.script_type]
|
||||||
address_label = (
|
address_label = (
|
||||||
address_n_to_name(coin, output.address_n, script_type)
|
address_n_to_name(
|
||||||
|
coin,
|
||||||
|
output.address_n,
|
||||||
|
script_type,
|
||||||
|
show_account_str=show_account_str,
|
||||||
|
)
|
||||||
or f"address path {address_n_to_str(output.address_n)}"
|
or f"address path {address_n_to_str(output.address_n)}"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -109,6 +119,7 @@ async def confirm_output(
|
|||||||
title=title,
|
title=title,
|
||||||
address_label=address_label,
|
address_label=address_label,
|
||||||
output_index=output_index,
|
output_index=output_index,
|
||||||
|
chunkify=chunkify,
|
||||||
)
|
)
|
||||||
|
|
||||||
await layout
|
await layout
|
||||||
@ -147,7 +158,7 @@ async def confirm_payment_request(
|
|||||||
) -> Any:
|
) -> Any:
|
||||||
from trezor import wire
|
from trezor import wire
|
||||||
|
|
||||||
memo_texts = []
|
memo_texts: list[str] = []
|
||||||
for m in msg.memos:
|
for m in msg.memos:
|
||||||
if m.text_memo is not None:
|
if m.text_memo is not None:
|
||||||
memo_texts.append(m.text_memo.text)
|
memo_texts.append(m.text_memo.text)
|
||||||
|
@ -39,6 +39,8 @@ async def get_address(
|
|||||||
Credential.payment_credential(address_parameters),
|
Credential.payment_credential(address_parameters),
|
||||||
Credential.stake_credential(address_parameters),
|
Credential.stake_credential(address_parameters),
|
||||||
)
|
)
|
||||||
await show_cardano_address(address_parameters, address, msg.protocol_magic)
|
await show_cardano_address(
|
||||||
|
address_parameters, address, msg.protocol_magic, chunkify=bool(msg.chunkify)
|
||||||
|
)
|
||||||
|
|
||||||
return CardanoAddress(address=address)
|
return CardanoAddress(address=address)
|
||||||
|
@ -210,6 +210,7 @@ async def confirm_sending(
|
|||||||
to: str,
|
to: str,
|
||||||
output_type: Literal["address", "change", "collateral-return"],
|
output_type: Literal["address", "change", "collateral-return"],
|
||||||
network_id: int,
|
network_id: int,
|
||||||
|
chunkify: bool,
|
||||||
) -> None:
|
) -> None:
|
||||||
if output_type == "address":
|
if output_type == "address":
|
||||||
title = "Sending"
|
title = "Sending"
|
||||||
@ -225,6 +226,7 @@ async def confirm_sending(
|
|||||||
format_coin_amount(ada_amount, network_id),
|
format_coin_amount(ada_amount, network_id),
|
||||||
title,
|
title,
|
||||||
br_code=ButtonRequestType.Other,
|
br_code=ButtonRequestType.Other,
|
||||||
|
chunkify=chunkify,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -898,6 +900,7 @@ async def show_cardano_address(
|
|||||||
address_parameters: messages.CardanoAddressParametersType,
|
address_parameters: messages.CardanoAddressParametersType,
|
||||||
address: str,
|
address: str,
|
||||||
protocol_magic: int,
|
protocol_magic: int,
|
||||||
|
chunkify: bool,
|
||||||
) -> None:
|
) -> None:
|
||||||
CAT = CardanoAddressType # local_cache_global
|
CAT = CardanoAddressType # local_cache_global
|
||||||
|
|
||||||
@ -925,4 +928,5 @@ async def show_cardano_address(
|
|||||||
path=path,
|
path=path,
|
||||||
account=account,
|
account=account,
|
||||||
network=network_name,
|
network=network_name,
|
||||||
|
chunkify=chunkify,
|
||||||
)
|
)
|
||||||
|
@ -366,6 +366,7 @@ class Signer:
|
|||||||
address,
|
address,
|
||||||
"change" if self._is_change_output(output) else "address",
|
"change" if self._is_change_output(output) else "address",
|
||||||
self.msg.network_id,
|
self.msg.network_id,
|
||||||
|
chunkify=bool(self.msg.chunkify),
|
||||||
)
|
)
|
||||||
|
|
||||||
async def _show_output_credentials(
|
async def _show_output_credentials(
|
||||||
@ -1043,6 +1044,7 @@ class Signer:
|
|||||||
address,
|
address,
|
||||||
"collateral-return",
|
"collateral-return",
|
||||||
self.msg.network_id,
|
self.msg.network_id,
|
||||||
|
chunkify=bool(self.msg.chunkify),
|
||||||
)
|
)
|
||||||
|
|
||||||
def _should_show_collateral_return_init(self, output: CardanoTxOutput) -> bool:
|
def _should_show_collateral_return_init(self, output: CardanoTxOutput) -> bool:
|
||||||
|
@ -32,6 +32,8 @@ async def get_address(
|
|||||||
address = address_from_bytes(node.ethereum_pubkeyhash(), defs.network)
|
address = address_from_bytes(node.ethereum_pubkeyhash(), defs.network)
|
||||||
|
|
||||||
if msg.show_display:
|
if msg.show_display:
|
||||||
await show_address(address, path=paths.address_n_to_str(address_n))
|
await show_address(
|
||||||
|
address, path=paths.address_n_to_str(address_n), chunkify=bool(msg.chunkify)
|
||||||
|
)
|
||||||
|
|
||||||
return EthereumAddress(address=address)
|
return EthereumAddress(address=address)
|
||||||
|
@ -69,6 +69,7 @@ async def get_address(msg: MoneroGetAddress, keychain: Keychain) -> MoneroAddres
|
|||||||
addr,
|
addr,
|
||||||
address_qr="monero:" + addr,
|
address_qr="monero:" + addr,
|
||||||
path=paths.address_n_to_str(msg.address_n),
|
path=paths.address_n_to_str(msg.address_n),
|
||||||
|
chunkify=bool(msg.chunkify),
|
||||||
)
|
)
|
||||||
|
|
||||||
return MoneroAddress(address=addr.encode())
|
return MoneroAddress(address=addr.encode())
|
||||||
|
@ -126,7 +126,9 @@ async def require_confirm_transaction(
|
|||||||
cur_payment = payment_id
|
cur_payment = payment_id
|
||||||
else:
|
else:
|
||||||
cur_payment = None
|
cur_payment = None
|
||||||
await _require_confirm_output(dst, network_type, cur_payment)
|
await _require_confirm_output(
|
||||||
|
dst, network_type, cur_payment, chunkify=bool(tsx_data.chunkify)
|
||||||
|
)
|
||||||
|
|
||||||
if (
|
if (
|
||||||
payment_id
|
payment_id
|
||||||
@ -143,6 +145,7 @@ async def _require_confirm_output(
|
|||||||
dst: MoneroTransactionDestinationEntry,
|
dst: MoneroTransactionDestinationEntry,
|
||||||
network_type: MoneroNetworkType,
|
network_type: MoneroNetworkType,
|
||||||
payment_id: bytes | None,
|
payment_id: bytes | None,
|
||||||
|
chunkify: bool,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Single transaction destination confirmation
|
Single transaction destination confirmation
|
||||||
@ -161,6 +164,7 @@ async def _require_confirm_output(
|
|||||||
addr,
|
addr,
|
||||||
_format_amount(dst.amount),
|
_format_amount(dst.amount),
|
||||||
br_code=BRT_SignTx,
|
br_code=BRT_SignTx,
|
||||||
|
chunkify=chunkify,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -35,6 +35,7 @@ async def get_address(msg: NEMGetAddress, keychain: Keychain) -> NEMAddress:
|
|||||||
case_sensitive=False,
|
case_sensitive=False,
|
||||||
path=address_n_to_str(address_n),
|
path=address_n_to_str(address_n),
|
||||||
network=get_network_str(network),
|
network=get_network_str(network),
|
||||||
|
chunkify=bool(msg.chunkify),
|
||||||
)
|
)
|
||||||
|
|
||||||
return NEMAddress(address=address)
|
return NEMAddress(address=address)
|
||||||
|
@ -46,7 +46,9 @@ async def sign_tx(msg: NEMSignTx, keychain: Keychain) -> NEMSignedTx:
|
|||||||
common = transaction
|
common = transaction
|
||||||
|
|
||||||
if msg.transfer:
|
if msg.transfer:
|
||||||
tx = await transfer.transfer(public_key, common, msg.transfer, node)
|
tx = await transfer.transfer(
|
||||||
|
public_key, common, msg.transfer, node, chunkify=bool(msg.chunkify)
|
||||||
|
)
|
||||||
elif msg.provision_namespace:
|
elif msg.provision_namespace:
|
||||||
tx = await namespace.namespace(public_key, common, msg.provision_namespace)
|
tx = await namespace.namespace(public_key, common, msg.provision_namespace)
|
||||||
elif msg.mosaic_creation:
|
elif msg.mosaic_creation:
|
||||||
|
@ -12,11 +12,12 @@ async def transfer(
|
|||||||
common: NEMTransactionCommon,
|
common: NEMTransactionCommon,
|
||||||
transfer: NEMTransfer,
|
transfer: NEMTransfer,
|
||||||
node: bip32.HDNode,
|
node: bip32.HDNode,
|
||||||
|
chunkify: bool,
|
||||||
) -> bytes:
|
) -> bytes:
|
||||||
transfer.mosaics = serialize.canonicalize_mosaics(transfer.mosaics)
|
transfer.mosaics = serialize.canonicalize_mosaics(transfer.mosaics)
|
||||||
payload, encrypted = serialize.get_transfer_payload(transfer, node)
|
payload, encrypted = serialize.get_transfer_payload(transfer, node)
|
||||||
|
|
||||||
await layout.ask_transfer(common, transfer, encrypted)
|
await layout.ask_transfer(common, transfer, encrypted, chunkify)
|
||||||
|
|
||||||
w = serialize.serialize_transfer(common, transfer, public_key, payload, encrypted)
|
w = serialize.serialize_transfer(common, transfer, public_key, payload, encrypted)
|
||||||
for mosaic in transfer.mosaics:
|
for mosaic in transfer.mosaics:
|
||||||
|
@ -20,6 +20,7 @@ async def ask_transfer(
|
|||||||
common: NEMTransactionCommon,
|
common: NEMTransactionCommon,
|
||||||
transfer: NEMTransfer,
|
transfer: NEMTransfer,
|
||||||
encrypted: bool,
|
encrypted: bool,
|
||||||
|
chunkify: bool,
|
||||||
) -> None:
|
) -> None:
|
||||||
from trezor.ui.layouts import confirm_output, confirm_text
|
from trezor.ui.layouts import confirm_output, confirm_text
|
||||||
|
|
||||||
@ -42,6 +43,7 @@ async def ask_transfer(
|
|||||||
await confirm_output(
|
await confirm_output(
|
||||||
transfer.recipient,
|
transfer.recipient,
|
||||||
f"{format_amount(_get_xem_amount(transfer), NEM_MAX_DIVISIBILITY)} XEM",
|
f"{format_amount(_get_xem_amount(transfer), NEM_MAX_DIVISIBILITY)} XEM",
|
||||||
|
chunkify=chunkify,
|
||||||
)
|
)
|
||||||
|
|
||||||
await require_confirm_final(common.fee)
|
await require_confirm_final(common.fee)
|
||||||
|
@ -25,6 +25,10 @@ async def get_address(msg: RippleGetAddress, keychain: Keychain) -> RippleAddres
|
|||||||
address = address_from_public_key(pubkey)
|
address = address_from_public_key(pubkey)
|
||||||
|
|
||||||
if msg.show_display:
|
if msg.show_display:
|
||||||
await show_address(address, path=paths.address_n_to_str(msg.address_n))
|
await show_address(
|
||||||
|
address,
|
||||||
|
path=paths.address_n_to_str(msg.address_n),
|
||||||
|
chunkify=bool(msg.chunkify),
|
||||||
|
)
|
||||||
|
|
||||||
return RippleAddress(address=address)
|
return RippleAddress(address=address)
|
||||||
|
@ -22,7 +22,7 @@ async def require_confirm_destination_tag(tag: int) -> None:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def require_confirm_tx(to: str, value: int) -> None:
|
async def require_confirm_tx(to: str, value: int, chunkify: bool = False) -> None:
|
||||||
from trezor.ui.layouts import confirm_output
|
from trezor.ui.layouts import confirm_output
|
||||||
|
|
||||||
await confirm_output(to, format_amount(value, DECIMALS) + " XRP")
|
await confirm_output(to, format_amount(value, DECIMALS) + " XRP", chunkify=chunkify)
|
||||||
|
@ -47,7 +47,9 @@ async def sign_tx(msg: RippleSignTx, keychain: Keychain) -> RippleSignedTx:
|
|||||||
|
|
||||||
if payment.destination_tag is not None:
|
if payment.destination_tag is not None:
|
||||||
await layout.require_confirm_destination_tag(payment.destination_tag)
|
await layout.require_confirm_destination_tag(payment.destination_tag)
|
||||||
await layout.require_confirm_tx(payment.destination, payment.amount)
|
await layout.require_confirm_tx(
|
||||||
|
payment.destination, payment.amount, chunkify=bool(msg.chunkify)
|
||||||
|
)
|
||||||
await layout.require_confirm_total(payment.amount + msg.fee, msg.fee)
|
await layout.require_confirm_total(payment.amount + msg.fee, msg.fee)
|
||||||
|
|
||||||
# Signs and encodes signature into DER format
|
# Signs and encodes signature into DER format
|
||||||
|
@ -25,6 +25,8 @@ async def get_address(msg: StellarGetAddress, keychain: Keychain) -> StellarAddr
|
|||||||
|
|
||||||
if msg.show_display:
|
if msg.show_display:
|
||||||
path = paths.address_n_to_str(msg.address_n)
|
path = paths.address_n_to_str(msg.address_n)
|
||||||
await show_address(address, case_sensitive=False, path=path)
|
await show_address(
|
||||||
|
address, case_sensitive=False, path=path, chunkify=bool(msg.chunkify)
|
||||||
|
)
|
||||||
|
|
||||||
return StellarAddress(address=address)
|
return StellarAddress(address=address)
|
||||||
|
@ -29,6 +29,10 @@ async def get_address(msg: TezosGetAddress, keychain: Keychain) -> TezosAddress:
|
|||||||
address = helpers.base58_encode_check(pkh, helpers.TEZOS_ED25519_ADDRESS_PREFIX)
|
address = helpers.base58_encode_check(pkh, helpers.TEZOS_ED25519_ADDRESS_PREFIX)
|
||||||
|
|
||||||
if msg.show_display:
|
if msg.show_display:
|
||||||
await show_address(address, path=paths.address_n_to_str(msg.address_n))
|
await show_address(
|
||||||
|
address,
|
||||||
|
path=paths.address_n_to_str(msg.address_n),
|
||||||
|
chunkify=bool(msg.chunkify),
|
||||||
|
)
|
||||||
|
|
||||||
return TezosAddress(address=address)
|
return TezosAddress(address=address)
|
||||||
|
@ -4,13 +4,14 @@ from trezor.ui.layouts import confirm_address, confirm_metadata, confirm_propert
|
|||||||
BR_SIGN_TX = ButtonRequestType.SignTx # global_import_cache
|
BR_SIGN_TX = ButtonRequestType.SignTx # global_import_cache
|
||||||
|
|
||||||
|
|
||||||
async def require_confirm_tx(to: str, value: int) -> None:
|
async def require_confirm_tx(to: str, value: int, chunkify: bool = False) -> None:
|
||||||
from trezor.ui.layouts import confirm_output
|
from trezor.ui.layouts import confirm_output
|
||||||
|
|
||||||
await confirm_output(
|
await confirm_output(
|
||||||
to,
|
to,
|
||||||
format_tezos_amount(value),
|
format_tezos_amount(value),
|
||||||
br_code=BR_SIGN_TX,
|
br_code=BR_SIGN_TX,
|
||||||
|
chunkify=chunkify,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -71,12 +71,16 @@ async def sign_tx(msg: TezosSignTx, keychain: Keychain) -> TezosSignedTx:
|
|||||||
# operation to transfer tokens from a smart contract to an implicit account or a smart contract
|
# operation to transfer tokens from a smart contract to an implicit account or a smart contract
|
||||||
elif transfer is not None:
|
elif transfer is not None:
|
||||||
to = _get_address_from_contract(transfer.destination)
|
to = _get_address_from_contract(transfer.destination)
|
||||||
await layout.require_confirm_tx(to, transfer.amount)
|
await layout.require_confirm_tx(
|
||||||
|
to, transfer.amount, chunkify=bool(msg.chunkify)
|
||||||
|
)
|
||||||
await layout.require_confirm_fee(transfer.amount, fee)
|
await layout.require_confirm_fee(transfer.amount, fee)
|
||||||
else:
|
else:
|
||||||
# transactions from an implicit account
|
# transactions from an implicit account
|
||||||
to = _get_address_from_contract(transaction.destination)
|
to = _get_address_from_contract(transaction.destination)
|
||||||
await layout.require_confirm_tx(to, transaction.amount)
|
await layout.require_confirm_tx(
|
||||||
|
to, transaction.amount, chunkify=bool(msg.chunkify)
|
||||||
|
)
|
||||||
await layout.require_confirm_fee(transaction.amount, fee)
|
await layout.require_confirm_fee(transaction.amount, fee)
|
||||||
|
|
||||||
elif origination is not None:
|
elif origination is not None:
|
||||||
|
@ -466,6 +466,7 @@ async def show_address(
|
|||||||
mismatch_title: str = "ADDRESS MISMATCH?",
|
mismatch_title: str = "ADDRESS MISMATCH?",
|
||||||
br_type: str = "show_address",
|
br_type: str = "show_address",
|
||||||
br_code: ButtonRequestType = ButtonRequestType.Address,
|
br_code: ButtonRequestType = ButtonRequestType.Address,
|
||||||
|
chunkify: bool = False,
|
||||||
) -> None:
|
) -> None:
|
||||||
send_button_request = True
|
send_button_request = True
|
||||||
if title is None:
|
if title is None:
|
||||||
@ -482,6 +483,7 @@ async def show_address(
|
|||||||
data=address,
|
data=address,
|
||||||
description="", # unused on TR
|
description="", # unused on TR
|
||||||
extra=None, # unused on TR
|
extra=None, # unused on TR
|
||||||
|
chunkify=chunkify,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
if send_button_request:
|
if send_button_request:
|
||||||
@ -550,6 +552,7 @@ def show_pubkey(
|
|||||||
br_type=br_type,
|
br_type=br_type,
|
||||||
br_code=ButtonRequestType.PublicKey,
|
br_code=ButtonRequestType.PublicKey,
|
||||||
mismatch_title=mismatch_title,
|
mismatch_title=mismatch_title,
|
||||||
|
chunkify=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -652,6 +655,7 @@ async def confirm_output(
|
|||||||
br_code: ButtonRequestType = ButtonRequestType.ConfirmOutput,
|
br_code: ButtonRequestType = ButtonRequestType.ConfirmOutput,
|
||||||
address_label: str | None = None,
|
address_label: str | None = None,
|
||||||
output_index: int | None = None,
|
output_index: int | None = None,
|
||||||
|
chunkify: bool = False,
|
||||||
) -> None:
|
) -> None:
|
||||||
address_title = (
|
address_title = (
|
||||||
"RECIPIENT" if output_index is None else f"RECIPIENT #{output_index + 1}"
|
"RECIPIENT" if output_index is None else f"RECIPIENT #{output_index + 1}"
|
||||||
@ -667,6 +671,7 @@ async def confirm_output(
|
|||||||
address_title=address_title,
|
address_title=address_title,
|
||||||
amount_title=amount_title,
|
amount_title=amount_title,
|
||||||
amount=amount,
|
amount=amount,
|
||||||
|
chunkify=chunkify,
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
"confirm_output",
|
"confirm_output",
|
||||||
|
@ -417,6 +417,7 @@ async def show_address(
|
|||||||
mismatch_title: str = "Address mismatch?",
|
mismatch_title: str = "Address mismatch?",
|
||||||
br_type: str = "show_address",
|
br_type: str = "show_address",
|
||||||
br_code: ButtonRequestType = ButtonRequestType.Address,
|
br_code: ButtonRequestType = ButtonRequestType.Address,
|
||||||
|
chunkify: bool = False,
|
||||||
) -> None:
|
) -> None:
|
||||||
send_button_request = True
|
send_button_request = True
|
||||||
if title is None:
|
if title is None:
|
||||||
@ -435,6 +436,7 @@ async def show_address(
|
|||||||
data=address,
|
data=address,
|
||||||
description=network or "",
|
description=network or "",
|
||||||
extra=None,
|
extra=None,
|
||||||
|
chunkify=chunkify,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
if send_button_request:
|
if send_button_request:
|
||||||
@ -500,6 +502,7 @@ def show_pubkey(
|
|||||||
br_type=br_type,
|
br_type=br_type,
|
||||||
br_code=ButtonRequestType.PublicKey,
|
br_code=ButtonRequestType.PublicKey,
|
||||||
mismatch_title=mismatch_title,
|
mismatch_title=mismatch_title,
|
||||||
|
chunkify=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -577,6 +580,7 @@ async def confirm_output(
|
|||||||
br_code: ButtonRequestType = ButtonRequestType.ConfirmOutput,
|
br_code: ButtonRequestType = ButtonRequestType.ConfirmOutput,
|
||||||
address_label: str | None = None,
|
address_label: str | None = None,
|
||||||
output_index: int | None = None,
|
output_index: int | None = None,
|
||||||
|
chunkify: bool = False,
|
||||||
) -> None:
|
) -> None:
|
||||||
if title is not None:
|
if title is not None:
|
||||||
if title.upper().startswith("CONFIRM "):
|
if title.upper().startswith("CONFIRM "):
|
||||||
@ -601,6 +605,7 @@ async def confirm_output(
|
|||||||
verb="CONTINUE",
|
verb="CONTINUE",
|
||||||
hold=False,
|
hold=False,
|
||||||
info_button=False,
|
info_button=False,
|
||||||
|
chunkify=chunkify,
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
"confirm_output",
|
"confirm_output",
|
||||||
|
Loading…
Reference in New Issue
Block a user