from typing import TYPE_CHECKING from trezor import TR, ui from trezor.enums import ButtonRequestType from trezor.ui.layouts import ( confirm_blob, confirm_ethereum_staking_tx, confirm_text, should_show_more, ) from .helpers import address_from_bytes, decode_typed_data, format_ethereum_amount if TYPE_CHECKING: from typing import Awaitable, Iterable from trezor.messages import ( EthereumFieldType, EthereumNetworkInfo, EthereumStructMember, EthereumTokenInfo, ) async def require_confirm_tx( to_bytes: bytes, value: int, maximum_fee: str, fee_info_items: Iterable[tuple[str, str]], network: EthereumNetworkInfo, token: EthereumTokenInfo | None, chunkify: bool, ) -> None: from trezor.ui.layouts import confirm_ethereum_tx if to_bytes: to_str = address_from_bytes(to_bytes, network) else: to_str = TR.ethereum__new_contract chunkify = False total_amount = format_ethereum_amount(value, token, network) await confirm_ethereum_tx( to_str, total_amount, maximum_fee, fee_info_items, chunkify=chunkify ) async def require_confirm_stake( addr_bytes: bytes, value: int, maximum_fee: str, fee_info_items: Iterable[tuple[str, str]], network: EthereumNetworkInfo, chunkify: bool, ) -> None: addr_str = address_from_bytes(addr_bytes, network) total_amount = format_ethereum_amount(value, None, network) await confirm_ethereum_staking_tx( TR.ethereum__staking_title_stake, # title TR.ethereum__staking_stake_intro, # intro_question TR.ethereum__staking_title_stake, # verb total_amount, # total_amount maximum_fee, # maximum_fee addr_str, # address TR.ethereum__staking_title_stake_address, # address_title fee_info_items, # info_items chunkify=chunkify, ) async def require_confirm_unstake( addr_bytes: bytes, value: int, maximum_fee: str, fee_info_items: Iterable[tuple[str, str]], network: EthereumNetworkInfo, chunkify: bool, ) -> None: addr_str = address_from_bytes(addr_bytes, network) total_amount = format_ethereum_amount(value, None, network) await confirm_ethereum_staking_tx( TR.ethereum__staking_title_unstake, # title TR.ethereum__staking_unstake_intro, # intro_question TR.ethereum__staking_title_unstake, # verb total_amount, # total_amount maximum_fee, # maximum_fee addr_str, # address TR.ethereum__staking_title_stake_address, # address_title fee_info_items, # info_items chunkify=chunkify, ) async def require_confirm_claim( addr_bytes: bytes, maximum_fee: str, fee_info_items: Iterable[tuple[str, str]], network: EthereumNetworkInfo, chunkify: bool, ) -> None: addr_str = address_from_bytes(addr_bytes, network) await confirm_ethereum_staking_tx( TR.ethereum__staking_title_claim, # title TR.ethereum__staking_claim_intro, # intro_question TR.ethereum__staking_title_claim, # verb "", # total_amount maximum_fee, # maximum_fee addr_str, # address TR.ethereum__staking_title_claim_address, # address_title fee_info_items, # info_items chunkify=chunkify, ) def require_confirm_unknown_token(address_bytes: bytes) -> Awaitable[None]: from ubinascii import hexlify from trezor.ui.layouts import confirm_address contract_address_hex = "0x" + hexlify(address_bytes).decode() return confirm_address( TR.ethereum__unknown_token, contract_address_hex, TR.ethereum__contract, "unknown_token", br_code=ButtonRequestType.SignTx, ) def require_confirm_address(address_bytes: bytes) -> Awaitable[None]: from ubinascii import hexlify from trezor.ui.layouts import confirm_address address_hex = "0x" + hexlify(address_bytes).decode() return confirm_address( TR.ethereum__title_signing_address, address_hex, br_code=ButtonRequestType.SignTx, ) def require_confirm_other_data(data: bytes, data_total: int) -> Awaitable[None]: return confirm_blob( "confirm_data", TR.ethereum__title_confirm_data, data, TR.ethereum__data_size_template.format(data_total), br_code=ButtonRequestType.SignTx, ask_pagination=True, ) async def confirm_typed_data_final() -> None: from trezor.ui.layouts import confirm_action await confirm_action( "confirm_typed_data_final", TR.ethereum__title_confirm_typed_data, TR.ethereum__sign_eip712, verb=TR.buttons__hold_to_confirm, hold=True, ) def confirm_empty_typed_message() -> Awaitable[None]: return confirm_text( "confirm_empty_typed_message", TR.ethereum__title_confirm_message, "", TR.ethereum__no_message_field, ) async def should_show_domain(name: bytes, version: bytes) -> bool: domain_name = decode_typed_data(name, "string") domain_version = decode_typed_data(version, "string") para = ( (ui.NORMAL, TR.ethereum__name_and_version), (ui.DEMIBOLD, domain_name), (ui.DEMIBOLD, domain_version), ) return await should_show_more( TR.ethereum__title_confirm_domain, para, TR.ethereum__show_full_domain, "should_show_domain", ) async def should_show_struct( description: str, data_members: list[EthereumStructMember], title: str | None = None, button_text: str | None = None, ) -> bool: from trezor.strings import format_plural title = title or TR.ethereum__title_confirm_struct # def_arg button_text = button_text or TR.ethereum__show_full_struct # def_arg plural = format_plural( "{count} {plural}", len(data_members), TR.plurals__contains_x_keys ) contains_plural = f"{TR.words__contains} {plural}" para = ( (ui.DEMIBOLD, description), ( ui.NORMAL, contains_plural, ), (ui.NORMAL, ", ".join(field.name for field in data_members)), ) return await should_show_more( title, para, button_text, "should_show_struct", ) async def should_show_array( parent_objects: Iterable[str], data_type: str, size: int, ) -> bool: from trezor.strings import format_plural_english # Leaving english plural form because of dynamic noun - data_type plural = format_plural_english("{count} {plural}", size, data_type) array_of_plural = f"{TR.words__array_of} {plural}" para = ((ui.NORMAL, array_of_plural),) return await should_show_more( limit_str(".".join(parent_objects)), para, TR.ethereum__show_full_array, "should_show_array", ) async def confirm_typed_value( name: str, value: bytes, parent_objects: list[str], field: EthereumFieldType, array_index: int | None = None, ) -> None: from trezor.enums import EthereumDataType from .helpers import get_type_name type_name = get_type_name(field) if array_index is not None: title = limit_str(".".join(parent_objects + [name])) description = f"[{array_index}] ({type_name})" else: title = limit_str(".".join(parent_objects)) description = f"{name} ({type_name})" data = decode_typed_data(value, type_name) if field.data_type in (EthereumDataType.ADDRESS, EthereumDataType.BYTES): await confirm_blob( "confirm_typed_value", title, data, description, ask_pagination=True, ) else: await confirm_text( "confirm_typed_value", title, data, description, ) def limit_str(s: str, limit: int = 16) -> str: """Shortens string to show the last characters.""" if len(s) <= limit + 2: return s return ".." + s[-limit:]