mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-03-19 01:26:06 +00:00
feat(core/ui): T3T1 send flow
[no changelog]
This commit is contained in:
parent
7bbfa64dbb
commit
a212b325fe
@ -19,7 +19,9 @@ static void _librust_qstrs(void) {
|
||||
MP_QSTR___dict__;
|
||||
MP_QSTR___name__;
|
||||
MP_QSTR_account;
|
||||
MP_QSTR_account_items;
|
||||
MP_QSTR_account_label;
|
||||
MP_QSTR_account_path;
|
||||
MP_QSTR_accounts;
|
||||
MP_QSTR_action;
|
||||
MP_QSTR_active;
|
||||
@ -175,6 +177,7 @@ static void _librust_qstrs(void) {
|
||||
MP_QSTR_confirm_reset_device;
|
||||
MP_QSTR_confirm_total;
|
||||
MP_QSTR_confirm_total__fee_rate;
|
||||
MP_QSTR_confirm_total__fee_rate_colon;
|
||||
MP_QSTR_confirm_total__sending_from_account;
|
||||
MP_QSTR_confirm_total__title_fee;
|
||||
MP_QSTR_confirm_total__title_sending_from;
|
||||
@ -205,6 +208,7 @@ static void _librust_qstrs(void) {
|
||||
MP_QSTR_experimental_mode__title;
|
||||
MP_QSTR_extra;
|
||||
MP_QSTR_fee_amount;
|
||||
MP_QSTR_fee_items;
|
||||
MP_QSTR_fee_label;
|
||||
MP_QSTR_fee_rate_amount;
|
||||
MP_QSTR_fee_title;
|
||||
@ -212,9 +216,11 @@ static void _librust_qstrs(void) {
|
||||
MP_QSTR_fingerprint;
|
||||
MP_QSTR_firmware_update__title;
|
||||
MP_QSTR_firmware_update__title_fingerprint;
|
||||
MP_QSTR_flow_confirm_output;
|
||||
MP_QSTR_flow_confirm_reset_create;
|
||||
MP_QSTR_flow_confirm_reset_recover;
|
||||
MP_QSTR_flow_confirm_set_new_pin;
|
||||
MP_QSTR_flow_confirm_summary;
|
||||
MP_QSTR_flow_get_address;
|
||||
MP_QSTR_flow_prompt_backup;
|
||||
MP_QSTR_flow_show_share_words;
|
||||
@ -248,6 +254,7 @@ static void _librust_qstrs(void) {
|
||||
MP_QSTR_inputs__space;
|
||||
MP_QSTR_instructions__continue_in_app;
|
||||
MP_QSTR_instructions__hold_to_confirm;
|
||||
MP_QSTR_instructions__hold_to_sign;
|
||||
MP_QSTR_instructions__swipe_up;
|
||||
MP_QSTR_instructions__tap_to_confirm;
|
||||
MP_QSTR_is_type_of;
|
||||
@ -521,11 +528,15 @@ static void _librust_qstrs(void) {
|
||||
MP_QSTR_select_word;
|
||||
MP_QSTR_select_word_count;
|
||||
MP_QSTR_send__address_path;
|
||||
MP_QSTR_send__cancel_sign;
|
||||
MP_QSTR_send__confirm_sending;
|
||||
MP_QSTR_send__from_multiple_accounts;
|
||||
MP_QSTR_send__incl_transaction_fee;
|
||||
MP_QSTR_send__including_fee;
|
||||
MP_QSTR_send__maximum_fee;
|
||||
MP_QSTR_send__receiving_to_multisig;
|
||||
MP_QSTR_send__send_from;
|
||||
MP_QSTR_send__sign_transaction;
|
||||
MP_QSTR_send__title_confirm_sending;
|
||||
MP_QSTR_send__title_joint_transaction;
|
||||
MP_QSTR_send__title_receiving_to;
|
||||
@ -534,6 +545,7 @@ static void _librust_qstrs(void) {
|
||||
MP_QSTR_send__title_sending_to;
|
||||
MP_QSTR_send__to_the_total_amount;
|
||||
MP_QSTR_send__total_amount;
|
||||
MP_QSTR_send__total_amount_colon;
|
||||
MP_QSTR_send__transaction_id;
|
||||
MP_QSTR_send__you_are_contributing;
|
||||
MP_QSTR_share_words;
|
||||
@ -833,7 +845,6 @@ static void _librust_qstrs(void) {
|
||||
MP_QSTR_eos__requirement;
|
||||
MP_QSTR_eos__sell_ram;
|
||||
MP_QSTR_eos__sender;
|
||||
MP_QSTR_eos__sign_transaction;
|
||||
MP_QSTR_eos__threshold;
|
||||
MP_QSTR_eos__to;
|
||||
MP_QSTR_eos__transfer;
|
||||
|
@ -351,9 +351,9 @@ pub enum TranslatedString {
|
||||
coinjoin__title_do_not_disconnect = 217, // "Do not disconnect your trezor!"
|
||||
coinjoin__title_progress = 218, // "Coinjoin in progress"
|
||||
coinjoin__waiting_for_others = 219, // "Waiting for others"
|
||||
confirm_total__fee_rate = 220, // "Fee rate:"
|
||||
confirm_total__fee_rate_colon = 220, // "Fee rate:"
|
||||
confirm_total__sending_from_account = 221, // "Sending from account:"
|
||||
confirm_total__title_fee = 222, // "Fee information"
|
||||
confirm_total__title_fee = 222, // "Fee info"
|
||||
confirm_total__title_sending_from = 223, // "Sending from"
|
||||
debug__loading_seed = 224, // "Loading seed"
|
||||
debug__loading_seed_not_recommended = 225, // "Loading private seed is not recommended."
|
||||
@ -420,8 +420,7 @@ pub enum TranslatedString {
|
||||
eos__sell_ram = 258, // "Sell RAM"
|
||||
#[cfg(feature = "universal_fw")]
|
||||
eos__sender = 259, // "Sender:"
|
||||
#[cfg(feature = "universal_fw")]
|
||||
eos__sign_transaction = 260, // "Sign transaction"
|
||||
send__sign_transaction = 260, // "Sign transaction"
|
||||
#[cfg(feature = "universal_fw")]
|
||||
eos__threshold = 261, // "Threshold:"
|
||||
#[cfg(feature = "universal_fw")]
|
||||
@ -940,7 +939,7 @@ pub enum TranslatedString {
|
||||
send__title_sending_amount = 650, // "Sending amount"
|
||||
send__title_sending_to = 651, // "Sending to"
|
||||
send__to_the_total_amount = 652, // "To the total amount:"
|
||||
send__total_amount = 653, // "Total amount:"
|
||||
send__total_amount_colon = 653, // "Total amount:"
|
||||
send__transaction_id = 654, // "Transaction ID:"
|
||||
send__you_are_contributing = 655, // "You are contributing:"
|
||||
share_words__words_in_order = 656, // " words in order."
|
||||
@ -1271,6 +1270,12 @@ pub enum TranslatedString {
|
||||
pin__cancel_description = 870, // "Continue without PIN"
|
||||
pin__cancel_info = 871, // "Without a PIN, anyone can access this device."
|
||||
pin__cancel_setup = 872, // "Cancel PIN setup"
|
||||
send__cancel_sign = 873, // "Cancel sign"
|
||||
send__send_from = 874, // "Send from"
|
||||
instructions__hold_to_sign = 875, // "Hold to sign"
|
||||
confirm_total__fee_rate = 876, // "Fee rate"
|
||||
send__incl_transaction_fee = 877, // "incl. Transaction fee"
|
||||
send__total_amount = 878, // "Total amount"
|
||||
}
|
||||
|
||||
impl TranslatedString {
|
||||
@ -1617,9 +1622,9 @@ impl TranslatedString {
|
||||
Self::coinjoin__title_do_not_disconnect => "Do not disconnect your trezor!",
|
||||
Self::coinjoin__title_progress => "Coinjoin in progress",
|
||||
Self::coinjoin__waiting_for_others => "Waiting for others",
|
||||
Self::confirm_total__fee_rate => "Fee rate:",
|
||||
Self::confirm_total__fee_rate_colon => "Fee rate:",
|
||||
Self::confirm_total__sending_from_account => "Sending from account:",
|
||||
Self::confirm_total__title_fee => "Fee information",
|
||||
Self::confirm_total__title_fee => "Fee info",
|
||||
Self::confirm_total__title_sending_from => "Sending from",
|
||||
Self::debug__loading_seed => "Loading seed",
|
||||
Self::debug__loading_seed_not_recommended => "Loading private seed is not recommended.",
|
||||
@ -1686,8 +1691,7 @@ impl TranslatedString {
|
||||
Self::eos__sell_ram => "Sell RAM",
|
||||
#[cfg(feature = "universal_fw")]
|
||||
Self::eos__sender => "Sender:",
|
||||
#[cfg(feature = "universal_fw")]
|
||||
Self::eos__sign_transaction => "Sign transaction",
|
||||
Self::send__sign_transaction => "Sign transaction",
|
||||
#[cfg(feature = "universal_fw")]
|
||||
Self::eos__threshold => "Threshold:",
|
||||
#[cfg(feature = "universal_fw")]
|
||||
@ -2206,7 +2210,7 @@ impl TranslatedString {
|
||||
Self::send__title_sending_amount => "Sending amount",
|
||||
Self::send__title_sending_to => "Sending to",
|
||||
Self::send__to_the_total_amount => "To the total amount:",
|
||||
Self::send__total_amount => "Total amount:",
|
||||
Self::send__total_amount_colon => "Total amount:",
|
||||
Self::send__transaction_id => "Transaction ID:",
|
||||
Self::send__you_are_contributing => "You are contributing:",
|
||||
Self::share_words__words_in_order => " words in order.",
|
||||
@ -2537,6 +2541,12 @@ impl TranslatedString {
|
||||
Self::pin__cancel_description => "Continue without PIN",
|
||||
Self::pin__cancel_info => "Without a PIN, anyone can access this device.",
|
||||
Self::pin__cancel_setup => "Cancel PIN setup",
|
||||
Self::send__cancel_sign => "Cancel sign",
|
||||
Self::send__send_from => "Send from",
|
||||
Self::instructions__hold_to_sign => "Hold to sign",
|
||||
Self::confirm_total__fee_rate => "Fee rate",
|
||||
Self::send__incl_transaction_fee => "incl. Transaction fee",
|
||||
Self::send__total_amount => "Total amount",
|
||||
}
|
||||
}
|
||||
|
||||
@ -2884,7 +2894,7 @@ impl TranslatedString {
|
||||
Qstr::MP_QSTR_coinjoin__title_do_not_disconnect => Some(Self::coinjoin__title_do_not_disconnect),
|
||||
Qstr::MP_QSTR_coinjoin__title_progress => Some(Self::coinjoin__title_progress),
|
||||
Qstr::MP_QSTR_coinjoin__waiting_for_others => Some(Self::coinjoin__waiting_for_others),
|
||||
Qstr::MP_QSTR_confirm_total__fee_rate => Some(Self::confirm_total__fee_rate),
|
||||
Qstr::MP_QSTR_confirm_total__fee_rate_colon => Some(Self::confirm_total__fee_rate_colon),
|
||||
Qstr::MP_QSTR_confirm_total__sending_from_account => Some(Self::confirm_total__sending_from_account),
|
||||
Qstr::MP_QSTR_confirm_total__title_fee => Some(Self::confirm_total__title_fee),
|
||||
Qstr::MP_QSTR_confirm_total__title_sending_from => Some(Self::confirm_total__title_sending_from),
|
||||
@ -2953,8 +2963,7 @@ impl TranslatedString {
|
||||
Qstr::MP_QSTR_eos__sell_ram => Some(Self::eos__sell_ram),
|
||||
#[cfg(feature = "universal_fw")]
|
||||
Qstr::MP_QSTR_eos__sender => Some(Self::eos__sender),
|
||||
#[cfg(feature = "universal_fw")]
|
||||
Qstr::MP_QSTR_eos__sign_transaction => Some(Self::eos__sign_transaction),
|
||||
Qstr::MP_QSTR_send__sign_transaction => Some(Self::send__sign_transaction),
|
||||
#[cfg(feature = "universal_fw")]
|
||||
Qstr::MP_QSTR_eos__threshold => Some(Self::eos__threshold),
|
||||
#[cfg(feature = "universal_fw")]
|
||||
@ -3473,7 +3482,7 @@ impl TranslatedString {
|
||||
Qstr::MP_QSTR_send__title_sending_amount => Some(Self::send__title_sending_amount),
|
||||
Qstr::MP_QSTR_send__title_sending_to => Some(Self::send__title_sending_to),
|
||||
Qstr::MP_QSTR_send__to_the_total_amount => Some(Self::send__to_the_total_amount),
|
||||
Qstr::MP_QSTR_send__total_amount => Some(Self::send__total_amount),
|
||||
Qstr::MP_QSTR_send__total_amount_colon => Some(Self::send__total_amount_colon),
|
||||
Qstr::MP_QSTR_send__transaction_id => Some(Self::send__transaction_id),
|
||||
Qstr::MP_QSTR_send__you_are_contributing => Some(Self::send__you_are_contributing),
|
||||
Qstr::MP_QSTR_share_words__words_in_order => Some(Self::share_words__words_in_order),
|
||||
@ -3804,6 +3813,12 @@ impl TranslatedString {
|
||||
Qstr::MP_QSTR_pin__cancel_description => Some(Self::pin__cancel_description),
|
||||
Qstr::MP_QSTR_pin__cancel_info => Some(Self::pin__cancel_info),
|
||||
Qstr::MP_QSTR_pin__cancel_setup => Some(Self::pin__cancel_setup),
|
||||
Qstr::MP_QSTR_send__cancel_sign => Some(Self::send__cancel_sign),
|
||||
Qstr::MP_QSTR_send__send_from => Some(Self::send__send_from),
|
||||
Qstr::MP_QSTR_instructions__hold_to_sign => Some(Self::instructions__hold_to_sign),
|
||||
Qstr::MP_QSTR_confirm_total__fee_rate => Some(Self::confirm_total__fee_rate),
|
||||
Qstr::MP_QSTR_send__incl_transaction_fee => Some(Self::send__incl_transaction_fee),
|
||||
Qstr::MP_QSTR_send__total_amount => Some(Self::send__total_amount),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
@ -104,6 +104,7 @@ where
|
||||
|
||||
pub fn with_warning_button(self) -> Self {
|
||||
self.with_button(theme::ICON_WARNING, CancelInfoConfirmMsg::Info, false)
|
||||
.button_styled(theme::button_danger())
|
||||
}
|
||||
|
||||
pub fn button_styled(mut self, style: ButtonStyleSheet) -> Self {
|
||||
|
158
core/embed/rust/src/ui/model_mercury/flow/confirm_output.rs
Normal file
158
core/embed/rust/src/ui/model_mercury/flow/confirm_output.rs
Normal file
@ -0,0 +1,158 @@
|
||||
use crate::{
|
||||
error,
|
||||
micropython::qstr::Qstr,
|
||||
strutil::TString,
|
||||
translations::TR,
|
||||
ui::{
|
||||
component::{ComponentExt, SwipeDirection},
|
||||
flow::{base::Decision, flow_store, FlowMsg, FlowState, FlowStore, SwipeFlow, SwipePage},
|
||||
},
|
||||
};
|
||||
|
||||
use super::super::{
|
||||
component::{
|
||||
AddressDetails, Frame, FrameMsg, PromptScreen, VerticalMenu, VerticalMenuChoiceMsg,
|
||||
},
|
||||
theme,
|
||||
};
|
||||
|
||||
use super::util::ConfirmBlobParams;
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, ToPrimitive)]
|
||||
pub enum ConfirmOutput {
|
||||
Address,
|
||||
Amount,
|
||||
// Tap,
|
||||
Menu,
|
||||
AccountInfo,
|
||||
CancelTap,
|
||||
}
|
||||
|
||||
impl FlowState for ConfirmOutput {
|
||||
fn handle_swipe(&self, direction: SwipeDirection) -> Decision<Self> {
|
||||
match (self, direction) {
|
||||
(ConfirmOutput::Address | ConfirmOutput::Amount, SwipeDirection::Left) => {
|
||||
Decision::Goto(ConfirmOutput::Menu, direction)
|
||||
}
|
||||
(ConfirmOutput::Address, SwipeDirection::Up) => {
|
||||
Decision::Goto(ConfirmOutput::Amount, direction)
|
||||
}
|
||||
(ConfirmOutput::Amount, SwipeDirection::Up) => Decision::Return(FlowMsg::Confirmed),
|
||||
(ConfirmOutput::Amount, SwipeDirection::Down) => {
|
||||
Decision::Goto(ConfirmOutput::Address, direction)
|
||||
}
|
||||
(ConfirmOutput::Menu, SwipeDirection::Right) => {
|
||||
Decision::Goto(ConfirmOutput::Address, direction)
|
||||
}
|
||||
(ConfirmOutput::Menu, SwipeDirection::Left) => {
|
||||
Decision::Goto(ConfirmOutput::AccountInfo, direction)
|
||||
}
|
||||
(ConfirmOutput::AccountInfo | ConfirmOutput::CancelTap, SwipeDirection::Right) => {
|
||||
Decision::Goto(ConfirmOutput::Menu, direction)
|
||||
}
|
||||
_ => Decision::Nothing,
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_event(&self, msg: FlowMsg) -> Decision<Self> {
|
||||
match (self, msg) {
|
||||
(_, FlowMsg::Info) => Decision::Goto(ConfirmOutput::Menu, SwipeDirection::Left),
|
||||
(ConfirmOutput::Menu, FlowMsg::Choice(0)) => {
|
||||
Decision::Goto(ConfirmOutput::AccountInfo, SwipeDirection::Left)
|
||||
}
|
||||
(ConfirmOutput::Menu, FlowMsg::Choice(1)) => {
|
||||
Decision::Goto(ConfirmOutput::CancelTap, SwipeDirection::Left)
|
||||
}
|
||||
(ConfirmOutput::Menu, FlowMsg::Cancelled) => {
|
||||
Decision::Goto(ConfirmOutput::Address, SwipeDirection::Right)
|
||||
}
|
||||
(ConfirmOutput::CancelTap, FlowMsg::Confirmed) => Decision::Return(FlowMsg::Cancelled),
|
||||
(_, FlowMsg::Cancelled) => Decision::Goto(ConfirmOutput::Menu, SwipeDirection::Right),
|
||||
_ => Decision::Nothing,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
use crate::{
|
||||
micropython::{map::Map, obj::Obj, util},
|
||||
ui::layout::obj::LayoutObj,
|
||||
};
|
||||
|
||||
#[allow(clippy::not_unsafe_ptr_arg_deref)]
|
||||
pub extern "C" fn new_confirm_output(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, ConfirmOutput::new_obj) }
|
||||
}
|
||||
|
||||
impl ConfirmOutput {
|
||||
const EXTRA_PADDING: i16 = 6;
|
||||
|
||||
fn new_obj(_args: &[Obj], kwargs: &Map) -> Result<Obj, error::Error> {
|
||||
let title: Option<TString> = kwargs.get(Qstr::MP_QSTR_title)?.try_into_option()?;
|
||||
let account: Option<TString> = kwargs.get(Qstr::MP_QSTR_account)?.try_into_option()?;
|
||||
let account_path: Option<TString> =
|
||||
kwargs.get(Qstr::MP_QSTR_account_path)?.try_into_option()?;
|
||||
|
||||
let address: Obj = kwargs.get(Qstr::MP_QSTR_address)?;
|
||||
let amount: Obj = kwargs.get(Qstr::MP_QSTR_amount)?;
|
||||
|
||||
let chunkify: bool = kwargs.get_or(Qstr::MP_QSTR_chunkify, false)?;
|
||||
let text_mono: bool = kwargs.get_or(Qstr::MP_QSTR_text_mono, true)?;
|
||||
|
||||
// Address
|
||||
let content_address = ConfirmBlobParams::new(TR::words__address.into(), address, None)
|
||||
.with_subtitle(title)
|
||||
.with_menu_button()
|
||||
.with_footer(TR::instructions__swipe_up.into(), None)
|
||||
.with_chunkify(chunkify)
|
||||
.with_text_mono(text_mono)
|
||||
.into_layout()?;
|
||||
// .one_button_request(ButtonRequestCode::ConfirmOutput, br_type);
|
||||
|
||||
// Amount
|
||||
let content_amount = ConfirmBlobParams::new(TR::words__amount.into(), amount, None)
|
||||
.with_subtitle(title)
|
||||
.with_menu_button()
|
||||
.with_footer(TR::instructions__swipe_up.into(), None)
|
||||
.with_text_mono(text_mono)
|
||||
.into_layout()?;
|
||||
// .one_button_request(ButtonRequestCode::ConfirmOutput, br_type);
|
||||
|
||||
// Menu
|
||||
let content_menu = Frame::left_aligned(
|
||||
"".into(),
|
||||
VerticalMenu::empty()
|
||||
.item(theme::ICON_CHEVRON_RIGHT, "Account info".into())
|
||||
.danger(theme::ICON_CANCEL, "Cancel sign".into()),
|
||||
)
|
||||
.with_cancel_button()
|
||||
.map(|msg| match msg {
|
||||
FrameMsg::Content(VerticalMenuChoiceMsg::Selected(i)) => Some(FlowMsg::Choice(i)),
|
||||
FrameMsg::Button(_) => Some(FlowMsg::Cancelled),
|
||||
});
|
||||
|
||||
// AccountInfo
|
||||
let ad = AddressDetails::new(TR::send__send_from.into(), account, account_path)?;
|
||||
let content_account = SwipePage::horizontal(ad).map(|_| Some(FlowMsg::Cancelled));
|
||||
|
||||
// CancelTap
|
||||
let content_cancel_tap = Frame::left_aligned(
|
||||
TR::send__cancel_sign.into(),
|
||||
PromptScreen::new_tap_to_cancel(),
|
||||
)
|
||||
.with_cancel_button()
|
||||
.with_footer(TR::instructions__tap_to_confirm.into(), None)
|
||||
.map(|msg| match msg {
|
||||
FrameMsg::Content(()) => Some(FlowMsg::Confirmed),
|
||||
FrameMsg::Button(_) => Some(FlowMsg::Cancelled),
|
||||
});
|
||||
|
||||
let store = flow_store()
|
||||
.add(content_address)?
|
||||
.add(content_amount)?
|
||||
.add(content_menu)?
|
||||
.add(content_account)?
|
||||
.add(content_cancel_tap)?;
|
||||
let res = SwipeFlow::new(ConfirmOutput::Address, store)?;
|
||||
Ok(LayoutObj::new(res)?.into())
|
||||
}
|
||||
}
|
169
core/embed/rust/src/ui/model_mercury/flow/confirm_summary.rs
Normal file
169
core/embed/rust/src/ui/model_mercury/flow/confirm_summary.rs
Normal file
@ -0,0 +1,169 @@
|
||||
use crate::{
|
||||
error,
|
||||
micropython::{iter::IterBuf, qstr::Qstr},
|
||||
strutil::TString,
|
||||
translations::TR,
|
||||
ui::{
|
||||
component::{ComponentExt, SwipeDirection},
|
||||
flow::{base::Decision, flow_store, FlowMsg, FlowState, FlowStore, SwipeFlow},
|
||||
},
|
||||
};
|
||||
|
||||
use super::super::{
|
||||
component::{Frame, FrameMsg, PromptScreen, VerticalMenu, VerticalMenuChoiceMsg},
|
||||
theme,
|
||||
};
|
||||
|
||||
use super::util::ShowInfoParams;
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, ToPrimitive)]
|
||||
pub enum ConfirmSummary {
|
||||
Summary,
|
||||
Hold,
|
||||
Menu,
|
||||
FeeInfo,
|
||||
AccountInfo,
|
||||
CancelTap,
|
||||
}
|
||||
|
||||
impl FlowState for ConfirmSummary {
|
||||
fn handle_swipe(&self, direction: SwipeDirection) -> Decision<Self> {
|
||||
match (self, direction) {
|
||||
(ConfirmSummary::Summary | ConfirmSummary::Hold, SwipeDirection::Left) => {
|
||||
Decision::Goto(ConfirmSummary::Menu, direction)
|
||||
}
|
||||
(ConfirmSummary::Summary, SwipeDirection::Up) => {
|
||||
Decision::Goto(ConfirmSummary::Hold, direction)
|
||||
}
|
||||
(ConfirmSummary::Hold, SwipeDirection::Down) => {
|
||||
Decision::Goto(ConfirmSummary::Summary, direction)
|
||||
}
|
||||
(ConfirmSummary::Menu, SwipeDirection::Right) => {
|
||||
Decision::Goto(ConfirmSummary::Summary, direction)
|
||||
}
|
||||
(ConfirmSummary::Menu, SwipeDirection::Left) => {
|
||||
Decision::Goto(ConfirmSummary::FeeInfo, direction)
|
||||
}
|
||||
(
|
||||
ConfirmSummary::AccountInfo | ConfirmSummary::FeeInfo | ConfirmSummary::CancelTap,
|
||||
SwipeDirection::Right,
|
||||
) => Decision::Goto(ConfirmSummary::Menu, direction),
|
||||
_ => Decision::Nothing,
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_event(&self, msg: FlowMsg) -> Decision<Self> {
|
||||
match (self, msg) {
|
||||
(_, FlowMsg::Info) => Decision::Goto(ConfirmSummary::Menu, SwipeDirection::Left),
|
||||
(ConfirmSummary::Hold, FlowMsg::Confirmed) => Decision::Return(FlowMsg::Confirmed),
|
||||
(ConfirmSummary::Menu, FlowMsg::Choice(0)) => {
|
||||
Decision::Goto(ConfirmSummary::FeeInfo, SwipeDirection::Left)
|
||||
}
|
||||
(ConfirmSummary::Menu, FlowMsg::Choice(1)) => {
|
||||
Decision::Goto(ConfirmSummary::AccountInfo, SwipeDirection::Left)
|
||||
}
|
||||
(ConfirmSummary::Menu, FlowMsg::Choice(2)) => {
|
||||
Decision::Goto(ConfirmSummary::CancelTap, SwipeDirection::Left)
|
||||
}
|
||||
(ConfirmSummary::Menu, FlowMsg::Cancelled) => {
|
||||
Decision::Goto(ConfirmSummary::Summary, SwipeDirection::Right)
|
||||
}
|
||||
(ConfirmSummary::CancelTap, FlowMsg::Confirmed) => Decision::Return(FlowMsg::Cancelled),
|
||||
(_, FlowMsg::Cancelled) => Decision::Goto(ConfirmSummary::Menu, SwipeDirection::Right),
|
||||
_ => Decision::Nothing,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
use crate::{
|
||||
micropython::{map::Map, obj::Obj, util},
|
||||
ui::layout::obj::LayoutObj,
|
||||
};
|
||||
|
||||
#[allow(clippy::not_unsafe_ptr_arg_deref)]
|
||||
pub extern "C" fn new_confirm_summary(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, ConfirmSummary::new_obj) }
|
||||
}
|
||||
|
||||
impl ConfirmSummary {
|
||||
fn new_obj(_args: &[Obj], kwargs: &Map) -> Result<Obj, error::Error> {
|
||||
let title: TString = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
|
||||
let items: Obj = kwargs.get(Qstr::MP_QSTR_items)?;
|
||||
let account_items: Obj = kwargs.get(Qstr::MP_QSTR_account_items)?;
|
||||
let fee_items: Obj = kwargs.get(Qstr::MP_QSTR_fee_items)?;
|
||||
|
||||
// Summary
|
||||
let mut summary = ShowInfoParams::new(title)
|
||||
.with_menu_button()
|
||||
.with_footer(TR::instructions__swipe_up.into(), None);
|
||||
for pair in IterBuf::new().try_iterate(items)? {
|
||||
let [label, value]: [TString; 2] = util::iter_into_array(pair)?;
|
||||
summary = unwrap!(summary.add(label, value));
|
||||
}
|
||||
let content_summary = summary.into_layout()?;
|
||||
|
||||
// Hold to confirm
|
||||
let content_hold = Frame::left_aligned(
|
||||
TR::send__sign_transaction.into(),
|
||||
PromptScreen::new_hold_to_confirm(),
|
||||
)
|
||||
.with_menu_button()
|
||||
.with_footer(TR::instructions__hold_to_sign.into(), None)
|
||||
.map(|msg| match msg {
|
||||
FrameMsg::Content(()) => Some(FlowMsg::Confirmed),
|
||||
FrameMsg::Button(_) => Some(FlowMsg::Info),
|
||||
});
|
||||
|
||||
// Menu
|
||||
let content_menu = Frame::left_aligned(
|
||||
"".into(),
|
||||
VerticalMenu::empty()
|
||||
.item(theme::ICON_CHEVRON_RIGHT, "Fee info".into())
|
||||
.item(theme::ICON_CHEVRON_RIGHT, "Account info".into())
|
||||
.danger(theme::ICON_CANCEL, "Cancel sign".into()),
|
||||
)
|
||||
.with_cancel_button()
|
||||
.map(|msg| match msg {
|
||||
FrameMsg::Content(VerticalMenuChoiceMsg::Selected(i)) => Some(FlowMsg::Choice(i)),
|
||||
FrameMsg::Button(_) => Some(FlowMsg::Cancelled),
|
||||
});
|
||||
|
||||
// FeeInfo
|
||||
let mut fee = ShowInfoParams::new(TR::confirm_total__title_fee.into()).with_cancel_button();
|
||||
for pair in IterBuf::new().try_iterate(fee_items)? {
|
||||
let [label, value]: [TString; 2] = util::iter_into_array(pair)?;
|
||||
fee = unwrap!(fee.add(label, value));
|
||||
}
|
||||
let content_fee = fee.into_layout()?;
|
||||
|
||||
// AccountInfo
|
||||
let mut account = ShowInfoParams::new(TR::send__send_from.into()).with_cancel_button();
|
||||
for pair in IterBuf::new().try_iterate(account_items)? {
|
||||
let [label, value]: [TString; 2] = util::iter_into_array(pair)?;
|
||||
account = unwrap!(account.add(label, value));
|
||||
}
|
||||
let content_account = account.into_layout()?;
|
||||
|
||||
// CancelTap
|
||||
let content_cancel_tap = Frame::left_aligned(
|
||||
TR::send__cancel_sign.into(),
|
||||
PromptScreen::new_tap_to_cancel(),
|
||||
)
|
||||
.with_cancel_button()
|
||||
.with_footer(TR::instructions__tap_to_confirm.into(), None)
|
||||
.map(|msg| match msg {
|
||||
FrameMsg::Content(()) => Some(FlowMsg::Confirmed),
|
||||
FrameMsg::Button(_) => Some(FlowMsg::Cancelled),
|
||||
});
|
||||
|
||||
let store = flow_store()
|
||||
.add(content_summary)?
|
||||
.add(content_hold)?
|
||||
.add(content_menu)?
|
||||
.add(content_fee)?
|
||||
.add(content_account)?
|
||||
.add(content_cancel_tap)?;
|
||||
let res = SwipeFlow::new(ConfirmSummary::Summary, store)?;
|
||||
Ok(LayoutObj::new(res)?.into())
|
||||
}
|
||||
}
|
@ -1,16 +1,22 @@
|
||||
pub mod confirm_action;
|
||||
pub mod confirm_output;
|
||||
pub mod confirm_reset_create;
|
||||
pub mod confirm_reset_recover;
|
||||
pub mod confirm_set_new_pin;
|
||||
pub mod confirm_summary;
|
||||
pub mod get_address;
|
||||
pub mod prompt_backup;
|
||||
pub mod show_share_words;
|
||||
pub mod warning_hi_prio;
|
||||
|
||||
pub use confirm_action::new_confirm_action;
|
||||
mod util;
|
||||
|
||||
pub use confirm_output::new_confirm_output;
|
||||
pub use confirm_reset_create::ConfirmResetCreate;
|
||||
pub use confirm_reset_recover::ConfirmResetRecover;
|
||||
pub use confirm_set_new_pin::SetNewPin;
|
||||
pub use confirm_summary::new_confirm_summary;
|
||||
pub use get_address::GetAddress;
|
||||
pub use prompt_backup::PromptBackup;
|
||||
pub use show_share_words::ShowShareWords;
|
||||
|
236
core/embed/rust/src/ui/model_mercury/flow/util.rs
Normal file
236
core/embed/rust/src/ui/model_mercury/flow/util.rs
Normal file
@ -0,0 +1,236 @@
|
||||
use super::super::{
|
||||
component::{Frame, FrameMsg},
|
||||
theme,
|
||||
};
|
||||
use crate::{
|
||||
error::Error,
|
||||
maybe_trace::MaybeTrace,
|
||||
micropython::obj::Obj,
|
||||
strutil::TString,
|
||||
ui::{
|
||||
component::{
|
||||
base::ComponentExt,
|
||||
text::paragraphs::{Paragraph, ParagraphSource, ParagraphVecShort, VecExt},
|
||||
Component,
|
||||
},
|
||||
flow::{FlowMsg, Swipable, SwipePage},
|
||||
layout::util::ConfirmBlob,
|
||||
},
|
||||
};
|
||||
use heapless::Vec;
|
||||
|
||||
pub struct ConfirmBlobParams {
|
||||
title: TString<'static>,
|
||||
subtitle: Option<TString<'static>>,
|
||||
footer_instruction: Option<TString<'static>>,
|
||||
footer_description: Option<TString<'static>>,
|
||||
data: Obj,
|
||||
description: Option<TString<'static>>,
|
||||
extra: Option<TString<'static>>,
|
||||
menu_button: bool,
|
||||
chunkify: bool,
|
||||
text_mono: bool,
|
||||
}
|
||||
|
||||
impl ConfirmBlobParams {
|
||||
pub const fn new(
|
||||
title: TString<'static>,
|
||||
data: Obj,
|
||||
description: Option<TString<'static>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
title,
|
||||
subtitle: None,
|
||||
footer_instruction: None,
|
||||
footer_description: None,
|
||||
data,
|
||||
description,
|
||||
extra: None,
|
||||
menu_button: false,
|
||||
chunkify: false,
|
||||
text_mono: true,
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn with_extra(mut self, extra: Option<TString<'static>>) -> Self {
|
||||
self.extra = extra;
|
||||
self
|
||||
}
|
||||
|
||||
pub const fn with_subtitle(mut self, subtitle: Option<TString<'static>>) -> Self {
|
||||
self.subtitle = subtitle;
|
||||
self
|
||||
}
|
||||
|
||||
pub const fn with_menu_button(mut self) -> Self {
|
||||
self.menu_button = true;
|
||||
self
|
||||
}
|
||||
|
||||
pub const fn with_footer(
|
||||
mut self,
|
||||
instruction: TString<'static>,
|
||||
description: Option<TString<'static>>,
|
||||
) -> Self {
|
||||
self.footer_instruction = Some(instruction);
|
||||
self.footer_description = description;
|
||||
self
|
||||
}
|
||||
|
||||
pub const fn with_chunkify(mut self, chunkify: bool) -> Self {
|
||||
self.chunkify = chunkify;
|
||||
self
|
||||
}
|
||||
|
||||
pub const fn with_text_mono(mut self, text_mono: bool) -> Self {
|
||||
self.text_mono = text_mono;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn into_layout(
|
||||
self,
|
||||
) -> Result<impl Component<Msg = FlowMsg> + Swipable + MaybeTrace, 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,
|
||||
extra_font: &theme::TEXT_DEMIBOLD,
|
||||
data_font: if self.chunkify {
|
||||
let data: TString = self.data.try_into()?;
|
||||
theme::get_chunkified_text_style(data.len())
|
||||
} else if self.text_mono {
|
||||
&theme::TEXT_MONO
|
||||
} else {
|
||||
&theme::TEXT_NORMAL
|
||||
},
|
||||
}
|
||||
.into_paragraphs();
|
||||
|
||||
let page = SwipePage::vertical(paragraphs);
|
||||
let mut frame = Frame::left_aligned(self.title, page);
|
||||
if let Some(subtitle) = self.subtitle {
|
||||
frame = frame.with_subtitle(subtitle);
|
||||
}
|
||||
if self.menu_button {
|
||||
frame = frame.with_menu_button();
|
||||
}
|
||||
if let Some(instruction) = self.footer_instruction {
|
||||
frame = frame.with_footer(instruction, self.footer_description);
|
||||
}
|
||||
Ok(frame.map(|msg| matches!(msg, FrameMsg::Button(_)).then_some(FlowMsg::Info)))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ShowInfoParams {
|
||||
title: TString<'static>,
|
||||
subtitle: Option<TString<'static>>,
|
||||
menu_button: bool,
|
||||
cancel_button: bool,
|
||||
footer_instruction: Option<TString<'static>>,
|
||||
footer_description: Option<TString<'static>>,
|
||||
chunkify: bool,
|
||||
items: Vec<(TString<'static>, TString<'static>), 4>,
|
||||
}
|
||||
|
||||
impl ShowInfoParams {
|
||||
pub const fn new(title: TString<'static>) -> Self {
|
||||
Self {
|
||||
title,
|
||||
subtitle: None,
|
||||
menu_button: false,
|
||||
cancel_button: false,
|
||||
footer_instruction: None,
|
||||
footer_description: None,
|
||||
chunkify: false,
|
||||
items: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add(mut self, key: TString<'static>, value: TString<'static>) -> Option<Self> {
|
||||
if self.items.push((key, value)).is_ok() {
|
||||
Some(self)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn with_subtitle(mut self, subtitle: Option<TString<'static>>) -> Self {
|
||||
self.subtitle = subtitle;
|
||||
self
|
||||
}
|
||||
|
||||
pub const fn with_menu_button(mut self) -> Self {
|
||||
self.menu_button = true;
|
||||
self
|
||||
}
|
||||
|
||||
pub const fn with_cancel_button(mut self) -> Self {
|
||||
self.cancel_button = true;
|
||||
self
|
||||
}
|
||||
|
||||
pub const fn with_footer(
|
||||
mut self,
|
||||
instruction: TString<'static>,
|
||||
description: Option<TString<'static>>,
|
||||
) -> Self {
|
||||
self.footer_instruction = Some(instruction);
|
||||
self.footer_description = description;
|
||||
self
|
||||
}
|
||||
|
||||
pub const fn with_chunkify(mut self, chunkify: bool) -> Self {
|
||||
self.chunkify = chunkify;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn into_layout(
|
||||
self,
|
||||
) -> Result<impl Component<Msg = FlowMsg> + Swipable + MaybeTrace, Error> {
|
||||
let mut paragraphs = ParagraphVecShort::new();
|
||||
let mut first: bool = true;
|
||||
for item in self.items {
|
||||
// FIXME: padding:
|
||||
if !first {
|
||||
paragraphs.add(Paragraph::new::<TString<'static>>(
|
||||
&theme::TEXT_SUB_GREY,
|
||||
" ".into(),
|
||||
));
|
||||
}
|
||||
first = false;
|
||||
paragraphs.add(Paragraph::new(&theme::TEXT_SUB_GREY, item.0).no_break());
|
||||
if self.chunkify {
|
||||
paragraphs.add(Paragraph::new(
|
||||
theme::get_chunkified_text_style(item.1.len()),
|
||||
item.1,
|
||||
));
|
||||
} else {
|
||||
paragraphs.add(Paragraph::new(&theme::TEXT_MONO_GREY_LIGHT, item.1));
|
||||
}
|
||||
}
|
||||
|
||||
let mut frame = Frame::left_aligned(
|
||||
self.title,
|
||||
SwipePage::vertical(paragraphs.into_paragraphs()),
|
||||
);
|
||||
if let Some(subtitle) = self.subtitle {
|
||||
frame = frame.with_subtitle(subtitle);
|
||||
}
|
||||
if self.cancel_button {
|
||||
frame = frame.with_cancel_button();
|
||||
} else if self.menu_button {
|
||||
frame = frame.with_menu_button();
|
||||
}
|
||||
if let Some(instruction) = self.footer_instruction {
|
||||
frame = frame.with_footer(instruction, self.footer_description);
|
||||
}
|
||||
Ok(frame.map(move |msg| {
|
||||
matches!(msg, FrameMsg::Button(_)).then_some(if self.cancel_button {
|
||||
FlowMsg::Cancelled
|
||||
} else {
|
||||
FlowMsg::Info
|
||||
})
|
||||
}))
|
||||
}
|
||||
}
|
@ -893,6 +893,7 @@ extern "C" fn new_show_warning(n_args: usize, args: *const Obj, kwargs: *mut Map
|
||||
let title: TString = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
|
||||
let description: TString = kwargs.get_or(Qstr::MP_QSTR_description, "".into())?;
|
||||
let value: TString = kwargs.get_or(Qstr::MP_QSTR_value, "".into())?;
|
||||
let action: Option<TString> = kwargs.get(Qstr::MP_QSTR_button)?.try_into_option()?;
|
||||
|
||||
let content = SwipeUpScreen::new(Paragraphs::new([
|
||||
Paragraph::new(&theme::TEXT_MAIN_GREY_LIGHT, description),
|
||||
@ -901,8 +902,7 @@ extern "C" fn new_show_warning(n_args: usize, args: *const Obj, kwargs: *mut Map
|
||||
let obj = LayoutObj::new(
|
||||
Frame::left_aligned(title, content)
|
||||
.with_warning_button()
|
||||
.button_styled(theme::button_warning_low())
|
||||
.with_footer(TR::instructions__swipe_up.into(), None),
|
||||
.with_footer(TR::instructions__swipe_up.into(), action),
|
||||
)?;
|
||||
Ok(obj.into())
|
||||
};
|
||||
@ -1992,6 +1992,28 @@ pub static mp_module_trezorui2: Module = obj_module! {
|
||||
/// ) -> LayoutObj[UiResult]:
|
||||
/// """Warning modal with multiple steps to confirm."""
|
||||
Qstr::MP_QSTR_flow_warning_hi_prio => obj_fn_kw!(0, flow::warning_hi_prio::new_warning_hi_prio).as_obj(),
|
||||
|
||||
/// def flow_confirm_output(
|
||||
/// *,
|
||||
/// title: str | None,
|
||||
/// address: str,
|
||||
/// amount: str,
|
||||
/// chunkify: bool,
|
||||
/// account: str | None,
|
||||
/// account_path: str | None,
|
||||
/// ) -> LayoutObj[UiResult]:
|
||||
/// """Confirm recipient."""
|
||||
Qstr::MP_QSTR_flow_confirm_output => obj_fn_kw!(0, flow::new_confirm_output).as_obj(),
|
||||
|
||||
/// def flow_confirm_summary(
|
||||
/// *,
|
||||
/// title: str,
|
||||
/// items: Iterable[tuple[str, str]],
|
||||
/// account_items: Iterable[tuple[str, str]],
|
||||
/// fee_items: Iterable[tuple[str, str]],
|
||||
/// ) -> LayoutObj[UiResult]:
|
||||
/// """Total summary and hold to confirm."""
|
||||
Qstr::MP_QSTR_flow_confirm_summary => obj_fn_kw!(0, flow::new_confirm_summary).as_obj(),
|
||||
};
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -658,7 +658,7 @@ extern "C" fn new_confirm_total(n_args: usize, args: *const Obj, kwargs: *mut Ma
|
||||
.newline()
|
||||
.newline()
|
||||
.newline_half()
|
||||
.text_bold(TR::confirm_total__fee_rate)
|
||||
.text_bold(TR::confirm_total__fee_rate_colon)
|
||||
.newline()
|
||||
.text_mono(fee_rate_amount);
|
||||
|
||||
|
@ -549,6 +549,30 @@ def flow_warning_hi_prio(
|
||||
value: str = "",
|
||||
) -> LayoutObj[UiResult]:
|
||||
"""Warning modal with multiple steps to confirm."""
|
||||
|
||||
|
||||
# rust/src/ui/model_mercury/layout.rs
|
||||
def flow_confirm_output(
|
||||
*,
|
||||
title: str | None,
|
||||
address: str,
|
||||
amount: str,
|
||||
chunkify: bool,
|
||||
account: str | None,
|
||||
account_path: str | None,
|
||||
) -> LayoutObj[UiResult]:
|
||||
"""Confirm recipient."""
|
||||
|
||||
|
||||
# rust/src/ui/model_mercury/layout.rs
|
||||
def flow_confirm_summary(
|
||||
*,
|
||||
title: str,
|
||||
items: Iterable[tuple[str, str]],
|
||||
account_items: Iterable[tuple[str, str]],
|
||||
fee_items: Iterable[tuple[str, str]],
|
||||
) -> LayoutObj[UiResult]:
|
||||
"""Total summary and hold to confirm."""
|
||||
CONFIRMED: UiResult
|
||||
CANCELLED: UiResult
|
||||
INFO: UiResult
|
||||
|
@ -235,9 +235,10 @@ class TR:
|
||||
coinjoin__title_do_not_disconnect: str = "Do not disconnect your trezor!"
|
||||
coinjoin__title_progress: str = "Coinjoin in progress"
|
||||
coinjoin__waiting_for_others: str = "Waiting for others"
|
||||
confirm_total__fee_rate: str = "Fee rate:"
|
||||
confirm_total__fee_rate: str = "Fee rate"
|
||||
confirm_total__fee_rate_colon: str = "Fee rate:"
|
||||
confirm_total__sending_from_account: str = "Sending from account:"
|
||||
confirm_total__title_fee: str = "Fee information"
|
||||
confirm_total__title_fee: str = "Fee info"
|
||||
confirm_total__title_sending_from: str = "Sending from"
|
||||
debug__loading_seed: str = "Loading seed"
|
||||
debug__loading_seed_not_recommended: str = "Loading private seed is not recommended."
|
||||
@ -275,7 +276,6 @@ class TR:
|
||||
eos__requirement: str = "Requirement:"
|
||||
eos__sell_ram: str = "Sell RAM"
|
||||
eos__sender: str = "Sender:"
|
||||
eos__sign_transaction: str = "Sign transaction"
|
||||
eos__threshold: str = "Threshold:"
|
||||
eos__to: str = "To:"
|
||||
eos__transfer: str = "Transfer:"
|
||||
@ -364,6 +364,7 @@ class TR:
|
||||
inputs__space: str = "SPACE"
|
||||
instructions__continue_in_app: str = "Continue in the app"
|
||||
instructions__hold_to_confirm: str = "Hold to confirm"
|
||||
instructions__hold_to_sign: str = "Hold to sign"
|
||||
instructions__swipe_up: str = "Swipe up"
|
||||
instructions__tap_to_confirm: str = "Tap to confirm"
|
||||
joint__title: str = "Joint transaction"
|
||||
@ -675,11 +676,15 @@ class TR:
|
||||
sd_card__wanna_format: str = "Do you really want to format the SD card?"
|
||||
sd_card__wrong_sd_card: str = "Wrong SD card."
|
||||
send__address_path: str = "address path"
|
||||
send__cancel_sign: str = "Cancel sign"
|
||||
send__confirm_sending: str = "Sending amount"
|
||||
send__from_multiple_accounts: str = "Sending from multiple accounts."
|
||||
send__incl_transaction_fee: str = "incl. Transaction fee"
|
||||
send__including_fee: str = "Including fee:"
|
||||
send__maximum_fee: str = "Maximum fee:"
|
||||
send__receiving_to_multisig: str = "Receiving to a multisig address."
|
||||
send__send_from: str = "Send from"
|
||||
send__sign_transaction: str = "Sign transaction"
|
||||
send__title_confirm_sending: str = "Confirm sending"
|
||||
send__title_joint_transaction: str = "Joint transaction"
|
||||
send__title_receiving_to: str = "Receiving to"
|
||||
@ -687,7 +692,8 @@ class TR:
|
||||
send__title_sending_amount: str = "Sending amount"
|
||||
send__title_sending_to: str = "Sending to"
|
||||
send__to_the_total_amount: str = "To the total amount:"
|
||||
send__total_amount: str = "Total amount:"
|
||||
send__total_amount: str = "Total amount"
|
||||
send__total_amount_colon: str = "Total amount:"
|
||||
send__transaction_id: str = "Transaction ID:"
|
||||
send__you_are_contributing: str = "You are contributing:"
|
||||
share_words__words_in_order: str = " words in order."
|
||||
|
@ -121,6 +121,7 @@ class Approver:
|
||||
self,
|
||||
txo: TxOutput,
|
||||
script_pubkey: bytes,
|
||||
tx_info: TxInfo | None,
|
||||
orig_txo: TxOutput | None = None,
|
||||
) -> None:
|
||||
await self._add_output(txo, script_pubkey)
|
||||
@ -193,11 +194,12 @@ class BasicApprover(Approver):
|
||||
self,
|
||||
txo: TxOutput,
|
||||
script_pubkey: bytes,
|
||||
tx_info: TxInfo | None,
|
||||
orig_txo: TxOutput | None = None,
|
||||
) -> None:
|
||||
from trezor.enums import OutputScriptType
|
||||
|
||||
await super().add_external_output(txo, script_pubkey, orig_txo)
|
||||
await super().add_external_output(txo, script_pubkey, tx_info, orig_txo)
|
||||
|
||||
if orig_txo:
|
||||
if txo.amount < orig_txo.amount:
|
||||
@ -230,6 +232,7 @@ class BasicApprover(Approver):
|
||||
"Adding new OP_RETURN outputs in replacement transactions is not supported."
|
||||
)
|
||||
elif txo.payment_req_index is None or self.show_payment_req_details:
|
||||
source_path = tx_info.wallet_path.get_path() if tx_info else None
|
||||
# Ask user to confirm output, unless it is part of a payment
|
||||
# request, which gets confirmed separately.
|
||||
await helpers.confirm_output(
|
||||
@ -238,6 +241,7 @@ class BasicApprover(Approver):
|
||||
self.amount_unit,
|
||||
self.external_output_index,
|
||||
self.chunkify,
|
||||
source_path,
|
||||
)
|
||||
self.external_output_index += 1
|
||||
|
||||
|
@ -530,7 +530,9 @@ class Bitcoin:
|
||||
# Output is change and does not need approval.
|
||||
await approver.add_change_output(txo, script_pubkey)
|
||||
else:
|
||||
await approver.add_external_output(txo, script_pubkey, orig_txo)
|
||||
await approver.add_external_output(
|
||||
txo, script_pubkey, self.tx_info, orig_txo
|
||||
)
|
||||
|
||||
self.tx_info.add_output(txo, script_pubkey)
|
||||
|
||||
|
@ -92,7 +92,9 @@ class DecredApprover(BasicApprover):
|
||||
) -> None:
|
||||
# NOTE: The following calls Approver.add_external_output(), not BasicApprover.add_external_output().
|
||||
# This is needed to skip calling helpers.confirm_output(), which is what BasicApprover would do.
|
||||
await super(BasicApprover, self).add_external_output(txo, script_pubkey, None)
|
||||
await super(BasicApprover, self).add_external_output(
|
||||
txo, script_pubkey, None, None
|
||||
)
|
||||
await helpers.confirm_decred_sstx_submission(txo, self.coin, self.amount_unit)
|
||||
|
||||
|
||||
|
@ -45,12 +45,14 @@ class UiConfirmOutput(UiConfirm):
|
||||
amount_unit: AmountUnit,
|
||||
output_index: int,
|
||||
chunkify: bool,
|
||||
address_n: Bip32Path | None,
|
||||
):
|
||||
self.output = output
|
||||
self.coin = coin
|
||||
self.amount_unit = amount_unit
|
||||
self.output_index = output_index
|
||||
self.chunkify = chunkify
|
||||
self.address_n = address_n
|
||||
|
||||
def confirm_dialog(self) -> Awaitable[Any]:
|
||||
return layout.confirm_output(
|
||||
@ -59,6 +61,7 @@ class UiConfirmOutput(UiConfirm):
|
||||
self.amount_unit,
|
||||
self.output_index,
|
||||
self.chunkify,
|
||||
self.address_n,
|
||||
)
|
||||
|
||||
|
||||
@ -241,8 +244,12 @@ class UiConfirmMultipleAccounts(UiConfirm):
|
||||
return layout.confirm_multiple_accounts()
|
||||
|
||||
|
||||
def confirm_output(output: TxOutput, coin: CoinInfo, amount_unit: AmountUnit, output_index: int, chunkify: bool) -> Awaitable[None]: # type: ignore [awaitable-return-type]
|
||||
return (yield UiConfirmOutput(output, coin, amount_unit, output_index, chunkify)) # type: ignore [awaitable-return-type]
|
||||
def confirm_output(output: TxOutput, coin: CoinInfo, amount_unit: AmountUnit, output_index: int, chunkify: bool, address_n: Bip32Path | None) -> Awaitable[None]: # type: ignore [awaitable-return-type]
|
||||
return (
|
||||
yield UiConfirmOutput( # type: ignore [awaitable-return-type]
|
||||
output, coin, amount_unit, output_index, chunkify, address_n
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def confirm_decred_sstx_submission(output: TxOutput, coin: CoinInfo, amount_unit: AmountUnit) -> Awaitable[None]: # type: ignore [awaitable-return-type]
|
||||
|
@ -62,6 +62,7 @@ async def confirm_output(
|
||||
amount_unit: AmountUnit,
|
||||
output_index: int,
|
||||
chunkify: bool,
|
||||
address_n: Bip32Path | None,
|
||||
) -> None:
|
||||
from trezor.enums import OutputScriptType
|
||||
|
||||
@ -119,6 +120,8 @@ async def confirm_output(
|
||||
address_label=address_label,
|
||||
output_index=output_index,
|
||||
chunkify=chunkify,
|
||||
source_account=account_label(coin, address_n),
|
||||
source_account_path=address_n_to_str(address_n) if address_n else None,
|
||||
)
|
||||
|
||||
await layout
|
||||
@ -245,7 +248,8 @@ async def confirm_total(
|
||||
format_coin_amount(spending, coin, amount_unit),
|
||||
format_coin_amount(fee, coin, amount_unit),
|
||||
fee_rate_amount=format_fee_rate(fee_rate, coin) if fee_rate >= 0 else None,
|
||||
account_label=account_label(coin, address_n),
|
||||
source_account=account_label(coin, address_n),
|
||||
source_account_path=address_n_to_str(address_n) if address_n else None,
|
||||
)
|
||||
|
||||
|
||||
|
@ -14,7 +14,7 @@ async def require_sign_tx(num_actions: int) -> None:
|
||||
|
||||
await confirm_action(
|
||||
"confirm_tx",
|
||||
TR.eos__sign_transaction,
|
||||
TR.send__sign_transaction,
|
||||
description=TR.eos__about_to_sign_template,
|
||||
description_param=format_plural(
|
||||
"{count} {plural}", num_actions, TR.plurals__sign_x_actions
|
||||
|
@ -187,7 +187,7 @@ class RustLayout(ui.Layout):
|
||||
# Turn the brightness on again.
|
||||
ui.backlight_fade(self.BACKLIGHT_LEVEL)
|
||||
|
||||
def handle_input_and_rendering(self) -> loop.Task: # type: ignore [awaitable-is-generator]
|
||||
def handle_input_and_rendering(self) -> loop.Task:
|
||||
from trezor import workflow
|
||||
|
||||
touch = loop.wait(io.TOUCH)
|
||||
@ -203,7 +203,7 @@ class RustLayout(ui.Layout):
|
||||
raise ui.Result(msg)
|
||||
self._paint()
|
||||
|
||||
def handle_timers(self) -> loop.Task: # type: ignore [awaitable-is-generator]
|
||||
def handle_timers(self) -> loop.Task:
|
||||
while True:
|
||||
# Using `yield` instead of `await` to avoid allocations.
|
||||
token = yield self.timer
|
||||
@ -499,9 +499,9 @@ async def show_warning(
|
||||
interact(
|
||||
RustLayout(
|
||||
trezorui2.show_warning(
|
||||
title=content,
|
||||
description=subheader or "",
|
||||
button=button,
|
||||
title=TR.words__important,
|
||||
value=content,
|
||||
button=subheader or TR.words__continue_anyway,
|
||||
)
|
||||
),
|
||||
br_type,
|
||||
@ -539,58 +539,35 @@ async def confirm_output(
|
||||
address_label: str | None = None,
|
||||
output_index: int | None = None,
|
||||
chunkify: bool = False,
|
||||
source_account: str | None = None,
|
||||
source_account_path: str | None = None,
|
||||
) -> None:
|
||||
if title is not None:
|
||||
# TODO: handle translation
|
||||
if title.upper().startswith("CONFIRM "):
|
||||
title = title[len("CONFIRM ") :]
|
||||
amount_title = title
|
||||
recipient_title = title
|
||||
if address_label is not None:
|
||||
title = address_label
|
||||
elif title is not None:
|
||||
pass
|
||||
elif output_index is not None:
|
||||
amount_title = f"{TR.words__amount} #{output_index + 1}"
|
||||
recipient_title = f"{TR.words__recipient} #{output_index + 1}"
|
||||
title = f"{TR.words__recipient} #{output_index + 1}"
|
||||
else:
|
||||
amount_title = TR.send__confirm_sending
|
||||
recipient_title = TR.send__title_sending_to
|
||||
title = TR.send__title_sending_to
|
||||
|
||||
while True:
|
||||
result = await interact(
|
||||
# TODO: this should send 2x ButtonRequest
|
||||
await raise_if_not_confirmed(
|
||||
interact(
|
||||
RustLayout(
|
||||
trezorui2.confirm_value(
|
||||
title=recipient_title,
|
||||
subtitle=address_label,
|
||||
description=None,
|
||||
value=address,
|
||||
verb=TR.buttons__continue,
|
||||
hold=False,
|
||||
info_button=False,
|
||||
trezorui2.flow_confirm_output(
|
||||
address=address,
|
||||
amount=amount,
|
||||
title=title,
|
||||
chunkify=chunkify,
|
||||
account=source_account,
|
||||
account_path=source_account_path,
|
||||
)
|
||||
),
|
||||
"confirm_output",
|
||||
br_code,
|
||||
)
|
||||
if result is not CONFIRMED:
|
||||
raise ActionCancelled
|
||||
|
||||
result = await interact(
|
||||
RustLayout(
|
||||
trezorui2.confirm_value(
|
||||
title=amount_title,
|
||||
subtitle=None,
|
||||
description=None,
|
||||
value=amount,
|
||||
verb=None if hold else TR.buttons__confirm,
|
||||
verb_cancel="^",
|
||||
hold=hold,
|
||||
info_button=False,
|
||||
)
|
||||
),
|
||||
"confirm_output",
|
||||
br_code,
|
||||
)
|
||||
if result is CONFIRMED:
|
||||
return
|
||||
)
|
||||
|
||||
|
||||
async def should_show_payment_request_details(
|
||||
@ -627,7 +604,7 @@ async def should_show_payment_request_details(
|
||||
|
||||
async def should_show_more(
|
||||
title: str,
|
||||
para: Iterable[tuple[int, str]],
|
||||
para: Iterable[tuple[int, str | bytes]],
|
||||
button_text: str | None = None,
|
||||
br_type: str = "should_show_more",
|
||||
br_code: ButtonRequestType = BR_TYPE_OTHER,
|
||||
@ -876,31 +853,42 @@ async def confirm_total(
|
||||
title: str | None = None,
|
||||
total_label: str | None = None,
|
||||
fee_label: str | None = None,
|
||||
account_label: str | None = None,
|
||||
source_account: str | None = None,
|
||||
source_account_path: str | None = None,
|
||||
fee_rate_amount: str | None = None,
|
||||
br_type: str = "confirm_total",
|
||||
br_code: ButtonRequestType = ButtonRequestType.SignTx,
|
||||
) -> None:
|
||||
title = title or TR.words__title_summary # def_arg
|
||||
total_label = total_label or TR.send__total_amount # def_arg
|
||||
fee_label = fee_label or TR.send__including_fee # def_arg
|
||||
fee_label = fee_label or TR.send__incl_transaction_fee # def_arg
|
||||
|
||||
items = [
|
||||
(total_label, total_amount),
|
||||
(fee_label, fee_amount),
|
||||
]
|
||||
info_items = []
|
||||
if account_label:
|
||||
info_items.append((TR.confirm_total__sending_from_account, account_label))
|
||||
fee_items = []
|
||||
account_items = []
|
||||
if source_account:
|
||||
account_items.append((TR.confirm_total__sending_from_account, source_account))
|
||||
if source_account_path:
|
||||
account_items.append((TR.address_details__derivation_path, source_account_path))
|
||||
if fee_rate_amount:
|
||||
info_items.append((TR.confirm_total__fee_rate, fee_rate_amount))
|
||||
fee_items.append((TR.confirm_total__fee_rate, fee_rate_amount))
|
||||
|
||||
await confirm_summary(
|
||||
items,
|
||||
TR.words__title_summary,
|
||||
info_items=info_items,
|
||||
br_type=br_type,
|
||||
br_code=br_code,
|
||||
await raise_if_not_confirmed(
|
||||
interact(
|
||||
RustLayout(
|
||||
trezorui2.flow_confirm_summary(
|
||||
title=title,
|
||||
items=items,
|
||||
fee_items=fee_items,
|
||||
account_items=account_items,
|
||||
)
|
||||
),
|
||||
br_type,
|
||||
br_code,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@ -912,23 +900,21 @@ async def confirm_summary(
|
||||
br_type: str = "confirm_total",
|
||||
br_code: ButtonRequestType = ButtonRequestType.SignTx,
|
||||
) -> None:
|
||||
# TODO: info_title
|
||||
title = title or TR.words__title_summary # def_arg
|
||||
|
||||
total_layout = RustLayout(
|
||||
trezorui2.confirm_total(
|
||||
title=title,
|
||||
items=items,
|
||||
info_button=bool(info_items),
|
||||
await raise_if_not_confirmed(
|
||||
RustLayout(
|
||||
trezorui2.flow_confirm_summary(
|
||||
title=title,
|
||||
items=items or (),
|
||||
fee_items=(),
|
||||
account_items=info_items or (),
|
||||
)
|
||||
)
|
||||
# TODO br_type,
|
||||
# TODO br_code,
|
||||
)
|
||||
info_items = info_items or []
|
||||
info_layout = RustLayout(
|
||||
trezorui2.show_info_with_cancel(
|
||||
title=info_title if info_title else TR.words__title_information,
|
||||
items=info_items,
|
||||
)
|
||||
)
|
||||
await raise_if_not_confirmed(with_info(total_layout, info_layout, br_type, br_code))
|
||||
|
||||
|
||||
if not utils.BITCOIN_ONLY:
|
||||
@ -1046,20 +1032,14 @@ if not utils.BITCOIN_ONLY:
|
||||
|
||||
|
||||
async def confirm_joint_total(spending_amount: str, total_amount: str) -> None:
|
||||
await raise_if_not_confirmed(
|
||||
interact(
|
||||
RustLayout(
|
||||
trezorui2.confirm_total(
|
||||
title=TR.send__title_joint_transaction,
|
||||
items=[
|
||||
(TR.send__you_are_contributing, spending_amount),
|
||||
(TR.send__to_the_total_amount, total_amount),
|
||||
],
|
||||
)
|
||||
),
|
||||
"confirm_joint_total",
|
||||
ButtonRequestType.SignTx,
|
||||
)
|
||||
await confirm_summary(
|
||||
items=[
|
||||
(TR.send__you_are_contributing, spending_amount),
|
||||
(TR.send__to_the_total_amount, total_amount),
|
||||
],
|
||||
title=TR.send__title_joint_transaction,
|
||||
br_type="confirm_joint_total",
|
||||
br_code=ButtonRequestType.SignTx,
|
||||
)
|
||||
|
||||
|
||||
|
@ -734,6 +734,8 @@ async def confirm_output(
|
||||
address_label: str | None = None,
|
||||
output_index: int | None = None,
|
||||
chunkify: bool = False,
|
||||
source_account: str | None = None, # ignored on safe 3
|
||||
source_account_path: str | None = None, # ignored on safe 3
|
||||
) -> None:
|
||||
title = title or TR.send__confirm_sending # def_arg
|
||||
address_title = TR.words__recipient
|
||||
@ -1096,11 +1098,12 @@ def confirm_total(
|
||||
title: str | None = None,
|
||||
total_label: str | None = None,
|
||||
fee_label: str | None = None,
|
||||
account_label: str | None = None,
|
||||
source_account: str | None = None,
|
||||
source_account_path: str | None = None,
|
||||
br_type: str = "confirm_total",
|
||||
br_code: ButtonRequestType = ButtonRequestType.SignTx,
|
||||
) -> Awaitable[None]:
|
||||
total_label = total_label or TR.send__total_amount # def_arg
|
||||
total_label = total_label or TR.send__total_amount_colon # def_arg
|
||||
fee_label = fee_label or TR.send__including_fee # def_arg
|
||||
return raise_if_not_confirmed(
|
||||
interact(
|
||||
@ -1110,7 +1113,7 @@ def confirm_total(
|
||||
total_amount=total_amount, # type: ignore [No parameter named]
|
||||
fee_amount=fee_amount, # type: ignore [No parameter named]
|
||||
fee_rate_amount=fee_rate_amount, # type: ignore [No parameter named]
|
||||
account_label=account_label, # type: ignore [No parameter named]
|
||||
account_label=source_account, # type: ignore [No parameter named]
|
||||
total_label=total_label, # type: ignore [No parameter named]
|
||||
fee_label=fee_label, # type: ignore [No parameter named]
|
||||
)
|
||||
|
@ -641,6 +641,8 @@ async def confirm_output(
|
||||
address_label: str | None = None,
|
||||
output_index: int | None = None,
|
||||
chunkify: bool = False,
|
||||
source_account: str | None = None, # ignored on model t
|
||||
source_account_path: str | None = None, # ignored on model t
|
||||
) -> None:
|
||||
if title is not None:
|
||||
# TODO: handle translation:
|
||||
@ -978,13 +980,14 @@ def confirm_total(
|
||||
title: str | None = None,
|
||||
total_label: str | None = None,
|
||||
fee_label: str | None = None,
|
||||
account_label: str | None = None,
|
||||
source_account: str | None = None,
|
||||
source_account_path: str | None = None,
|
||||
fee_rate_amount: str | None = None,
|
||||
br_type: str = "confirm_total",
|
||||
br_code: ButtonRequestType = ButtonRequestType.SignTx,
|
||||
) -> Awaitable[None]:
|
||||
title = title or TR.words__title_summary # def_arg
|
||||
total_label = total_label or TR.send__total_amount # def_arg
|
||||
total_label = total_label or TR.send__total_amount_colon # def_arg
|
||||
fee_label = fee_label or TR.send__including_fee # def_arg
|
||||
|
||||
items = [
|
||||
@ -992,10 +995,10 @@ def confirm_total(
|
||||
(fee_label, fee_amount),
|
||||
]
|
||||
info_items = []
|
||||
if account_label:
|
||||
info_items.append((TR.confirm_total__sending_from_account, account_label))
|
||||
if source_account:
|
||||
info_items.append((TR.confirm_total__sending_from_account, source_account))
|
||||
if fee_rate_amount:
|
||||
info_items.append((TR.confirm_total__fee_rate, fee_rate_amount))
|
||||
info_items.append((TR.confirm_total__fee_rate_colon, fee_rate_amount))
|
||||
|
||||
return confirm_summary(
|
||||
items,
|
||||
|
@ -175,6 +175,7 @@ class TestApprover(unittest.TestCase):
|
||||
authorization = CoinJoinAuthorization(self.msg_auth)
|
||||
approver = CoinJoinApprover(tx, self.coin, authorization)
|
||||
signer = Bitcoin(tx, None, self.coin, approver)
|
||||
tx_info = TxInfo(signer, tx)
|
||||
|
||||
for txi in inputs:
|
||||
if txi.script_type == InputScriptType.EXTERNAL:
|
||||
@ -186,9 +187,9 @@ class TestApprover(unittest.TestCase):
|
||||
if txo.address_n:
|
||||
await_result(approver.add_change_output(txo, script_pubkey=bytes(22)))
|
||||
else:
|
||||
await_result(approver.add_external_output(txo, script_pubkey=bytes(22)))
|
||||
await_result(approver.add_external_output(txo, script_pubkey=bytes(22), tx_info=tx_info))
|
||||
|
||||
await_result(approver.approve_tx(TxInfo(signer, tx), [], None))
|
||||
await_result(approver.approve_tx(tx_info, [], None))
|
||||
|
||||
def test_coinjoin_input_account_depth_mismatch(self):
|
||||
txi = TxInput(
|
||||
|
@ -112,7 +112,7 @@ class TestSignSegwitTxNativeP2WPKH(unittest.TestCase):
|
||||
serialized=EMPTY_SERIALIZED,
|
||||
),
|
||||
TxAckOutput(tx=TxAckOutputWrapper(output=out1)),
|
||||
helpers.UiConfirmOutput(out1, coin, AmountUnit.BITCOIN, 0, False),
|
||||
helpers.UiConfirmOutput(out1, coin, AmountUnit.BITCOIN, 0, False, [H_(49), H_(1), H_(0)]),
|
||||
True,
|
||||
TxRequest(
|
||||
request_type=TXOUTPUT,
|
||||
@ -120,7 +120,7 @@ class TestSignSegwitTxNativeP2WPKH(unittest.TestCase):
|
||||
serialized=EMPTY_SERIALIZED,
|
||||
),
|
||||
TxAckOutput(tx=TxAckOutputWrapper(output=out2)),
|
||||
helpers.UiConfirmOutput(out2, coin, AmountUnit.BITCOIN, 1, False),
|
||||
helpers.UiConfirmOutput(out2, coin, AmountUnit.BITCOIN, 1, False, [H_(49), H_(1), H_(0)]),
|
||||
True,
|
||||
helpers.UiConfirmTotal(
|
||||
12300000, 11000, fee_rate, coin, AmountUnit.BITCOIN, inp1.address_n[:3]
|
||||
@ -309,7 +309,7 @@ class TestSignSegwitTxNativeP2WPKH(unittest.TestCase):
|
||||
serialized=EMPTY_SERIALIZED,
|
||||
),
|
||||
TxAckOutput(tx=TxAckOutputWrapper(output=out1)),
|
||||
helpers.UiConfirmOutput(out1, coin, AmountUnit.BITCOIN, 0, False),
|
||||
helpers.UiConfirmOutput(out1, coin, AmountUnit.BITCOIN, 0, False, [H_(49), H_(1), H_(0)]),
|
||||
True,
|
||||
TxRequest(
|
||||
request_type=TXOUTPUT,
|
||||
|
@ -123,7 +123,7 @@ class TestSignSegwitTxNativeP2WPKH_GRS(unittest.TestCase):
|
||||
serialized=EMPTY_SERIALIZED,
|
||||
),
|
||||
TxAckOutput(tx=TxAckOutputWrapper(output=out1)),
|
||||
helpers.UiConfirmOutput(out1, coin, AmountUnit.BITCOIN, 0, False),
|
||||
helpers.UiConfirmOutput(out1, coin, AmountUnit.BITCOIN, 0, False, [H_(84), H_(1), H_(0)]),
|
||||
True,
|
||||
TxRequest(
|
||||
request_type=TXOUTPUT,
|
||||
@ -131,7 +131,7 @@ class TestSignSegwitTxNativeP2WPKH_GRS(unittest.TestCase):
|
||||
serialized=EMPTY_SERIALIZED,
|
||||
),
|
||||
TxAckOutput(tx=TxAckOutputWrapper(output=out2)),
|
||||
helpers.UiConfirmOutput(out2, coin, AmountUnit.BITCOIN, 1, False),
|
||||
helpers.UiConfirmOutput(out2, coin, AmountUnit.BITCOIN, 1, False, [H_(84), H_(1), H_(0)]),
|
||||
True,
|
||||
helpers.UiConfirmNonDefaultLocktime(tx.lock_time, lock_time_disabled=False),
|
||||
True,
|
||||
@ -332,7 +332,7 @@ class TestSignSegwitTxNativeP2WPKH_GRS(unittest.TestCase):
|
||||
serialized=EMPTY_SERIALIZED,
|
||||
),
|
||||
TxAckOutput(tx=TxAckOutputWrapper(output=out1)),
|
||||
helpers.UiConfirmOutput(out1, coin, AmountUnit.BITCOIN, 0, False),
|
||||
helpers.UiConfirmOutput(out1, coin, AmountUnit.BITCOIN, 0, False, [H_(84), H_(1), H_(0)]),
|
||||
True,
|
||||
TxRequest(
|
||||
request_type=TXOUTPUT,
|
||||
|
@ -114,7 +114,7 @@ class TestSignSegwitTxP2WPKHInP2SH(unittest.TestCase):
|
||||
serialized=EMPTY_SERIALIZED,
|
||||
),
|
||||
TxAckOutput(tx=TxAckOutputWrapper(output=out1)),
|
||||
helpers.UiConfirmOutput(out1, coin, AmountUnit.BITCOIN, 0, False),
|
||||
helpers.UiConfirmOutput(out1, coin, AmountUnit.BITCOIN, 0, False, [H_(49), H_(1), H_(0)]),
|
||||
True,
|
||||
TxRequest(
|
||||
request_type=TXOUTPUT,
|
||||
@ -122,7 +122,7 @@ class TestSignSegwitTxP2WPKHInP2SH(unittest.TestCase):
|
||||
serialized=EMPTY_SERIALIZED,
|
||||
),
|
||||
TxAckOutput(tx=TxAckOutputWrapper(output=out2)),
|
||||
helpers.UiConfirmOutput(out2, coin, AmountUnit.BITCOIN, 1, False),
|
||||
helpers.UiConfirmOutput(out2, coin, AmountUnit.BITCOIN, 1, False, [H_(49), H_(1), H_(0)]),
|
||||
True,
|
||||
helpers.UiConfirmTotal(
|
||||
123445789 + 11000,
|
||||
@ -317,7 +317,7 @@ class TestSignSegwitTxP2WPKHInP2SH(unittest.TestCase):
|
||||
serialized=EMPTY_SERIALIZED,
|
||||
),
|
||||
TxAckOutput(tx=TxAckOutputWrapper(output=out1)),
|
||||
helpers.UiConfirmOutput(out1, coin, AmountUnit.BITCOIN, 0, False),
|
||||
helpers.UiConfirmOutput(out1, coin, AmountUnit.BITCOIN, 0, False, [H_(49), H_(1), H_(0)]),
|
||||
True,
|
||||
TxRequest(
|
||||
request_type=TXOUTPUT,
|
||||
@ -531,7 +531,7 @@ class TestSignSegwitTxP2WPKHInP2SH(unittest.TestCase):
|
||||
serialized=EMPTY_SERIALIZED,
|
||||
),
|
||||
TxAckOutput(tx=TxAckOutputWrapper(output=out1)),
|
||||
helpers.UiConfirmOutput(out1, coin, AmountUnit.BITCOIN, 0, False),
|
||||
helpers.UiConfirmOutput(out1, coin, AmountUnit.BITCOIN, 0, False, [H_(49), H_(1), H_(0)]),
|
||||
True,
|
||||
TxRequest(
|
||||
request_type=TXOUTPUT,
|
||||
|
@ -123,7 +123,7 @@ class TestSignSegwitTxP2WPKHInP2SH_GRS(unittest.TestCase):
|
||||
serialized=EMPTY_SERIALIZED,
|
||||
),
|
||||
TxAckOutput(tx=TxAckOutputWrapper(output=out1)),
|
||||
helpers.UiConfirmOutput(out1, coin, AmountUnit.BITCOIN, 0, False),
|
||||
helpers.UiConfirmOutput(out1, coin, AmountUnit.BITCOIN, 0, False, [H_(49), H_(1), H_(0)]),
|
||||
True,
|
||||
TxRequest(
|
||||
request_type=TXOUTPUT,
|
||||
@ -131,7 +131,7 @@ class TestSignSegwitTxP2WPKHInP2SH_GRS(unittest.TestCase):
|
||||
serialized=EMPTY_SERIALIZED,
|
||||
),
|
||||
TxAckOutput(tx=TxAckOutputWrapper(output=out2)),
|
||||
helpers.UiConfirmOutput(out2, coin, AmountUnit.BITCOIN, 1, False),
|
||||
helpers.UiConfirmOutput(out2, coin, AmountUnit.BITCOIN, 1, False, [H_(49), H_(1), H_(0)]),
|
||||
True,
|
||||
helpers.UiConfirmNonDefaultLocktime(tx.lock_time, lock_time_disabled=False),
|
||||
True,
|
||||
@ -336,7 +336,7 @@ class TestSignSegwitTxP2WPKHInP2SH_GRS(unittest.TestCase):
|
||||
serialized=EMPTY_SERIALIZED,
|
||||
),
|
||||
TxAckOutput(tx=TxAckOutputWrapper(output=out1)),
|
||||
helpers.UiConfirmOutput(out1, coin, AmountUnit.BITCOIN, 0, False),
|
||||
helpers.UiConfirmOutput(out1, coin, AmountUnit.BITCOIN, 0, False, [H_(49), H_(1), H_(0)]),
|
||||
True,
|
||||
TxRequest(
|
||||
request_type=TXOUTPUT,
|
||||
|
@ -180,7 +180,7 @@ class TestSignTxFeeThreshold(unittest.TestCase):
|
||||
serialized=EMPTY_SERIALIZED,
|
||||
),
|
||||
TxAckOutput(tx=TxAckOutputWrapper(output=out1)),
|
||||
helpers.UiConfirmOutput(out1, coin_bitcoin, AmountUnit.BITCOIN, 0, False),
|
||||
helpers.UiConfirmOutput(out1, coin_bitcoin, AmountUnit.BITCOIN, 0, False, None),
|
||||
True,
|
||||
helpers.UiConfirmMultipleAccounts(),
|
||||
True,
|
||||
|
@ -111,7 +111,7 @@ class TestSignTx(unittest.TestCase):
|
||||
serialized=EMPTY_SERIALIZED,
|
||||
),
|
||||
TxAckOutput(tx=TxAckOutputWrapper(output=out1)),
|
||||
helpers.UiConfirmOutput(out1, coin_bitcoin, AmountUnit.BITCOIN, 0, False),
|
||||
helpers.UiConfirmOutput(out1, coin_bitcoin, AmountUnit.BITCOIN, 0, False, [H_(44), H_(0), H_(0)]),
|
||||
True,
|
||||
helpers.UiConfirmTotal(
|
||||
3_801_747,
|
||||
|
@ -112,7 +112,7 @@ class TestSignTxDecred(unittest.TestCase):
|
||||
),
|
||||
),
|
||||
TxAckOutput(tx=TxAckOutputWrapper(output=out1)),
|
||||
helpers.UiConfirmOutput(out1, coin_decred, AmountUnit.BITCOIN, 0, False),
|
||||
helpers.UiConfirmOutput(out1, coin_decred, AmountUnit.BITCOIN, 0, False, [H_(44), H_(1), H_(0)]),
|
||||
True,
|
||||
helpers.UiConfirmTotal(
|
||||
200_000_000,
|
||||
|
@ -112,7 +112,7 @@ class TestSignTx_GRS(unittest.TestCase):
|
||||
serialized=EMPTY_SERIALIZED,
|
||||
),
|
||||
TxAckOutput(tx=TxAckOutputWrapper(output=out1)),
|
||||
helpers.UiConfirmOutput(out1, coin, AmountUnit.BITCOIN, 0, False),
|
||||
helpers.UiConfirmOutput(out1, coin, AmountUnit.BITCOIN, 0, False, [H_(44), H_(17), H_(0)]),
|
||||
True,
|
||||
helpers.UiConfirmTotal(
|
||||
210016, 192, fee_rate, coin, AmountUnit.BITCOIN, inp1.address_n[:3]
|
||||
|
@ -219,7 +219,7 @@
|
||||
"coinjoin__title_do_not_disconnect": "title,2",
|
||||
"coinjoin__title_progress": "title,1",
|
||||
"coinjoin__waiting_for_others": "text,1",
|
||||
"confirm_total__fee_rate": "text,1",
|
||||
"confirm_total__fee_rate_colon": "text,1",
|
||||
"confirm_total__sending_from_account": "text,1",
|
||||
"confirm_total__title_fee": "title,1",
|
||||
"confirm_total__title_sending_from": "title,1",
|
||||
@ -259,7 +259,7 @@
|
||||
"eos__requirement": "text,1",
|
||||
"eos__sell_ram": "text,1",
|
||||
"eos__sender": "text,1",
|
||||
"eos__sign_transaction": "text,1",
|
||||
"send__sign_transaction": "text,1",
|
||||
"eos__threshold": "text,1",
|
||||
"eos__to": "text,1",
|
||||
"eos__transfer": "text,1",
|
||||
@ -654,7 +654,7 @@
|
||||
"send__title_sending_amount": "title,1",
|
||||
"send__title_sending_to": "title,1",
|
||||
"send__to_the_total_amount": "text,1",
|
||||
"send__total_amount": "text,1",
|
||||
"send__total_amount_colon": "text,1",
|
||||
"send__transaction_id": "text,1",
|
||||
"send__you_are_contributing": "text,1",
|
||||
"share_words__words_in_order": "text,2",
|
||||
|
@ -257,7 +257,8 @@
|
||||
"coinjoin__title_do_not_disconnect": "Neodpojujte trezor!",
|
||||
"coinjoin__title_progress": "Probíhá coinjoin",
|
||||
"coinjoin__waiting_for_others": "Čeká se na ostatní",
|
||||
"confirm_total__fee_rate": "Výše poplatku:",
|
||||
"confirm_total__fee_rate": "Výše poplatku",
|
||||
"confirm_total__fee_rate_colon": "Výše poplatku:",
|
||||
"confirm_total__sending_from_account": "Odeslání z účtu:",
|
||||
"confirm_total__title_fee": "Info o poplatcích",
|
||||
"confirm_total__title_sending_from": "Odeslání z",
|
||||
@ -297,7 +298,7 @@
|
||||
"eos__requirement": "Požadavek:",
|
||||
"eos__sell_ram": "Prodat RAM",
|
||||
"eos__sender": "Odesílatel:",
|
||||
"eos__sign_transaction": "Podepsat transakci",
|
||||
"send__sign_transaction": "Podepsat transakci",
|
||||
"eos__threshold": "Počet částí zálohy pro obnovu:",
|
||||
"eos__to": "Komu:",
|
||||
"eos__transfer": "Převod:",
|
||||
@ -699,7 +700,8 @@
|
||||
"send__title_sending_amount": "Odesílání částky",
|
||||
"send__title_sending_to": "Odesílání",
|
||||
"send__to_the_total_amount": "Do celkové částky:",
|
||||
"send__total_amount": "Celková částka:",
|
||||
"send__total_amount": "Celková částka",
|
||||
"send__total_amount_colon": "Celková částka:",
|
||||
"send__transaction_id": "ID transakce:",
|
||||
"send__you_are_contributing": "Přispíváte:",
|
||||
"share_words__words_in_order": " slova v pořadí.",
|
||||
|
@ -257,7 +257,8 @@
|
||||
"coinjoin__title_do_not_disconnect": "Trenne deinen trezor nicht!",
|
||||
"coinjoin__title_progress": "Coinjoin läuft",
|
||||
"coinjoin__waiting_for_others": "Auf andere warten",
|
||||
"confirm_total__fee_rate": "Gebührensatz:",
|
||||
"confirm_total__fee_rate": "Gebührensatz",
|
||||
"confirm_total__fee_rate_colon": "Gebührensatz:",
|
||||
"confirm_total__sending_from_account": "Gesendet von Konto:",
|
||||
"confirm_total__title_fee": "Gebühren-info",
|
||||
"confirm_total__title_sending_from": "Gesendet von",
|
||||
@ -297,7 +298,7 @@
|
||||
"eos__requirement": "Anforderung:",
|
||||
"eos__sell_ram": "RAM verkaufen",
|
||||
"eos__sender": "Sender:",
|
||||
"eos__sign_transaction": "Transaktion signieren",
|
||||
"send__sign_transaction": "Transaktion signieren",
|
||||
"eos__threshold": "Schwelle:",
|
||||
"eos__to": "An:",
|
||||
"eos__transfer": "Überweisung:",
|
||||
@ -699,7 +700,8 @@
|
||||
"send__title_sending_amount": "Betrag senden",
|
||||
"send__title_sending_to": "Senden an",
|
||||
"send__to_the_total_amount": "Gesamtbetrag:",
|
||||
"send__total_amount": "Gesamtbetrag:",
|
||||
"send__total_amount": "Gesamtbetrag",
|
||||
"send__total_amount_colon": "Gesamtbetrag:",
|
||||
"send__transaction_id": "Transaktions-ID:",
|
||||
"send__you_are_contributing": "Dein Anteil:",
|
||||
"share_words__words_in_order": " Wörter der Reihe nach notiert.",
|
||||
|
@ -237,9 +237,10 @@
|
||||
"coinjoin__title_do_not_disconnect": "Do not disconnect your trezor!",
|
||||
"coinjoin__title_progress": "Coinjoin in progress",
|
||||
"coinjoin__waiting_for_others": "Waiting for others",
|
||||
"confirm_total__fee_rate": "Fee rate:",
|
||||
"confirm_total__fee_rate": "Fee rate",
|
||||
"confirm_total__fee_rate_colon": "Fee rate:",
|
||||
"confirm_total__sending_from_account": "Sending from account:",
|
||||
"confirm_total__title_fee": "Fee information",
|
||||
"confirm_total__title_fee": "Fee info",
|
||||
"confirm_total__title_sending_from": "Sending from",
|
||||
"debug__loading_seed": "Loading seed",
|
||||
"debug__loading_seed_not_recommended": "Loading private seed is not recommended.",
|
||||
@ -277,7 +278,6 @@
|
||||
"eos__requirement": "Requirement:",
|
||||
"eos__sell_ram": "Sell RAM",
|
||||
"eos__sender": "Sender:",
|
||||
"eos__sign_transaction": "Sign transaction",
|
||||
"eos__threshold": "Threshold:",
|
||||
"eos__to": "To:",
|
||||
"eos__transfer": "Transfer:",
|
||||
@ -365,9 +365,10 @@
|
||||
"inputs__show": "SHOW",
|
||||
"inputs__space": "SPACE",
|
||||
"instructions__continue_in_app": "Continue in the app",
|
||||
"instructions__hold_to_confirm": "Hold to confirm",
|
||||
"instructions__hold_to_sign": "Hold to sign",
|
||||
"instructions__swipe_up": "Swipe up",
|
||||
"instructions__tap_to_confirm": "Tap to confirm",
|
||||
"instructions__hold_to_confirm": "Hold to confirm",
|
||||
"joint__title": "Joint transaction",
|
||||
"joint__to_the_total_amount": "To the total amount:",
|
||||
"joint__you_are_contributing": "You are contributing:",
|
||||
@ -677,11 +678,15 @@
|
||||
"sd_card__wanna_format": "Do you really want to format the SD card?",
|
||||
"sd_card__wrong_sd_card": "Wrong SD card.",
|
||||
"send__address_path": "address path",
|
||||
"send__cancel_sign": "Cancel sign",
|
||||
"send__confirm_sending": "Sending amount",
|
||||
"send__from_multiple_accounts": "Sending from multiple accounts.",
|
||||
"send__incl_transaction_fee": "incl. Transaction fee",
|
||||
"send__including_fee": "Including fee:",
|
||||
"send__maximum_fee": "Maximum fee:",
|
||||
"send__receiving_to_multisig": "Receiving to a multisig address.",
|
||||
"send__send_from": "Send from",
|
||||
"send__sign_transaction": "Sign transaction",
|
||||
"send__title_confirm_sending": "Confirm sending",
|
||||
"send__title_joint_transaction": "Joint transaction",
|
||||
"send__title_receiving_to": "Receiving to",
|
||||
@ -689,7 +694,8 @@
|
||||
"send__title_sending_amount": "Sending amount",
|
||||
"send__title_sending_to": "Sending to",
|
||||
"send__to_the_total_amount": "To the total amount:",
|
||||
"send__total_amount": "Total amount:",
|
||||
"send__total_amount": "Total amount",
|
||||
"send__total_amount_colon": "Total amount:",
|
||||
"send__transaction_id": "Transaction ID:",
|
||||
"send__you_are_contributing": "You are contributing:",
|
||||
"share_words__words_in_order": " words in order.",
|
||||
|
@ -257,7 +257,8 @@
|
||||
"coinjoin__title_do_not_disconnect": "¡No desconectes el trezor!",
|
||||
"coinjoin__title_progress": "Coinjoin en curso",
|
||||
"coinjoin__waiting_for_others": "Esperando a los demás",
|
||||
"confirm_total__fee_rate": "Comisión:",
|
||||
"confirm_total__fee_rate": "Comisión",
|
||||
"confirm_total__fee_rate_colon": "Comisión:",
|
||||
"confirm_total__sending_from_account": "Envío desde cuenta:",
|
||||
"confirm_total__title_fee": "Info. comisión",
|
||||
"confirm_total__title_sending_from": "Envío desde",
|
||||
@ -297,7 +298,7 @@
|
||||
"eos__requirement": "Requisito:",
|
||||
"eos__sell_ram": "Vender RAM",
|
||||
"eos__sender": "Remitente:",
|
||||
"eos__sign_transaction": "Firmar transacción",
|
||||
"send__sign_transaction": "Firmar transacción",
|
||||
"eos__threshold": "Umbral:",
|
||||
"eos__to": "Para:",
|
||||
"eos__transfer": "Transferencia:",
|
||||
@ -699,7 +700,8 @@
|
||||
"send__title_sending_amount": "Importe envío",
|
||||
"send__title_sending_to": "Envío a",
|
||||
"send__to_the_total_amount": "Al importe total:",
|
||||
"send__total_amount": "Importe total:",
|
||||
"send__total_amount": "Importe total",
|
||||
"send__total_amount_colon": "Importe total:",
|
||||
"send__transaction_id": "ID de la transacción:",
|
||||
"send__you_are_contributing": "Estás aportando:",
|
||||
"share_words__words_in_order": " palabras en orden.",
|
||||
|
@ -257,7 +257,8 @@
|
||||
"coinjoin__title_do_not_disconnect": "Ne déconnectez pas votre trezor !",
|
||||
"coinjoin__title_progress": "Coinjoin en cours",
|
||||
"coinjoin__waiting_for_others": "En attente des autres",
|
||||
"confirm_total__fee_rate": "Taux des frais :",
|
||||
"confirm_total__fee_rate": "Taux des frais",
|
||||
"confirm_total__fee_rate_colon": "Taux des frais :",
|
||||
"confirm_total__sending_from_account": "Compte d'envoi :",
|
||||
"confirm_total__title_fee": "Infos sur les frais",
|
||||
"confirm_total__title_sending_from": "Envoi depuis",
|
||||
@ -297,7 +298,7 @@
|
||||
"eos__requirement": "Exigence :",
|
||||
"eos__sell_ram": "Vendre de la RAM",
|
||||
"eos__sender": "Expéditeur :",
|
||||
"eos__sign_transaction": "Signer la transaction",
|
||||
"send__sign_transaction": "Signer la transaction",
|
||||
"eos__threshold": "Seuil :",
|
||||
"eos__to": "À :",
|
||||
"eos__transfer": "Transfert :",
|
||||
@ -699,7 +700,8 @@
|
||||
"send__title_sending_amount": "Montant de l'envoi",
|
||||
"send__title_sending_to": "Envoi à",
|
||||
"send__to_the_total_amount": "Au montant total :",
|
||||
"send__total_amount": "Montant total :",
|
||||
"send__total_amount": "Montant total",
|
||||
"send__total_amount_colon": "Montant total :",
|
||||
"send__transaction_id": "ID de transaction :",
|
||||
"send__you_are_contributing": "Votre contribution :",
|
||||
"share_words__words_in_order": " mots dans l'ordre.",
|
||||
|
@ -219,7 +219,7 @@
|
||||
"217": "coinjoin__title_do_not_disconnect",
|
||||
"218": "coinjoin__title_progress",
|
||||
"219": "coinjoin__waiting_for_others",
|
||||
"220": "confirm_total__fee_rate",
|
||||
"220": "confirm_total__fee_rate_colon",
|
||||
"221": "confirm_total__sending_from_account",
|
||||
"222": "confirm_total__title_fee",
|
||||
"223": "confirm_total__title_sending_from",
|
||||
@ -259,7 +259,7 @@
|
||||
"257": "eos__requirement",
|
||||
"258": "eos__sell_ram",
|
||||
"259": "eos__sender",
|
||||
"260": "eos__sign_transaction",
|
||||
"260": "send__sign_transaction",
|
||||
"261": "eos__threshold",
|
||||
"262": "eos__to",
|
||||
"263": "eos__transfer",
|
||||
@ -652,7 +652,7 @@
|
||||
"650": "send__title_sending_amount",
|
||||
"651": "send__title_sending_to",
|
||||
"652": "send__to_the_total_amount",
|
||||
"653": "send__total_amount",
|
||||
"653": "send__total_amount_colon",
|
||||
"654": "send__transaction_id",
|
||||
"655": "send__you_are_contributing",
|
||||
"656": "share_words__words_in_order",
|
||||
@ -871,5 +871,11 @@
|
||||
"869": "address__confirmed",
|
||||
"870": "pin__cancel_description",
|
||||
"871": "pin__cancel_info",
|
||||
"872": "pin__cancel_setup"
|
||||
"872": "pin__cancel_setup",
|
||||
"873": "send__cancel_sign",
|
||||
"874": "send__send_from",
|
||||
"875": "instructions__hold_to_sign",
|
||||
"876": "confirm_total__fee_rate",
|
||||
"877": "send__incl_transaction_fee",
|
||||
"878": "send__total_amount"
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
{
|
||||
"current": {
|
||||
"merkle_root": "928438526b993d433d52359d86099848d570c13fbe6aac72a2f5a29a4e8e94c5",
|
||||
"datetime": "2024-05-17T10:55:04.124405",
|
||||
"commit": "1409ed27df07827a2a3e3756420db1b41fe108e5"
|
||||
"merkle_root": "329b06dbf2564bf17ba46d2b3304f91df15abc42794763a55ea33bc04281ac42",
|
||||
"datetime": "2024-05-20T15:37:23.831427",
|
||||
"commit": "2a5dc6f8d54701e86c32451fb154d3e40a778ca9"
|
||||
},
|
||||
"history": [
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user