mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-07-01 04:12:37 +00:00
WIP - design of Receive flow
This commit is contained in:
parent
c0e657e0d1
commit
47f7ec6972
@ -60,6 +60,7 @@ static void _librust_qstrs(void) {
|
||||
MP_QSTR_show_remaining_shares;
|
||||
MP_QSTR_show_share_words;
|
||||
MP_QSTR_show_progress;
|
||||
MP_QSTR_show_receive_address;
|
||||
|
||||
MP_QSTR_progress_event;
|
||||
MP_QSTR_usb_event;
|
||||
@ -128,4 +129,7 @@ static void _librust_qstrs(void) {
|
||||
MP_QSTR_fee_label;
|
||||
MP_QSTR_address_title;
|
||||
MP_QSTR_amount_title;
|
||||
MP_QSTR_show_receive_address;
|
||||
MP_QSTR_derivation_path;
|
||||
MP_QSTR_address_qr;
|
||||
}
|
||||
|
@ -755,6 +755,16 @@ impl ButtonActions {
|
||||
)
|
||||
}
|
||||
|
||||
/// Going to last page with left, to the next page with right and confirm
|
||||
/// with middle
|
||||
pub fn last_confirm_next() -> Self {
|
||||
Self::new(
|
||||
Some(ButtonAction::GoToIndex(-1)),
|
||||
Some(ButtonAction::Confirm),
|
||||
Some(ButtonAction::NextPage),
|
||||
)
|
||||
}
|
||||
|
||||
/// Cancelling with left, going to the next page with right
|
||||
pub fn cancel_next() -> Self {
|
||||
Self::new(
|
||||
@ -769,6 +779,11 @@ impl ButtonActions {
|
||||
Self::new(None, None, Some(ButtonAction::NextPage))
|
||||
}
|
||||
|
||||
/// Only going to the prev page with left
|
||||
pub fn only_prev() -> Self {
|
||||
Self::new(Some(ButtonAction::PrevPage), None, None)
|
||||
}
|
||||
|
||||
/// Cancelling with left, confirming with right
|
||||
pub fn cancel_confirm() -> Self {
|
||||
Self::new(
|
||||
|
@ -234,12 +234,16 @@ where
|
||||
fn paint(&mut self) {
|
||||
self.pad.paint();
|
||||
// Scrollbars are painted only with a title
|
||||
if let Some(title) = &self.common_title {
|
||||
if let Some(title) = self.common_title {
|
||||
self.scrollbar.paint();
|
||||
common::paint_header_left(title, self.title_area);
|
||||
}
|
||||
self.current_page.paint();
|
||||
self.buttons.paint();
|
||||
// On purpose painting current page at the end, after buttons,
|
||||
// because we sometimes (in the case of QR code) need to use the
|
||||
// whole height of the display for showing the content
|
||||
// (and painting buttons last would cover the lower part).
|
||||
self.current_page.paint();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,7 @@ use crate::{
|
||||
ui::{
|
||||
component::Paginate,
|
||||
display::{Font, Icon, IconAndName},
|
||||
geometry::{Alignment, Offset, Rect},
|
||||
geometry::{Alignment, Offset, Point, Rect},
|
||||
model_tr::theme,
|
||||
util::ResultExt,
|
||||
},
|
||||
@ -13,7 +13,8 @@ use heapless::Vec;
|
||||
|
||||
use super::{
|
||||
flow_pages_poc_helpers::{
|
||||
LayoutFit, LayoutSink, Op, TextLayout, TextNoOp, TextRenderer, TextStyle, ToDisplay,
|
||||
LayoutFit, LayoutSink, Op, QrCodeInfo, TextLayout, TextNoOp, TextRenderer, TextStyle,
|
||||
ToDisplay,
|
||||
},
|
||||
ButtonActions, ButtonDetails, ButtonLayout,
|
||||
};
|
||||
@ -148,13 +149,15 @@ impl<const M: usize> Page<M> {
|
||||
current.btn_left
|
||||
};
|
||||
|
||||
let btn_right = if self.has_next_page() {
|
||||
Some(ButtonDetails::down_arrow_icon_wide())
|
||||
// Middle button should be shown only on the last page, not to collide
|
||||
// with the fat right button.
|
||||
let (btn_middle, btn_right) = if self.has_next_page() {
|
||||
(None, Some(ButtonDetails::down_arrow_icon_wide()))
|
||||
} else {
|
||||
current.btn_right
|
||||
(current.btn_middle, current.btn_right)
|
||||
};
|
||||
|
||||
ButtonLayout::new(btn_left, current.btn_middle, btn_right)
|
||||
ButtonLayout::new(btn_left, btn_middle, btn_right)
|
||||
}
|
||||
|
||||
pub fn btn_actions(&self) -> ButtonActions {
|
||||
@ -211,6 +214,21 @@ impl<const M: usize> Page<M> {
|
||||
self.with_new_item(Op::Icon(Icon::new(icon)))
|
||||
}
|
||||
|
||||
pub fn qr_code(
|
||||
self,
|
||||
text: StrBuffer,
|
||||
max_size: i16,
|
||||
case_sensitive: bool,
|
||||
center: Point,
|
||||
) -> Self {
|
||||
self.with_new_item(Op::QrCode(QrCodeInfo::new(
|
||||
text,
|
||||
max_size,
|
||||
case_sensitive,
|
||||
center,
|
||||
)))
|
||||
}
|
||||
|
||||
pub fn font(self, font: Font) -> Self {
|
||||
self.with_new_item(Op::Font(font))
|
||||
}
|
||||
|
@ -12,7 +12,14 @@ use crate::{
|
||||
|
||||
use heapless::Vec;
|
||||
|
||||
// TODO: document this
|
||||
/// To account for operations that are not made of characters
|
||||
/// but need to be accounted for somehow.
|
||||
/// Number of processed characters will be increased by this
|
||||
/// to account for the operation.
|
||||
const PROCESSED_CHARS_ONE: usize = 1;
|
||||
|
||||
/// Container for text allowing for its displaying by chunks
|
||||
/// without the need to allocate a new String each time.
|
||||
#[derive(Clone)]
|
||||
pub struct ToDisplay {
|
||||
pub text: StrBuffer,
|
||||
@ -28,15 +35,26 @@ impl ToDisplay {
|
||||
}
|
||||
}
|
||||
|
||||
/// Holding information about a QR code to be displayed.
|
||||
#[derive(Clone)]
|
||||
pub struct QrCodeInfo {
|
||||
pub text: StrBuffer,
|
||||
pub data: StrBuffer,
|
||||
pub max_size: i16,
|
||||
pub case_sensitive: bool,
|
||||
pub center: Point,
|
||||
}
|
||||
|
||||
// TODO: add QrCode(QrCodeInfo)
|
||||
impl QrCodeInfo {
|
||||
pub fn new(data: StrBuffer, max_size: i16, case_sensitive: bool, center: Point) -> Self {
|
||||
Self {
|
||||
data,
|
||||
max_size,
|
||||
case_sensitive,
|
||||
center,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Operations that can be done on the screen.
|
||||
#[derive(Clone)]
|
||||
pub enum Op {
|
||||
@ -44,6 +62,8 @@ pub enum Op {
|
||||
Text(ToDisplay),
|
||||
/// Render icon.
|
||||
Icon(Icon),
|
||||
/// Render QR Code.
|
||||
QrCode(QrCodeInfo),
|
||||
/// Set current text color.
|
||||
Color(Color),
|
||||
/// Set currently used font.
|
||||
@ -218,13 +238,15 @@ impl TextLayout {
|
||||
}
|
||||
}
|
||||
Op::Icon(_) if skipped < skip_bytes => {
|
||||
// Assume the icon accounts for one character
|
||||
skipped = skipped.saturating_add(1);
|
||||
skipped = skipped.saturating_add(PROCESSED_CHARS_ONE);
|
||||
None
|
||||
}
|
||||
Op::QrCode(_) if skipped < skip_bytes => {
|
||||
skipped = skipped.saturating_add(PROCESSED_CHARS_ONE);
|
||||
None
|
||||
}
|
||||
Op::NextPage if skipped < skip_bytes => {
|
||||
// Skip the next page and consider it one character
|
||||
skipped = skipped.saturating_add(1);
|
||||
skipped = skipped.saturating_add(PROCESSED_CHARS_ONE);
|
||||
None
|
||||
}
|
||||
Op::CursorOffset(_) if skipped < skip_bytes => {
|
||||
@ -258,16 +280,28 @@ impl TextLayout {
|
||||
Op::NextPage => {
|
||||
// Pretending that nothing more fits on current page to force
|
||||
// continuing on the next one
|
||||
// Making that to account for one character for pagination purposes
|
||||
total_processed_chars += 1;
|
||||
total_processed_chars += PROCESSED_CHARS_ONE;
|
||||
return LayoutFit::OutOfBounds {
|
||||
processed_chars: total_processed_chars,
|
||||
height: self.layout_height(init_cursor, *cursor),
|
||||
};
|
||||
}
|
||||
Op::QrCode(qr_details) => {
|
||||
self.layout_qr_code(qr_details, sink);
|
||||
// QR codes are always the last component that can be shown
|
||||
// on the given page (meaning a series of Op's).
|
||||
// Throwing Fitting to force the end of the whole page.
|
||||
// (It would be too complicated to account for it by modifying cursor, etc.,
|
||||
// and there is not a need for it currently. If we want QR code together
|
||||
// with some other things on the same screen, just first render the other
|
||||
// things and do the QR code last.)
|
||||
total_processed_chars += PROCESSED_CHARS_ONE;
|
||||
return LayoutFit::Fitting {
|
||||
processed_chars: total_processed_chars,
|
||||
height: self.layout_height(init_cursor, *cursor),
|
||||
};
|
||||
}
|
||||
// Drawing text or icon
|
||||
// TODO: add QRCode support - always returning OOB
|
||||
// to force going to the next page
|
||||
Op::Text(_) | Op::Icon(_) => {
|
||||
// Text and Icon behave similarly - we try to fit them
|
||||
// on the current page and if they do not fit,
|
||||
@ -452,12 +486,18 @@ impl TextLayout {
|
||||
|
||||
cursor.x += icon.width() as i16;
|
||||
LayoutFit::Fitting {
|
||||
// TODO: unify the 1 being returned - make it a CONST probably
|
||||
processed_chars: 1,
|
||||
processed_chars: PROCESSED_CHARS_ONE,
|
||||
height: 0, // it should just draw on one line
|
||||
}
|
||||
}
|
||||
|
||||
/// Fitting the QR code on the current screen.
|
||||
/// Not returning `LayoutFit`, QR codes are handled differently,
|
||||
/// they automatically throw out of bounds.
|
||||
pub fn layout_qr_code(&self, qr_code_info: QrCodeInfo, sink: &mut dyn LayoutSink) {
|
||||
sink.qrcode(qr_code_info);
|
||||
}
|
||||
|
||||
/// Overall height of the content, including paddings.
|
||||
fn layout_height(&self, init_cursor: Point, end_cursor: Point) -> i16 {
|
||||
self.padding_top
|
||||
@ -495,7 +535,7 @@ pub trait LayoutSink {
|
||||
/// Icon should be displayed.
|
||||
fn icon(&mut self, _cursor: Point, _layout: &TextLayout, _icon: Icon) {}
|
||||
/// QR code should be displayed.
|
||||
fn qrcode(&mut self, _cursor: Point, _layout: &TextLayout, _qr_code: QrCodeInfo) {}
|
||||
fn qrcode(&mut self, _qr_code: QrCodeInfo) {}
|
||||
/// Hyphen at the end of line.
|
||||
fn hyphen(&mut self, _cursor: Point, _layout: &TextLayout) {}
|
||||
/// Ellipsis at the end of the page.
|
||||
@ -594,6 +634,16 @@ impl LayoutSink for TextRenderer {
|
||||
layout.style.background_color,
|
||||
);
|
||||
}
|
||||
|
||||
fn qrcode(&mut self, qr_code: QrCodeInfo) {
|
||||
display::qrcode(
|
||||
qr_code.center,
|
||||
qr_code.data.as_ref(),
|
||||
qr_code.max_size as _,
|
||||
qr_code.case_sensitive,
|
||||
)
|
||||
.unwrap_or(())
|
||||
}
|
||||
}
|
||||
|
||||
/// `LayoutSink` for debugging purposes.
|
||||
@ -613,6 +663,11 @@ impl<'a> LayoutSink for TraceSink<'a> {
|
||||
icon.trace(self.0);
|
||||
}
|
||||
|
||||
fn qrcode(&mut self, qr_code: QrCodeInfo) {
|
||||
self.0.string("QR code: ");
|
||||
self.0.string(qr_code.data.as_ref());
|
||||
}
|
||||
|
||||
fn hyphen(&mut self, _cursor: Point, _layout: &TextLayout) {
|
||||
self.0.string("-");
|
||||
}
|
||||
|
@ -18,7 +18,6 @@ use crate::{
|
||||
component::{
|
||||
base::Component,
|
||||
paginated::{PageMsg, Paginate},
|
||||
painter,
|
||||
text::paragraphs::{
|
||||
Paragraph, ParagraphSource, ParagraphVecLong, ParagraphVecShort, Paragraphs, VecExt,
|
||||
},
|
||||
@ -41,7 +40,7 @@ use super::{
|
||||
NoBtnDialogMsg, Page, PassphraseEntry, PassphraseEntryMsg, PinEntry, PinEntryMsg, Progress,
|
||||
QRCodePage, QRCodePageMessage, ShareWords, SimpleChoice, SimpleChoiceMsg,
|
||||
},
|
||||
theme,
|
||||
constant, theme,
|
||||
};
|
||||
|
||||
pub enum CancelConfirmMsg {
|
||||
@ -373,24 +372,81 @@ extern "C" fn new_confirm_total(n_args: usize, args: *const Obj, kwargs: *mut Ma
|
||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||
}
|
||||
|
||||
extern "C" fn new_show_qr(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
||||
extern "C" fn new_show_receive_address(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
||||
let block = move |_args: &[Obj], kwargs: &Map| {
|
||||
let title: StrBuffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
|
||||
let address: StrBuffer = kwargs.get(Qstr::MP_QSTR_address)?.try_into()?;
|
||||
let verb_cancel: StrBuffer = kwargs.get(Qstr::MP_QSTR_verb_cancel)?.try_into()?;
|
||||
let address_qr: StrBuffer = kwargs.get(Qstr::MP_QSTR_address_qr)?.try_into()?;
|
||||
let account: StrBuffer = kwargs.get(Qstr::MP_QSTR_account)?.try_into()?;
|
||||
let derivation_path: StrBuffer = kwargs.get(Qstr::MP_QSTR_derivation_path)?.try_into()?;
|
||||
let case_sensitive: bool = kwargs.get(Qstr::MP_QSTR_case_sensitive)?.try_into()?;
|
||||
|
||||
let verb: StrBuffer = "CONFIRM".into();
|
||||
let get_page = move |page_index| {
|
||||
// Showing two screens - the recipient address and summary confirmation
|
||||
match page_index {
|
||||
0 => {
|
||||
// RECEIVE ADDRESS
|
||||
let btn_layout = ButtonLayout::new(
|
||||
Some(ButtonDetails::cancel_icon()),
|
||||
Some(ButtonDetails::armed_text("CONFIRM".into())),
|
||||
Some(ButtonDetails::text("i".into())),
|
||||
);
|
||||
let btn_actions = ButtonActions::last_confirm_next();
|
||||
Page::<15>::new(btn_layout, btn_actions, Font::BOLD)
|
||||
.text_bold(title)
|
||||
.newline()
|
||||
.newline_half()
|
||||
.text_mono(address)
|
||||
}
|
||||
1 => {
|
||||
// QR CODE
|
||||
let btn_layout = ButtonLayout::new(
|
||||
Some(ButtonDetails::left_arrow_icon()),
|
||||
None,
|
||||
Some(ButtonDetails::right_arrow_icon()),
|
||||
);
|
||||
let btn_actions = ButtonActions::prev_next();
|
||||
Page::<15>::new(btn_layout, btn_actions, Font::MONO).qr_code(
|
||||
address_qr,
|
||||
theme::QR_SIDE_MAX,
|
||||
case_sensitive,
|
||||
constant::screen().center(),
|
||||
)
|
||||
}
|
||||
2 => {
|
||||
// ADDRESS INFO
|
||||
let btn_layout =
|
||||
ButtonLayout::new(Some(ButtonDetails::left_arrow_icon()), None, None);
|
||||
let btn_actions = ButtonActions::only_prev();
|
||||
Page::<15>::new(btn_layout, btn_actions, Font::MONO)
|
||||
.text_bold("Account:".into())
|
||||
.newline()
|
||||
.text_mono(account)
|
||||
.newline()
|
||||
.text_bold("Derivation path:".into())
|
||||
.newline()
|
||||
.text_mono(derivation_path)
|
||||
}
|
||||
3 => {
|
||||
// ADDRESS MISMATCH
|
||||
let btn_layout = ButtonLayout::new(
|
||||
Some(ButtonDetails::left_arrow_icon()),
|
||||
None,
|
||||
Some(ButtonDetails::text("QUIT".into())),
|
||||
);
|
||||
let btn_actions = ButtonActions::beginning_cancel();
|
||||
Page::<15>::new(btn_layout, btn_actions, Font::MONO)
|
||||
.text_bold("ADDRESS MISMATCH?".into())
|
||||
.newline()
|
||||
.newline_half()
|
||||
.text_mono("Please contact Trezor support on trezor.io/support".into())
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
};
|
||||
let pages = FlowPages::new(get_page, 4);
|
||||
|
||||
let qr_code = painter::qrcode_painter(address, theme::QR_SIDE_MAX as u32, case_sensitive);
|
||||
|
||||
let btn_layout = ButtonLayout::new(
|
||||
Some(ButtonDetails::text(verb_cancel)),
|
||||
None,
|
||||
Some(ButtonDetails::text(verb)),
|
||||
);
|
||||
|
||||
let obj = LayoutObj::new(QRCodePage::new(title, qr_code, btn_layout))?;
|
||||
let obj = LayoutObj::new(Flow::new(pages))?;
|
||||
Ok(obj.into())
|
||||
};
|
||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||
@ -760,15 +816,17 @@ pub static mp_module_trezorui2: Module = obj_module! {
|
||||
/// """Confirm summary of a transaction. Specific for model R."""
|
||||
Qstr::MP_QSTR_confirm_total_r => obj_fn_kw!(0, new_confirm_total).as_obj(),
|
||||
|
||||
/// def show_qr(
|
||||
/// def show_receive_address(
|
||||
/// *,
|
||||
/// title: str,
|
||||
/// address: str,
|
||||
/// verb_cancel: str,
|
||||
/// address_qr: str,
|
||||
/// account: str,
|
||||
/// derivation_path: str,
|
||||
/// case_sensitive: bool,
|
||||
/// ) -> object:
|
||||
/// """Show QR code."""
|
||||
Qstr::MP_QSTR_show_qr => obj_fn_kw!(0, new_show_qr).as_obj(),
|
||||
/// """Show receive address together with QR code and details about it."""
|
||||
Qstr::MP_QSTR_show_receive_address => obj_fn_kw!(0, new_show_receive_address).as_obj(),
|
||||
|
||||
/// def show_info(
|
||||
/// *,
|
||||
|
@ -65,5 +65,7 @@ pub const BUTTON_CONTENT_HEIGHT: i16 = 7;
|
||||
pub const BUTTON_OUTLINE: i16 = 3;
|
||||
pub const BUTTON_HEIGHT: i16 = BUTTON_CONTENT_HEIGHT + 2 * BUTTON_OUTLINE;
|
||||
|
||||
// Full-size QR code.
|
||||
pub const QR_SIDE_MAX: i16 = 64 - BUTTON_HEIGHT;
|
||||
/// Full-size QR code.
|
||||
/// Accounting for little larger QR code than the screen,
|
||||
/// to fit taproot addresses (top and bottom row will not be visible).
|
||||
pub const QR_SIDE_MAX: i16 = 66;
|
||||
|
@ -60,14 +60,16 @@ def confirm_total_r(
|
||||
|
||||
|
||||
# rust/src/ui/model_tr/layout.rs
|
||||
def show_qr(
|
||||
def show_receive_address(
|
||||
*,
|
||||
title: str,
|
||||
address: str,
|
||||
verb_cancel: str,
|
||||
address_qr: str,
|
||||
account: str,
|
||||
derivation_path: str,
|
||||
case_sensitive: bool,
|
||||
) -> object:
|
||||
"""Show QR code."""
|
||||
"""Show receive address together with QR code and details about it."""
|
||||
|
||||
|
||||
# rust/src/ui/model_tr/layout.rs
|
||||
|
@ -28,7 +28,12 @@ async def get_address(
|
||||
pubkey = node.public_key()
|
||||
address = address_from_public_key(pubkey, HRP)
|
||||
if msg.show_display:
|
||||
title = paths.address_n_to_str(address_n)
|
||||
await show_address(ctx, address, title=title)
|
||||
derivation_path = paths.address_n_to_str(address_n)
|
||||
await show_address(
|
||||
ctx,
|
||||
address,
|
||||
derivation_path=derivation_path,
|
||||
account="Binance",
|
||||
)
|
||||
|
||||
return BinanceAddress(address=address)
|
||||
|
@ -112,13 +112,38 @@ async def get_address(
|
||||
xpubs=_get_xpubs(coin, multisig_xpub_magic, pubnodes),
|
||||
)
|
||||
else:
|
||||
title = address_n_to_str(address_n)
|
||||
derivation_path = address_n_to_str(address_n)
|
||||
await show_address(
|
||||
ctx,
|
||||
address_short,
|
||||
address_qr=address,
|
||||
case_sensitive=address_case_sensitive,
|
||||
title=title,
|
||||
derivation_path=derivation_path,
|
||||
account=_path_to_account(derivation_path, coin.coin_shortcut),
|
||||
)
|
||||
|
||||
return Address(address=address, mac=mac)
|
||||
|
||||
|
||||
def _path_to_account(path: str, coin_shortcut: str) -> str:
|
||||
"""Transforms a BIP-32 path to a human-readable account name.
|
||||
|
||||
Examples: (m/44'/0'/0/0/0', BTC) -> BTC Legacy #1
|
||||
(m/84'/0'/3/0/0', BTC) -> BTC Segwit #4
|
||||
"""
|
||||
path = path.lstrip("m/")
|
||||
purpose = path.split("/")[0].rstrip("'hH")
|
||||
|
||||
try:
|
||||
account_num = int(path.split("/")[2].rstrip("'hH"))
|
||||
except (IndexError, ValueError):
|
||||
account_num = 0
|
||||
|
||||
purpose_str = {
|
||||
"44": "Legacy",
|
||||
"49": "L.Segwit",
|
||||
"84": "Segwit",
|
||||
"86": "Taproot",
|
||||
}.get(purpose, "Unknown")
|
||||
|
||||
return f"{coin_shortcut} {purpose_str} #{account_num + 1}"
|
||||
|
@ -924,7 +924,7 @@ async def show_cardano_address(
|
||||
network_name = protocol_magics.to_ui_string(protocol_magic)
|
||||
|
||||
title = f"{ADDRESS_TYPE_NAMES[address_parameters.address_type]} address"
|
||||
address_extra = None
|
||||
derivation_path = None
|
||||
title_qr = title
|
||||
if address_parameters.address_type in (
|
||||
CAT.BYRON,
|
||||
@ -935,10 +935,10 @@ async def show_cardano_address(
|
||||
CAT.REWARD,
|
||||
):
|
||||
if address_parameters.address_n:
|
||||
address_extra = address_n_to_str(address_parameters.address_n)
|
||||
derivation_path = address_n_to_str(address_parameters.address_n)
|
||||
title_qr = address_n_to_str(address_parameters.address_n)
|
||||
elif address_parameters.address_n_staking:
|
||||
address_extra = address_n_to_str(address_parameters.address_n_staking)
|
||||
derivation_path = address_n_to_str(address_parameters.address_n_staking)
|
||||
title_qr = address_n_to_str(address_parameters.address_n_staking)
|
||||
|
||||
await layouts.show_address(
|
||||
@ -946,6 +946,8 @@ async def show_cardano_address(
|
||||
address,
|
||||
title=title,
|
||||
network=network_name,
|
||||
address_extra=address_extra,
|
||||
address_extra=derivation_path,
|
||||
title_qr=title_qr,
|
||||
derivation_path=derivation_path,
|
||||
account="Cardano",
|
||||
)
|
||||
|
@ -32,7 +32,12 @@ async def get_address(
|
||||
address = address_from_bytes(node.ethereum_pubkeyhash(), network)
|
||||
|
||||
if msg.show_display:
|
||||
title = paths.address_n_to_str(address_n)
|
||||
await show_address(ctx, address, title=title)
|
||||
derivation_path = paths.address_n_to_str(address_n)
|
||||
await show_address(
|
||||
ctx,
|
||||
address,
|
||||
derivation_path=derivation_path,
|
||||
account="Ethereum",
|
||||
)
|
||||
|
||||
return EthereumAddress(address=address)
|
||||
|
@ -68,12 +68,13 @@ async def get_address(
|
||||
)
|
||||
|
||||
if msg.show_display:
|
||||
title = paths.address_n_to_str(msg.address_n)
|
||||
derivation_path = paths.address_n_to_str(msg.address_n)
|
||||
await show_address(
|
||||
ctx,
|
||||
addr,
|
||||
address_qr="monero:" + addr,
|
||||
title=title,
|
||||
derivation_path=derivation_path,
|
||||
account="Monero",
|
||||
)
|
||||
|
||||
return MoneroAddress(address=addr.encode())
|
||||
|
@ -30,13 +30,14 @@ async def get_address(
|
||||
address = node.nem_address(network)
|
||||
|
||||
if msg.show_display:
|
||||
title = address_n_to_str(address_n)
|
||||
derivation_path = address_n_to_str(address_n)
|
||||
await show_address(
|
||||
ctx,
|
||||
address,
|
||||
case_sensitive=False,
|
||||
title=title,
|
||||
network=get_network_str(network),
|
||||
derivation_path=derivation_path,
|
||||
account="NEM",
|
||||
)
|
||||
|
||||
return NEMAddress(address=address)
|
||||
|
@ -25,7 +25,12 @@ async def get_address(
|
||||
address = address_from_public_key(pubkey)
|
||||
|
||||
if msg.show_display:
|
||||
title = paths.address_n_to_str(msg.address_n)
|
||||
await show_address(ctx, address, title=title)
|
||||
derivation_path = paths.address_n_to_str(msg.address_n)
|
||||
await show_address(
|
||||
ctx,
|
||||
address,
|
||||
derivation_path=derivation_path,
|
||||
account="Ripple",
|
||||
)
|
||||
|
||||
return RippleAddress(address=address)
|
||||
|
@ -24,7 +24,13 @@ async def get_address(
|
||||
address = helpers.address_from_public_key(pubkey)
|
||||
|
||||
if msg.show_display:
|
||||
title = paths.address_n_to_str(msg.address_n)
|
||||
await show_address(ctx, address, case_sensitive=False, title=title)
|
||||
derivation_path = paths.address_n_to_str(msg.address_n)
|
||||
await show_address(
|
||||
ctx,
|
||||
address,
|
||||
case_sensitive=False,
|
||||
derivation_path=derivation_path,
|
||||
account="Stellar",
|
||||
)
|
||||
|
||||
return StellarAddress(address=address)
|
||||
|
@ -29,7 +29,12 @@ async def get_address(
|
||||
address = helpers.base58_encode_check(pkh, helpers.TEZOS_ED25519_ADDRESS_PREFIX)
|
||||
|
||||
if msg.show_display:
|
||||
title = paths.address_n_to_str(msg.address_n)
|
||||
await show_address(ctx, address, title=title)
|
||||
derivation_path = paths.address_n_to_str(msg.address_n)
|
||||
await show_address(
|
||||
ctx,
|
||||
address,
|
||||
derivation_path=derivation_path,
|
||||
account="Tezos",
|
||||
)
|
||||
|
||||
return TezosAddress(address=address)
|
||||
|
@ -655,69 +655,39 @@ async def show_address(
|
||||
*,
|
||||
case_sensitive: bool = True,
|
||||
address_qr: str | None = None,
|
||||
title: str = "Confirm address",
|
||||
title: str | None = None,
|
||||
network: str | None = None,
|
||||
multisig_index: int | None = None,
|
||||
xpubs: Sequence[str] = (),
|
||||
address_extra: str | None = None,
|
||||
title_qr: str | None = None,
|
||||
derivation_path: str | None = None,
|
||||
account: str | None = None,
|
||||
) -> None:
|
||||
is_multisig = len(xpubs) > 0
|
||||
# TODO: replace with confirm_blob
|
||||
data = address
|
||||
if network:
|
||||
data += f"\n\n{network}"
|
||||
if address_extra:
|
||||
data += f"\n\n{address_extra}"
|
||||
while True:
|
||||
result = await interact(
|
||||
account = account or "Unknown"
|
||||
derivation_path = derivation_path or "Unknown"
|
||||
title = title or "Receive address"
|
||||
|
||||
await raise_if_cancelled(
|
||||
interact(
|
||||
ctx,
|
||||
RustLayout(
|
||||
trezorui2.confirm_action(
|
||||
trezorui2.show_receive_address(
|
||||
title=title.upper(),
|
||||
action=data,
|
||||
description=None,
|
||||
verb="CONFIRM",
|
||||
verb_cancel="QR",
|
||||
reverse=False,
|
||||
hold=False,
|
||||
address=address,
|
||||
address_qr=address if address_qr is None else address_qr,
|
||||
account=account,
|
||||
derivation_path=derivation_path,
|
||||
case_sensitive=case_sensitive,
|
||||
)
|
||||
),
|
||||
"show_address",
|
||||
ButtonRequestType.Address,
|
||||
)
|
||||
if result is trezorui2.CONFIRMED:
|
||||
break
|
||||
)
|
||||
|
||||
result = await interact(
|
||||
ctx,
|
||||
RustLayout(
|
||||
trezorui2.show_qr(
|
||||
address=address if address_qr is None else address_qr,
|
||||
case_sensitive=case_sensitive,
|
||||
title=title.upper() if title_qr is None else title_qr.upper(),
|
||||
verb_cancel="XPUBs" if is_multisig else "ADDRESS",
|
||||
)
|
||||
),
|
||||
"show_qr",
|
||||
ButtonRequestType.Address,
|
||||
)
|
||||
if result is trezorui2.CONFIRMED:
|
||||
break
|
||||
|
||||
if is_multisig:
|
||||
for i, xpub in enumerate(xpubs):
|
||||
cancel = "NEXT" if i < len(xpubs) - 1 else "ADDRESS"
|
||||
title_xpub = f"XPUB #{i + 1}"
|
||||
title_xpub += " (yours)" if i == multisig_index else " (cosigner)"
|
||||
result = await interact(
|
||||
ctx,
|
||||
_show_xpub(xpub, title=title_xpub, cancel=cancel),
|
||||
"show_xpub",
|
||||
ButtonRequestType.PublicKey,
|
||||
)
|
||||
if result is trezorui2.CONFIRMED:
|
||||
return
|
||||
# TODO: support showing multisig xpubs?
|
||||
# TODO: send button requests in the flow above?
|
||||
|
||||
|
||||
def show_pubkey(
|
||||
|
@ -360,20 +360,29 @@ async def show_address(
|
||||
*,
|
||||
address_qr: str | None = None,
|
||||
case_sensitive: bool = True,
|
||||
title: str = "Confirm address",
|
||||
title: str | None = None,
|
||||
network: str | None = None,
|
||||
multisig_index: int | None = None,
|
||||
xpubs: Sequence[str] = (),
|
||||
address_extra: str | None = None,
|
||||
title_qr: str | None = None,
|
||||
derivation_path: str | None = None,
|
||||
account: str | None = None,
|
||||
) -> None:
|
||||
# TODO: could show the derivation path and account, the same was as TR
|
||||
is_multisig = len(xpubs) > 0
|
||||
if title:
|
||||
title = title.upper()
|
||||
elif derivation_path:
|
||||
title = derivation_path.upper()
|
||||
else:
|
||||
title = "CONFIRM ADDRESS"
|
||||
while True:
|
||||
result = await interact(
|
||||
ctx,
|
||||
RustLayout(
|
||||
trezorui2.confirm_blob(
|
||||
title=title.upper(),
|
||||
title=title,
|
||||
data=address,
|
||||
description=network or "",
|
||||
extra=address_extra or "",
|
||||
|
Loading…
Reference in New Issue
Block a user