mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-11-22 23:48:12 +00:00
feat(core): create new Ethereum send flow for TR
[no changelog]
This commit is contained in:
parent
61eb47636a
commit
eed6e0b71c
@ -41,6 +41,7 @@ static void _librust_qstrs(void) {
|
|||||||
MP_QSTR_confirm_blob;
|
MP_QSTR_confirm_blob;
|
||||||
MP_QSTR_confirm_coinjoin;
|
MP_QSTR_confirm_coinjoin;
|
||||||
MP_QSTR_confirm_emphasized;
|
MP_QSTR_confirm_emphasized;
|
||||||
|
MP_QSTR_confirm_ethereum_tx;
|
||||||
MP_QSTR_confirm_fido;
|
MP_QSTR_confirm_fido;
|
||||||
MP_QSTR_confirm_homescreen;
|
MP_QSTR_confirm_homescreen;
|
||||||
MP_QSTR_confirm_joint_total;
|
MP_QSTR_confirm_joint_total;
|
||||||
@ -85,6 +86,7 @@ static void _librust_qstrs(void) {
|
|||||||
MP_QSTR_max_feerate;
|
MP_QSTR_max_feerate;
|
||||||
MP_QSTR_max_len;
|
MP_QSTR_max_len;
|
||||||
MP_QSTR_max_rounds;
|
MP_QSTR_max_rounds;
|
||||||
|
MP_QSTR_maximum_fee;
|
||||||
MP_QSTR_min_count;
|
MP_QSTR_min_count;
|
||||||
MP_QSTR_multiple_pages_texts;
|
MP_QSTR_multiple_pages_texts;
|
||||||
MP_QSTR_notification;
|
MP_QSTR_notification;
|
||||||
@ -95,6 +97,7 @@ static void _librust_qstrs(void) {
|
|||||||
MP_QSTR_path;
|
MP_QSTR_path;
|
||||||
MP_QSTR_progress_event;
|
MP_QSTR_progress_event;
|
||||||
MP_QSTR_prompt;
|
MP_QSTR_prompt;
|
||||||
|
MP_QSTR_recipient;
|
||||||
MP_QSTR_request_bip39;
|
MP_QSTR_request_bip39;
|
||||||
MP_QSTR_request_complete_repaint;
|
MP_QSTR_request_complete_repaint;
|
||||||
MP_QSTR_request_number;
|
MP_QSTR_request_number;
|
||||||
|
@ -16,7 +16,7 @@ use heapless::Vec;
|
|||||||
|
|
||||||
// So that there is only one implementation, and not multiple generic ones
|
// So that there is only one implementation, and not multiple generic ones
|
||||||
// as would be via `const N: usize` generics.
|
// as would be via `const N: usize` generics.
|
||||||
const MAX_OPS: usize = 15;
|
const MAX_OPS: usize = 20;
|
||||||
|
|
||||||
/// To account for operations that are not made of characters
|
/// To account for operations that are not made of characters
|
||||||
/// but need to be accounted for somehow.
|
/// but need to be accounted for somehow.
|
||||||
@ -39,6 +39,10 @@ impl<'a, T: StringType + Clone + 'a> OpTextLayout<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.ops.len() == 0
|
||||||
|
}
|
||||||
|
|
||||||
pub fn place(&mut self, bounds: Rect) -> Rect {
|
pub fn place(&mut self, bounds: Rect) -> Rect {
|
||||||
self.layout.bounds = bounds;
|
self.layout.bounds = bounds;
|
||||||
bounds
|
bounds
|
||||||
|
@ -383,9 +383,9 @@ where
|
|||||||
pub fn from_text_possible_icon(text: T) -> Self {
|
pub fn from_text_possible_icon(text: T) -> Self {
|
||||||
if text.as_ref() == "" {
|
if text.as_ref() == "" {
|
||||||
Self::cancel_icon()
|
Self::cancel_icon()
|
||||||
} else if text.as_ref() == "left_arrow_icon" {
|
} else if text.as_ref() == "<" {
|
||||||
Self::left_arrow_icon()
|
Self::left_arrow_icon()
|
||||||
} else if text.as_ref() == "up_arrow_icon" {
|
} else if text.as_ref() == "^" {
|
||||||
Self::up_arrow_icon()
|
Self::up_arrow_icon()
|
||||||
} else {
|
} else {
|
||||||
Self::text(text)
|
Self::text(text)
|
||||||
@ -662,7 +662,7 @@ where
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Cancel cross on left and right arrow facing down.
|
/// Up arrow on left and right arrow facing down.
|
||||||
pub fn up_arrow_none_arrow_wide() -> Self {
|
pub fn up_arrow_none_arrow_wide() -> Self {
|
||||||
Self::new(
|
Self::new(
|
||||||
Some(ButtonDetails::up_arrow_icon()),
|
Some(ButtonDetails::up_arrow_icon()),
|
||||||
@ -671,6 +671,15 @@ where
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Up arrow on left, middle text and info on the right.
|
||||||
|
pub fn up_arrow_armed_info(text: T) -> Self {
|
||||||
|
Self::new(
|
||||||
|
Some(ButtonDetails::up_arrow_icon()),
|
||||||
|
Some(ButtonDetails::armed_text(text)),
|
||||||
|
Some(ButtonDetails::text("i".into()).with_fixed_width(theme::BUTTON_ICON_WIDTH)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/// Cancel cross on left and right arrow facing down.
|
/// Cancel cross on left and right arrow facing down.
|
||||||
pub fn cancel_none_arrow_down() -> Self {
|
pub fn cancel_none_arrow_down() -> Self {
|
||||||
Self::new(
|
Self::new(
|
||||||
|
@ -84,6 +84,7 @@ where
|
|||||||
current_page: usize,
|
current_page: usize,
|
||||||
page_count: usize,
|
page_count: usize,
|
||||||
title: Option<T>,
|
title: Option<T>,
|
||||||
|
slim_arrows: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
// For `layout.rs`
|
// For `layout.rs`
|
||||||
@ -103,6 +104,7 @@ where
|
|||||||
current_page: 0,
|
current_page: 0,
|
||||||
page_count: 1,
|
page_count: 1,
|
||||||
title: None,
|
title: None,
|
||||||
|
slim_arrows: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -118,6 +120,12 @@ where
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Using slim arrows instead of wide buttons.
|
||||||
|
pub fn with_slim_arrows(mut self) -> Self {
|
||||||
|
self.slim_arrows = true;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
pub fn paint(&mut self) {
|
pub fn paint(&mut self) {
|
||||||
self.change_page(self.current_page);
|
self.change_page(self.current_page);
|
||||||
self.formatted.paint();
|
self.formatted.paint();
|
||||||
@ -137,17 +145,29 @@ where
|
|||||||
// On the last page showing only the narrow arrow, so the right
|
// On the last page showing only the narrow arrow, so the right
|
||||||
// button with possibly long text has enough space.
|
// button with possibly long text has enough space.
|
||||||
let btn_left = if self.has_prev_page() && !self.has_next_page() {
|
let btn_left = if self.has_prev_page() && !self.has_next_page() {
|
||||||
|
if self.slim_arrows {
|
||||||
|
Some(ButtonDetails::left_arrow_icon())
|
||||||
|
} else {
|
||||||
Some(ButtonDetails::up_arrow_icon())
|
Some(ButtonDetails::up_arrow_icon())
|
||||||
|
}
|
||||||
} else if self.has_prev_page() {
|
} else if self.has_prev_page() {
|
||||||
|
if self.slim_arrows {
|
||||||
|
Some(ButtonDetails::left_arrow_icon())
|
||||||
|
} else {
|
||||||
Some(ButtonDetails::up_arrow_icon_wide())
|
Some(ButtonDetails::up_arrow_icon_wide())
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
current.btn_left
|
current.btn_left
|
||||||
};
|
};
|
||||||
|
|
||||||
// Middle button should be shown only on the last page, not to collide
|
// Middle button should be shown only on the last page, not to collide
|
||||||
// with the fat right button.
|
// with the possible fat right button.
|
||||||
let (btn_middle, btn_right) = if self.has_next_page() {
|
let (btn_middle, btn_right) = if self.has_next_page() {
|
||||||
|
if self.slim_arrows {
|
||||||
|
(None, Some(ButtonDetails::right_arrow_icon()))
|
||||||
|
} else {
|
||||||
(None, Some(ButtonDetails::down_arrow_icon_wide()))
|
(None, Some(ButtonDetails::down_arrow_icon_wide()))
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
(current.btn_middle, current.btn_right)
|
(current.btn_middle, current.btn_right)
|
||||||
};
|
};
|
||||||
|
@ -679,6 +679,76 @@ 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) }
|
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern "C" fn new_confirm_ethereum_tx(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
||||||
|
let block = |_args: &[Obj], kwargs: &Map| {
|
||||||
|
let recipient: StrBuffer = kwargs.get(Qstr::MP_QSTR_recipient)?.try_into()?;
|
||||||
|
let total_amount: StrBuffer = kwargs.get(Qstr::MP_QSTR_total_amount)?.try_into()?;
|
||||||
|
let maximum_fee: StrBuffer = kwargs.get(Qstr::MP_QSTR_maximum_fee)?.try_into()?;
|
||||||
|
let items: Obj = kwargs.get(Qstr::MP_QSTR_items)?;
|
||||||
|
|
||||||
|
let get_page = move |page_index| {
|
||||||
|
match page_index {
|
||||||
|
0 => {
|
||||||
|
// RECIPIENT
|
||||||
|
let btn_layout = ButtonLayout::cancel_none_text("CONTINUE".into());
|
||||||
|
let btn_actions = ButtonActions::cancel_none_next();
|
||||||
|
|
||||||
|
let ops = OpTextLayout::new(theme::TEXT_MONO_DATA).text_mono(recipient.clone());
|
||||||
|
|
||||||
|
let formatted = FormattedText::new(ops).vertically_centered();
|
||||||
|
Page::new(btn_layout, btn_actions, formatted).with_title("RECIPIENT".into())
|
||||||
|
}
|
||||||
|
1 => {
|
||||||
|
// Total amount + fee
|
||||||
|
let btn_layout = ButtonLayout::up_arrow_armed_info("CONFIRM".into());
|
||||||
|
let btn_actions = ButtonActions::prev_confirm_next();
|
||||||
|
|
||||||
|
let ops = OpTextLayout::new(theme::TEXT_MONO)
|
||||||
|
.text_mono(total_amount.clone())
|
||||||
|
.newline()
|
||||||
|
.newline_half()
|
||||||
|
.text_bold("Maximum fee:".into())
|
||||||
|
.newline()
|
||||||
|
.text_mono(maximum_fee.clone());
|
||||||
|
|
||||||
|
let formatted = FormattedText::new(ops);
|
||||||
|
Page::new(btn_layout, btn_actions, formatted).with_title("Amount:".into())
|
||||||
|
}
|
||||||
|
2 => {
|
||||||
|
// Fee information
|
||||||
|
let btn_layout = ButtonLayout::arrow_none_none();
|
||||||
|
let btn_actions = ButtonActions::prev_none_none();
|
||||||
|
|
||||||
|
let mut ops = OpTextLayout::new(theme::TEXT_MONO);
|
||||||
|
|
||||||
|
for item in unwrap!(IterBuf::new().try_iterate(items)) {
|
||||||
|
let [key, value]: [Obj; 2] = unwrap!(iter_into_array(item));
|
||||||
|
if !ops.is_empty() {
|
||||||
|
// Each key-value pair is on its own page
|
||||||
|
ops = ops.next_page();
|
||||||
|
}
|
||||||
|
ops = ops
|
||||||
|
.text_bold(unwrap!(key.try_into()))
|
||||||
|
.newline()
|
||||||
|
.text_mono(unwrap!(value.try_into()));
|
||||||
|
}
|
||||||
|
|
||||||
|
let formatted = FormattedText::new(ops).vertically_centered();
|
||||||
|
Page::new(btn_layout, btn_actions, formatted)
|
||||||
|
.with_title("FEE INFORMATION".into())
|
||||||
|
.with_slim_arrows()
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let pages = FlowPages::new(get_page, 3);
|
||||||
|
|
||||||
|
let obj = LayoutObj::new(Flow::new(pages).with_scrollbar(false))?;
|
||||||
|
Ok(obj.into())
|
||||||
|
};
|
||||||
|
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||||
|
}
|
||||||
|
|
||||||
extern "C" fn new_confirm_address(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
extern "C" fn new_confirm_address(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
||||||
let block = move |_args: &[Obj], kwargs: &Map| {
|
let block = move |_args: &[Obj], kwargs: &Map| {
|
||||||
let title: StrBuffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
|
let title: StrBuffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
|
||||||
@ -1099,7 +1169,7 @@ extern "C" fn new_confirm_more(n_args: usize, args: *const Obj, kwargs: *mut Map
|
|||||||
title,
|
title,
|
||||||
paragraphs.into_paragraphs(),
|
paragraphs.into_paragraphs(),
|
||||||
button,
|
button,
|
||||||
Some("left_arrow_icon".into()),
|
Some("<".into()),
|
||||||
false,
|
false,
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
@ -1599,6 +1669,16 @@ pub static mp_module_trezorui2: Module = obj_module! {
|
|||||||
/// """Confirm summary of a transaction."""
|
/// """Confirm summary of a transaction."""
|
||||||
Qstr::MP_QSTR_confirm_total => obj_fn_kw!(0, new_confirm_total).as_obj(),
|
Qstr::MP_QSTR_confirm_total => obj_fn_kw!(0, new_confirm_total).as_obj(),
|
||||||
|
|
||||||
|
/// def confirm_ethereum_tx(
|
||||||
|
/// *,
|
||||||
|
/// recipient: str,
|
||||||
|
/// total_amount: str,
|
||||||
|
/// maximum_fee: str,
|
||||||
|
/// items: Iterable[Tuple[str, str]],
|
||||||
|
/// ) -> object:
|
||||||
|
/// """Confirm details about Ethereum transaction."""
|
||||||
|
Qstr::MP_QSTR_confirm_ethereum_tx => obj_fn_kw!(0, new_confirm_ethereum_tx).as_obj(),
|
||||||
|
|
||||||
/// def tutorial() -> object:
|
/// def tutorial() -> object:
|
||||||
/// """Show user how to interact with the device."""
|
/// """Show user how to interact with the device."""
|
||||||
Qstr::MP_QSTR_tutorial => obj_fn_kw!(0, tutorial).as_obj(),
|
Qstr::MP_QSTR_tutorial => obj_fn_kw!(0, tutorial).as_obj(),
|
||||||
|
@ -149,6 +149,17 @@ def confirm_total(
|
|||||||
"""Confirm summary of a transaction."""
|
"""Confirm summary of a transaction."""
|
||||||
|
|
||||||
|
|
||||||
|
# rust/src/ui/model_tr/layout.rs
|
||||||
|
def confirm_ethereum_tx(
|
||||||
|
*,
|
||||||
|
recipient: str,
|
||||||
|
total_amount: str,
|
||||||
|
maximum_fee: str,
|
||||||
|
items: Iterable[Tuple[str, str]],
|
||||||
|
) -> object:
|
||||||
|
"""Confirm details about Ethereum transaction."""
|
||||||
|
|
||||||
|
|
||||||
# rust/src/ui/model_tr/layout.rs
|
# rust/src/ui/model_tr/layout.rs
|
||||||
def tutorial() -> object:
|
def tutorial() -> object:
|
||||||
"""Show user how to interact with the device."""
|
"""Show user how to interact with the device."""
|
||||||
|
@ -4,14 +4,13 @@ from trezor import ui
|
|||||||
from trezor.enums import ButtonRequestType
|
from trezor.enums import ButtonRequestType
|
||||||
from trezor.strings import format_plural
|
from trezor.strings import format_plural
|
||||||
from trezor.ui.layouts import (
|
from trezor.ui.layouts import (
|
||||||
confirm_amount,
|
|
||||||
confirm_blob,
|
confirm_blob,
|
||||||
|
confirm_ethereum_tx,
|
||||||
confirm_text,
|
confirm_text,
|
||||||
confirm_total,
|
|
||||||
should_show_more,
|
should_show_more,
|
||||||
)
|
)
|
||||||
|
|
||||||
from .helpers import decode_typed_data
|
from .helpers import address_from_bytes, decode_typed_data
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from typing import Awaitable, Iterable
|
from typing import Awaitable, Iterable
|
||||||
@ -24,72 +23,61 @@ if TYPE_CHECKING:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def require_confirm_tx(
|
async def require_confirm_tx(
|
||||||
to_bytes: bytes,
|
to_bytes: bytes,
|
||||||
value: int,
|
value: int,
|
||||||
network: EthereumNetworkInfo,
|
|
||||||
token: EthereumTokenInfo | None,
|
|
||||||
) -> Awaitable[None]:
|
|
||||||
from trezor.ui.layouts import confirm_output
|
|
||||||
|
|
||||||
from .helpers import address_from_bytes
|
|
||||||
|
|
||||||
if to_bytes:
|
|
||||||
to_str = address_from_bytes(to_bytes, network)
|
|
||||||
else:
|
|
||||||
to_str = "new contract?"
|
|
||||||
return confirm_output(
|
|
||||||
to_str,
|
|
||||||
format_ethereum_amount(value, token, network),
|
|
||||||
br_code=ButtonRequestType.SignTx,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
async def require_confirm_fee(
|
|
||||||
spending: int,
|
|
||||||
gas_price: int,
|
gas_price: int,
|
||||||
gas_limit: int,
|
gas_limit: int,
|
||||||
network: EthereumNetworkInfo,
|
network: EthereumNetworkInfo,
|
||||||
token: EthereumTokenInfo | None,
|
token: EthereumTokenInfo | None,
|
||||||
) -> None:
|
) -> None:
|
||||||
await confirm_amount(
|
if to_bytes:
|
||||||
title="Confirm fee",
|
to_str = address_from_bytes(to_bytes, network)
|
||||||
description="Gas price:",
|
else:
|
||||||
amount=format_ethereum_amount(gas_price, None, network),
|
to_str = "new contract?"
|
||||||
)
|
|
||||||
await confirm_total(
|
total_amount = format_ethereum_amount(value, token, network)
|
||||||
total_amount=format_ethereum_amount(spending, token, network),
|
maximum_fee = format_ethereum_amount(gas_price * gas_limit, None, network)
|
||||||
fee_amount=format_ethereum_amount(gas_price * gas_limit, None, network),
|
gas_limit_str = f"{gas_limit} units"
|
||||||
total_label="Amount sent:",
|
gas_price_str = format_ethereum_amount(gas_price, None, network)
|
||||||
fee_label="Maximum fee:",
|
|
||||||
|
items = (
|
||||||
|
("Gas limit:", gas_limit_str),
|
||||||
|
("Gas price:", gas_price_str),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
await confirm_ethereum_tx(to_str, total_amount, maximum_fee, items)
|
||||||
|
|
||||||
async def require_confirm_eip1559_fee(
|
|
||||||
spending: int,
|
async def require_confirm_tx_eip1559(
|
||||||
max_priority_fee: int,
|
to_bytes: bytes,
|
||||||
|
value: int,
|
||||||
max_gas_fee: int,
|
max_gas_fee: int,
|
||||||
|
max_priority_fee: int,
|
||||||
gas_limit: int,
|
gas_limit: int,
|
||||||
network: EthereumNetworkInfo,
|
network: EthereumNetworkInfo,
|
||||||
token: EthereumTokenInfo | None,
|
token: EthereumTokenInfo | None,
|
||||||
) -> None:
|
) -> None:
|
||||||
await confirm_amount(
|
|
||||||
"Confirm fee",
|
if to_bytes:
|
||||||
format_ethereum_amount(max_gas_fee, None, network),
|
to_str = address_from_bytes(to_bytes, network)
|
||||||
"Maximum fee per gas",
|
else:
|
||||||
)
|
to_str = "new contract?"
|
||||||
await confirm_amount(
|
|
||||||
"Confirm fee",
|
total_amount = format_ethereum_amount(value, token, network)
|
||||||
format_ethereum_amount(max_priority_fee, None, network),
|
maximum_fee = format_ethereum_amount(max_gas_fee * gas_limit, None, network)
|
||||||
"Priority fee per gas",
|
gas_limit_str = f"{gas_limit} units"
|
||||||
)
|
max_gas_fee_str = format_ethereum_amount(max_gas_fee, None, network)
|
||||||
await confirm_total(
|
max_priority_fee_str = format_ethereum_amount(max_priority_fee, None, network)
|
||||||
format_ethereum_amount(spending, token, network),
|
|
||||||
format_ethereum_amount(max_gas_fee * gas_limit, None, network),
|
items = (
|
||||||
total_label="Amount sent:",
|
("Gas limit:", gas_limit_str),
|
||||||
fee_label="Maximum fee:",
|
("Max gas price:", max_gas_fee_str),
|
||||||
|
("Priority fee:", max_priority_fee_str),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
await confirm_ethereum_tx(to_str, total_amount, maximum_fee, items)
|
||||||
|
|
||||||
|
|
||||||
def require_confirm_unknown_token(address_bytes: bytes) -> Awaitable[None]:
|
def require_confirm_unknown_token(address_bytes: bytes) -> Awaitable[None]:
|
||||||
from ubinascii import hexlify
|
from ubinascii import hexlify
|
||||||
|
@ -33,7 +33,7 @@ async def sign_tx(
|
|||||||
|
|
||||||
from apps.common import paths
|
from apps.common import paths
|
||||||
|
|
||||||
from .layout import require_confirm_data, require_confirm_fee, require_confirm_tx
|
from .layout import require_confirm_data, require_confirm_tx
|
||||||
|
|
||||||
# check
|
# check
|
||||||
if msg.tx_type not in [1, 6, None]:
|
if msg.tx_type not in [1, 6, None]:
|
||||||
@ -47,13 +47,13 @@ async def sign_tx(
|
|||||||
# Handle ERC20s
|
# Handle ERC20s
|
||||||
token, address_bytes, recipient, value = await handle_erc20(msg, defs)
|
token, address_bytes, recipient, value = await handle_erc20(msg, defs)
|
||||||
|
|
||||||
data_total = msg.data_length
|
data_total = msg.data_length # local_cache_attribute
|
||||||
|
|
||||||
await require_confirm_tx(recipient, value, defs.network, token)
|
if token is None and data_total > 0:
|
||||||
if token is None and msg.data_length > 0:
|
|
||||||
await require_confirm_data(msg.data_initial_chunk, data_total)
|
await require_confirm_data(msg.data_initial_chunk, data_total)
|
||||||
|
|
||||||
await require_confirm_fee(
|
await require_confirm_tx(
|
||||||
|
recipient,
|
||||||
value,
|
value,
|
||||||
int.from_bytes(msg.gas_price, "big"),
|
int.from_bytes(msg.gas_price, "big"),
|
||||||
int.from_bytes(msg.gas_limit, "big"),
|
int.from_bytes(msg.gas_limit, "big"),
|
||||||
|
@ -42,14 +42,11 @@ async def sign_tx_eip1559(
|
|||||||
|
|
||||||
from apps.common import paths
|
from apps.common import paths
|
||||||
|
|
||||||
from .layout import (
|
from .layout import require_confirm_data, require_confirm_tx_eip1559
|
||||||
require_confirm_data,
|
|
||||||
require_confirm_eip1559_fee,
|
|
||||||
require_confirm_tx,
|
|
||||||
)
|
|
||||||
from .sign_tx import check_common_fields, handle_erc20, send_request_chunk
|
from .sign_tx import check_common_fields, handle_erc20, send_request_chunk
|
||||||
|
|
||||||
gas_limit = msg.gas_limit # local_cache_attribute
|
gas_limit = msg.gas_limit # local_cache_attribute
|
||||||
|
data_total = msg.data_length # local_cache_attribute
|
||||||
|
|
||||||
# check
|
# check
|
||||||
if len(msg.max_gas_fee) + len(gas_limit) > 30:
|
if len(msg.max_gas_fee) + len(gas_limit) > 30:
|
||||||
@ -63,16 +60,14 @@ async def sign_tx_eip1559(
|
|||||||
# Handle ERC20s
|
# Handle ERC20s
|
||||||
token, address_bytes, recipient, value = await handle_erc20(msg, defs)
|
token, address_bytes, recipient, value = await handle_erc20(msg, defs)
|
||||||
|
|
||||||
data_total = msg.data_length
|
if token is None and data_total > 0:
|
||||||
|
|
||||||
await require_confirm_tx(recipient, value, defs.network, token)
|
|
||||||
if token is None and msg.data_length > 0:
|
|
||||||
await require_confirm_data(msg.data_initial_chunk, data_total)
|
await require_confirm_data(msg.data_initial_chunk, data_total)
|
||||||
|
|
||||||
await require_confirm_eip1559_fee(
|
await require_confirm_tx_eip1559(
|
||||||
|
recipient,
|
||||||
value,
|
value,
|
||||||
int.from_bytes(msg.max_priority_fee, "big"),
|
|
||||||
int.from_bytes(msg.max_gas_fee, "big"),
|
int.from_bytes(msg.max_gas_fee, "big"),
|
||||||
|
int.from_bytes(msg.max_priority_fee, "big"),
|
||||||
int.from_bytes(gas_limit, "big"),
|
int.from_bytes(gas_limit, "big"),
|
||||||
defs.network,
|
defs.network,
|
||||||
token,
|
token,
|
||||||
|
@ -972,6 +972,30 @@ async def confirm_total(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def confirm_ethereum_tx(
|
||||||
|
recipient: str,
|
||||||
|
total_amount: str,
|
||||||
|
maximum_fee: str,
|
||||||
|
items: Iterable[tuple[str, str]],
|
||||||
|
br_type: str = "confirm_ethereum_tx",
|
||||||
|
br_code: ButtonRequestType = ButtonRequestType.SignTx,
|
||||||
|
) -> None:
|
||||||
|
await raise_if_not_confirmed(
|
||||||
|
interact(
|
||||||
|
RustLayout(
|
||||||
|
trezorui2.confirm_ethereum_tx(
|
||||||
|
recipient=recipient,
|
||||||
|
total_amount=total_amount,
|
||||||
|
maximum_fee=maximum_fee,
|
||||||
|
items=items,
|
||||||
|
)
|
||||||
|
),
|
||||||
|
br_type,
|
||||||
|
br_code,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
async def confirm_joint_total(spending_amount: str, total_amount: str) -> None:
|
async def confirm_joint_total(spending_amount: str, total_amount: str) -> None:
|
||||||
|
|
||||||
await raise_if_not_confirmed(
|
await raise_if_not_confirmed(
|
||||||
@ -1114,7 +1138,7 @@ async def confirm_signverify(
|
|||||||
br_type,
|
br_type,
|
||||||
"CONFIRM MESSAGE",
|
"CONFIRM MESSAGE",
|
||||||
message,
|
message,
|
||||||
verb_cancel="up_arrow_icon",
|
verb_cancel="^",
|
||||||
br_code=BR_TYPE_OTHER,
|
br_code=BR_TYPE_OTHER,
|
||||||
ask_pagination=True,
|
ask_pagination=True,
|
||||||
)
|
)
|
||||||
|
@ -10,6 +10,7 @@ EXCEPTIONS+=( "mnemonic" ) # has NEM in it
|
|||||||
EXCEPTIONS+=( "workflow" "overflow" ) # has Flo in it
|
EXCEPTIONS+=( "workflow" "overflow" ) # has Flo in it
|
||||||
EXCEPTIONS+=( "SyntaxError" ) # has Axe in it
|
EXCEPTIONS+=( "SyntaxError" ) # has Axe in it
|
||||||
EXCEPTIONS+=( "DKDNEM" ) # has NEM in it, some sort of weird coincidence
|
EXCEPTIONS+=( "DKDNEM" ) # has NEM in it, some sort of weird coincidence
|
||||||
|
EXCEPTIONS+=( "confirm_ethereum_tx" ) # is model-specific, so is in layout/__init__.py instead of ethereum/layout.py
|
||||||
|
|
||||||
GREP_ARGS=()
|
GREP_ARGS=()
|
||||||
for exception in "${EXCEPTIONS[@]}"; do
|
for exception in "${EXCEPTIONS[@]}"; do
|
||||||
|
Loading…
Reference in New Issue
Block a user