diff --git a/common/protob/messages-management.proto b/common/protob/messages-management.proto index b67f212c47..387095ef11 100644 --- a/common/protob/messages-management.proto +++ b/common/protob/messages-management.proto @@ -77,6 +77,10 @@ enum OneKeySEState { APP = 0x01; } +// enum PinKeyboardLayout { +// LAYOUT_NORMAL = 0; +// LAYOUT_RANDOMIZED = 1; +// } /** * Response: Reports various information about the device * @end @@ -144,6 +148,7 @@ message Features { optional uint32 auto_lock_delay_ms = 38; // number of milliseconds after which the device locks itself optional uint32 display_rotation = 39; // in degrees from North optional bool experimental_features = 40; // are experimental message types enabled? + optional bool busy = 41; // is the device busy, showing "Do not disconnect"? optional uint32 offset = 500; optional string ble_name = 501; // OneKey BLE name @@ -166,7 +171,12 @@ message Features { optional uint32 coin_switch = 517; optional bytes build_id = 518; optional string boardloader_version = 519; - optional bool busy = 41; // is the device busy, showing "Do not disconnect"? + reserved 520, 521, 530, 531; // used by battery_level(520) product(521) se_build_id(530) se_hash(531) in another device type + optional uint32 brightness_percent = 522; // current brightness of device + optional bool haptic_feedback = 523; // is the vibration & haptic enabled + optional uint32 auto_shutdown_delay_ms = 524; // number of milliseconds after which the device will poweroff + // optional PinKeyboardLayout pin_keyboard_layout = 524; // the layout of the pin input keyboard + // optional bool usb_lock = 525; // the device will auto lock when usb plug/ununplug with usb lock enabled optional OneKeyDeviceType onekey_device_type = 600; optional OneKeySeType onekey_se_type = 601; @@ -292,6 +302,13 @@ message ApplySettings { optional bool passphrase_always_on_device = 8; // do not prompt for passphrase, enforce device entry optional SafetyCheckLevel safety_checks = 9; // Safety check level, set to Prompt to limit path namespace enforcement optional bool experimental_features = 10; // enable experimental message types + + reserved 11 to 499; + optional uint32 auto_shutdown_delay_ms = 500; + optional bool change_brightness = 501; + optional bool haptic_feedback = 502; + // optional PinKeyboardLayout keybaord_layout = 503; + // optional bool usb_lock = 504; } /** diff --git a/common/protob/messages-solana.proto b/common/protob/messages-solana.proto index 5068fdaeb9..6c697fadd5 100644 --- a/common/protob/messages-solana.proto +++ b/common/protob/messages-solana.proto @@ -38,6 +38,21 @@ message SolanaAddress { required string address = 1; // Address in Solana format (base58 of a pubkey) } +/** + * @embed + */ +message SolanaTxATADetails { + required string owner_address = 1; + required string program_id = 2; + required string mint_address = 3; + required string associated_token_address = 4; +} +/** + * @embed + */ +message SolanaTxExtraInfo { + repeated SolanaTxATADetails ata_details = 1; +} /** * Request: ask device to sign Solana transaction * @start @@ -46,6 +61,7 @@ message SolanaAddress { message SolanaSignTx { repeated uint32 address_n = 1; // BIP-32 path to derive the key from master node required bytes raw_tx = 2; // serialized raw transaction + optional SolanaTxExtraInfo extra_info = 3; // extra information for the transaction } /** diff --git a/core/src/all_modules.py b/core/src/all_modules.py index bd12b554b5..ab62fd6888 100644 --- a/core/src/all_modules.py +++ b/core/src/all_modules.py @@ -845,6 +845,8 @@ import apps.solana.spl.spl_token_program apps.solana.spl.spl_tokens import apps.solana.spl.spl_tokens +apps.solana.spl.token_account +import apps.solana.spl.token_account apps.solana.stake.program import apps.solana.stake.program apps.solana.system._layouts diff --git a/core/src/apps/base.py b/core/src/apps/base.py index 15253a235a..2b720f88d1 100644 --- a/core/src/apps/base.py +++ b/core/src/apps/base.py @@ -78,7 +78,7 @@ def busy_expiry_ms() -> int: def get_features() -> Features: - import storage.recovery + # import storage.recovery import storage.sd_salt import storage # workaround for https://github.com/microsoft/pyright/issues/2685 @@ -87,6 +87,7 @@ def get_features() -> Features: from trezor.messages import Features from trezor import uart from apps.common import mnemonic, safety_checks + from trezor.ui import style storage_serial_no = storage.device.get_serial() serial_no = storage_serial_no @@ -125,6 +126,12 @@ def get_features() -> Features: onekey_serial_no=storage_serial_no, onekey_ble_name=uart.get_ble_name(), onekey_ble_version=uart.get_ble_version(), + haptic_feedback=storage.device.haptic_enabled(), + auto_lock_delay_ms=storage.device.get_autolock_delay_ms(), + auto_shutdown_delay_ms=storage.device.get_autoshutdown_delay_ms(), + brightness_percent=int( + storage.device.get_brightness() / style.BACKLIGHT_MAX * 100 + ), ) if utils.BITCOIN_ONLY: @@ -188,7 +195,6 @@ def get_features() -> Features: f.wipe_code_protection = config.has_wipe_code() f.passphrase_always_on_device = storage.device.get_passphrase_always_on_device() f.safety_checks = safety_checks.read_setting() - f.auto_lock_delay_ms = storage.device.get_autolock_delay_ms() f.display_rotation = storage.device.get_rotation() f.experimental_features = storage.device.get_experimental_features() diff --git a/core/src/apps/bitcoin/get_address.py b/core/src/apps/bitcoin/get_address.py index 20bc9bafba..8ee4621e39 100644 --- a/core/src/apps/bitcoin/get_address.py +++ b/core/src/apps/bitcoin/get_address.py @@ -49,10 +49,11 @@ async def get_address( ctx, keychain, msg.address_n, + not msg.multisig, validate_path_against_script_type(coin, msg), ) - node = keychain.derive(msg.address_n) + node = keychain.derive(msg.address_n, force_strict=not msg.multisig) address = addresses.get_address(msg.script_type, coin, node, msg.multisig) address_short = addresses.address_short(coin, address) diff --git a/core/src/apps/bitcoin/sign_tx/bitcoin.py b/core/src/apps/bitcoin/sign_tx/bitcoin.py index f406c5269d..e1007a9f68 100644 --- a/core/src/apps/bitcoin/sign_tx/bitcoin.py +++ b/core/src/apps/bitcoin/sign_tx/bitcoin.py @@ -179,7 +179,9 @@ async def step1_process_inputs(self) -> None: writers.write_tx_input_check(h_presigned_inputs_check, txi) await self.process_external_input(txi) else: - node = self.keychain.derive(txi.address_n) + node = self.keychain.derive( + txi.address_n, force_strict=not txi.multisig + ) await self.process_internal_input(txi, node) script_pubkey = self.input_derive_script(txi, node) @@ -585,7 +587,7 @@ async def serialize_segwit_input(self, i: int) -> None: self.tx_info.check_input(txi) if txi.script_type == InputScriptType.SPENDP2SHWITNESS: - node = self.keychain.derive(txi.address_n) + node = self.keychain.derive(txi.address_n, force_strict=not txi.multisig) key_sign_pub = node.public_key() else: # Native SegWit has an empty scriptSig. Public key is not needed. @@ -599,7 +601,7 @@ def sign_bip143_input(self, i: int, txi: TxInput) -> tuple[bytes, bytes]: # script type than the one that was provided during the confirmation phase. raise wire.ProcessError("Transaction has changed during signing") - node = self.keychain.derive(txi.address_n) + node = self.keychain.derive(txi.address_n, force_strict=not txi.multisig) public_key = node.public_key() if txi.multisig: @@ -629,7 +631,7 @@ def sign_taproot_input(self, i: int, txi: TxInput) -> bytes: self.get_sighash_type(txi), ) - node = self.keychain.derive(txi.address_n) + node = self.keychain.derive(txi.address_n, not txi.multisig) return bip340_sign(node, sigmsg_digest) async def sign_segwit_input(self, i: int) -> None: @@ -699,7 +701,9 @@ async def get_legacy_tx_digest( txi_sign = txi if not script_pubkey: self.tx_info.check_input(txi) - node = self.keychain.derive(txi.address_n) + node = self.keychain.derive( + txi.address_n, force_strict=not txi.multisig + ) key_sign_pub = node.public_key() if txi.multisig: # Sanity check to ensure we are signing with a key that is included in the multisig. @@ -917,7 +921,7 @@ def input_derive_script( return txi.script_pubkey if node is None: - node = self.keychain.derive(txi.address_n) + node = self.keychain.derive(txi.address_n, force_strict=not txi.multisig) address = addresses.get_address(txi.script_type, self.coin, node, txi.multisig) return scripts.output_derive_script(address, self.coin) @@ -935,7 +939,7 @@ def output_derive_script(self, txo: TxOutput) -> bytes: ] except KeyError: raise wire.DataError("Invalid script type") - node = self.keychain.derive(txo.address_n) + node = self.keychain.derive(txo.address_n, force_strict=not txo.multisig) txo.address = addresses.get_address( input_script_type, self.coin, node, txo.multisig ) diff --git a/core/src/apps/ethereum/networks.py b/core/src/apps/ethereum/networks.py index bb987c51ee..fddc37b013 100644 --- a/core/src/apps/ethereum/networks.py +++ b/core/src/apps/ethereum/networks.py @@ -1860,3 +1860,11 @@ def _networks_iterator() -> Iterator[NetworkInfoTuple]: "evm-dtt.png", # icon 0x1A2A5F, # primary_color ) + yield ( + 1672, # chain_id + 1672, # slip44 + "PROS", # symbol + "Pharos", # name + "evm-pros.png", # icon + 0x00FF33, # primary_color + ) diff --git a/core/src/apps/ethereum/networks.py.mako b/core/src/apps/ethereum/networks.py.mako index ca605409b3..ea8c1e4ea7 100644 --- a/core/src/apps/ethereum/networks.py.mako +++ b/core/src/apps/ethereum/networks.py.mako @@ -94,3 +94,11 @@ def _networks_iterator() -> Iterator[NetworkInfoTuple]: "evm-dtt.png", # icon 0x1A2A5F, # primary_color ) + yield ( + 1672, # chain_id + 1672, # slip44 + "PROS", # symbol + "Pharos", # name + "evm-pros.png", # icon + 0x00FF33, # primary_color + ) diff --git a/core/src/apps/management/apply_settings.py b/core/src/apps/management/apply_settings.py index fbfe069707..90447247f3 100644 --- a/core/src/apps/management/apply_settings.py +++ b/core/src/apps/management/apply_settings.py @@ -1,6 +1,6 @@ from typing import TYPE_CHECKING -import storage.device +import storage.device as storage_device from trezor import wire from trezor.enums import ButtonRequestType, SafetyCheckLevel from trezor.lvglui.i18n import gettext as _, keys as i18n_keys @@ -19,9 +19,9 @@ def validate_homescreen(homescreen: bytes) -> None: # if homescreen == b"": # return - # if len(homescreen) > storage.device.HOMESCREEN_MAXSIZE: + # if len(homescreen) > storage_device.HOMESCREEN_MAXSIZE: # raise wire.DataError( - # f"Homescreen is too large, maximum size is {storage.device.HOMESCREEN_MAXSIZE} bytes" + # f"Homescreen is too large, maximum size is {storage_device.HOMESCREEN_MAXSIZE} bytes" # ) # try: @@ -46,77 +46,120 @@ def validate_homescreen(homescreen: bytes) -> None: async def apply_settings(ctx: wire.Context, msg: ApplySettings) -> Success: - if not storage.device.is_initialized(): + if not storage_device.is_initialized(): raise wire.NotInitialized("Device is not initialized") - if ( - msg.homescreen is None - and msg.label is None - and msg.use_passphrase is None - and msg.passphrase_always_on_device is None - and msg.display_rotation is None - and msg.auto_lock_delay_ms is None - and msg.safety_checks is None - and msg.experimental_features is None - ): + language = msg.language + # homescreen = msg.homescreen + label = msg.label + auto_lock_delay_ms = msg.auto_lock_delay_ms + use_passphrase = msg.use_passphrase + passphrase_always_on_device = msg.passphrase_always_on_device + # display_rotation = msg.display_rotation + msg_safety_checks = msg.safety_checks + # experimental_features = msg.experimental_features + haptic_feedback = msg.haptic_feedback + auto_shutdown_delay_ms = msg.auto_shutdown_delay_ms + change_brightness = msg.change_brightness + + allowed_settings = [ + language, + # homescreen, + label, + auto_lock_delay_ms, + use_passphrase, + passphrase_always_on_device, + msg_safety_checks, + haptic_feedback, + auto_shutdown_delay_ms, + change_brightness, + ] + if all(s is None for s in allowed_settings): raise wire.ProcessError("No setting provided") - if msg.homescreen is not None: - validate_homescreen(msg.homescreen) - # await confirm_set_homescreen(ctx) - # storage.device.set_homescreen(f"A:/res/{msg.homescreen.decode()}") + # if homescreen is not None: + # validate_homescreen(homescreen) + # # await confirm_set_homescreen(ctx) + # # storage_device.set_homescreen(f"A:/res/{msg.homescreen.decode()}") - if msg.label is not None: - if len(msg.label.encode("utf-8")) > storage.device.LABEL_MAXLENGTH: + if label is not None: + if len(label.encode("utf-8")) > storage_device.LABEL_MAXLENGTH: raise wire.DataError("Label too long") - await require_confirm_change_label(ctx, msg.label) - storage.device.set_label(msg.label) + await require_confirm_change_label(ctx, label) + storage_device.set_label(label) - if msg.use_passphrase is not None: - await require_confirm_change_passphrase(ctx, msg.use_passphrase) - storage.device.set_passphrase_enabled(msg.use_passphrase) + if use_passphrase is not None: + await require_confirm_change_passphrase(ctx, use_passphrase) + storage_device.set_passphrase_enabled(use_passphrase) - if not msg.use_passphrase and storage.device.is_passphrase_pin_enabled(): + if not use_passphrase and storage_device.is_passphrase_pin_enabled(): from apps.base import lock_device - storage.device.set_passphrase_pin_enabled(False) + storage_device.set_passphrase_pin_enabled(False) lock_device() - if msg.passphrase_always_on_device is not None: - if not storage.device.is_passphrase_enabled(): + if passphrase_always_on_device is not None: + if not storage_device.is_passphrase_enabled(): raise wire.DataError("Passphrase is not enabled") # else: # if not msg.passphrase_always_on_device: # raise wire.DataError("Only support passphrase input on device") - await require_confirm_change_passphrase_source( - ctx, msg.passphrase_always_on_device - ) - storage.device.set_passphrase_always_on_device(msg.passphrase_always_on_device) + await require_confirm_change_passphrase_source(ctx, passphrase_always_on_device) + storage_device.set_passphrase_always_on_device(passphrase_always_on_device) - if msg.auto_lock_delay_ms is not None: - if msg.auto_lock_delay_ms < storage.device.AUTOLOCK_DELAY_MINIMUM: + if auto_lock_delay_ms is not None: + if auto_lock_delay_ms < storage_device.AUTOLOCK_DELAY_MINIMUM: raise wire.ProcessError("Auto-lock delay too short") - if msg.auto_lock_delay_ms > storage.device.AUTOLOCK_DELAY_MAXIMUM: + if auto_lock_delay_ms > storage_device.AUTOLOCK_DELAY_MAXIMUM: raise wire.ProcessError("Auto-lock delay too long") - await require_confirm_change_autolock_delay(ctx, msg.auto_lock_delay_ms) - storage.device.set_autolock_delay_ms(msg.auto_lock_delay_ms) - - if msg.safety_checks is not None: - await require_confirm_safety_checks(ctx, msg.safety_checks) - safety_checks.apply_setting(msg.safety_checks) - - if msg.display_rotation is not None: - raise wire.ProcessError("Not support yet") - # await require_confirm_change_display_rotation(ctx, msg.display_rotation) - # storage.device.set_rotation(msg.display_rotation) - - if msg.experimental_features is not None: - await require_confirm_experimental_features(ctx, msg.experimental_features) - storage.device.set_experimental_features(msg.experimental_features) + await require_confirm_change_autolock_delay(ctx, auto_lock_delay_ms) + storage_device.set_autolock_delay_ms(auto_lock_delay_ms) + + if auto_shutdown_delay_ms is not None: + if auto_shutdown_delay_ms < storage_device.AUTOSHUTDOWN_DELAY_MINIMUM: + raise wire.ProcessError("Auto-shutdown delay too short") + if auto_shutdown_delay_ms > storage_device.AUTOSHUTDOWN_DELAY_MAXIMUM: + raise wire.ProcessError("Auto-shutdown delay too long") + await require_confirm_change_autoshutdown_delay(ctx, auto_shutdown_delay_ms) + storage_device.set_autoshutdown_delay_ms(auto_shutdown_delay_ms) + + if msg_safety_checks is not None: + await require_confirm_safety_checks(ctx, msg_safety_checks) + safety_checks.apply_setting(msg_safety_checks) + + # if display_rotation is not None: + # raise wire.ProcessError("Not support yet") + # # await require_confirm_change_display_rotation(ctx, msg.display_rotation) + # # storage_device.set_rotation(msg.display_rotation) + if haptic_feedback is not None: + await require_confirm_haptic_feedback(ctx, haptic_feedback) + storage_device.toggle_keyboard_haptic(haptic_feedback) + if change_brightness is not None: + from trezor.ui.layouts import request_change_brightness + + await request_change_brightness(ctx) + # if experimental_features is not None: + # raise wire.ProcessError("Not support yet") + # # await require_confirm_experimental_features(ctx, experimental_features) + # # storage_device.set_experimental_features(experimental_features) + + if language: + from trezor.langs import langs_keys, langs + from trezor.lvglui.i18n import i18n_refresh + + if language not in langs_keys: + raise wire.DataError( + f"all support ISO_639-1 language keys include {' '.join(langs_keys)})" + ) + index = langs_keys.index(language) + language_str = langs[index][1] + await require_confirm_change_language(ctx, language_str) + storage_device.set_language(language) + i18n_refresh(language) reload_settings_from_storage() - storage.device._LABEL_VALUE = None - updated_label = storage.device.get_label() + storage_device._LABEL_VALUE = None + updated_label = storage_device.get_label() from trezor.lvglui.scrs.homescreen import MainScreen @@ -125,7 +168,7 @@ async def apply_settings(ctx: wire.Context, msg: ApplySettings) -> Success: if ( hasattr(main_screen, "title") and main_screen.title - and storage.device.is_device_name_display_enabled() + and storage_device.is_device_name_display_enabled() ): main_screen.title.set_text(updated_label) @@ -136,7 +179,7 @@ async def apply_settings(ctx: wire.Context, msg: ApplySettings) -> Success: if ( hasattr(lock_screen, "title") and lock_screen.title - and storage.device.is_device_name_display_enabled() + and storage_device.is_device_name_display_enabled() ): lock_screen.title.set_text(updated_label) @@ -239,10 +282,30 @@ async def require_confirm_change_autolock_delay( ctx, "set_autolock_delay", _(i18n_keys.TITLE__AUTO_LOCK), - description=_( - i18n_keys.SUBTITLE__DO_YOU_REALLY_WANT_TO_AUTO_LOCK_YOUR_DEVICE_AFTER_STR + description=_(i18n_keys.SUBTITLE__SET_AUTO_LOCK), + description_param=format_duration_ms( + delay_ms, storage_device.AUTOLOCK_DELAY_MAXIMUM ), - description_param=format_duration_ms(delay_ms), + icon=None, + verb=_(i18n_keys.BUTTON__CHANGE), + br_code=ButtonRequestType.ProtectCall, + anim_dir=2, + ) + + +async def require_confirm_change_autoshutdown_delay( + ctx: wire.GenericContext, delay_ms: int +) -> None: + await confirm_action( + ctx, + "set_autoshutdown_delay", + _(i18n_keys.TITLE__SET_AUTO_SHUTDOWN), + description=_(i18n_keys.SUBTITLE__SET_AUTO_SHUTDOWN), + description_param=format_duration_ms( + delay_ms, storage_device.AUTOSHUTDOWN_DELAY_MAXIMUM + ), + icon=None, + verb=_(i18n_keys.BUTTON__CHANGE), br_code=ButtonRequestType.ProtectCall, anim_dir=2, ) @@ -309,3 +372,39 @@ async def require_confirm_experimental_features( br_code=ButtonRequestType.ProtectCall, anim_dir=2, ) + + +async def require_confirm_haptic_feedback( + ctx: wire.GenericContext, enable: bool +) -> None: + if enable: + description = _(i18n_keys.SUBTITLE__OPEN_VIBRATION_HAPTIC) + confirm_text = _(i18n_keys.BUTTON__OPEN) + else: + description = _(i18n_keys.SUBTITLE__CLOSE_VIBRATION_HAPTIC) + confirm_text = _(i18n_keys.BUTTON__CLOSE) + await confirm_action( + ctx, + "set_haptic_feedback", + _(i18n_keys.TITLE__VIBRATION_AND_HAPTIC), + description=description, + icon=None, + verb=confirm_text, + br_code=ButtonRequestType.ProtectCall, + ) + + +async def require_confirm_change_language( + ctx: wire.GenericContext, language_str +) -> None: + await confirm_action( + ctx, + "set_language", + _(i18n_keys.TITLE__SET_LANGUAGE), + description=_(i18n_keys.SUBTITLE__SET_LANGUAGE), + description_param=language_str, + icon=None, + verb=_(i18n_keys.BUTTON__CHANGE), + br_code=ButtonRequestType.ProtectCall, + anim_dir=2, + ) diff --git a/core/src/apps/solana/sign_tx.py b/core/src/apps/solana/sign_tx.py index 66d6dea400..48f784a520 100644 --- a/core/src/apps/solana/sign_tx.py +++ b/core/src/apps/solana/sign_tx.py @@ -105,6 +105,9 @@ async def sign_tx( elif program_id == SPL_TOKEN_PROGRAM_ID: from .spl.spl_token_program import parse + if msg.extra_info: + + ctx.extra = msg.extra_info.ata_details await parse(ctx, accounts, i.data) elif program_id == SPL_ASSOCIATED_TOKEN_ACCOUNT_PROGRAM_ID: from .spl.ata_program import parse diff --git a/core/src/apps/solana/spl/spl_token_program.py b/core/src/apps/solana/spl/spl_token_program.py index 7d93222084..8eee613da2 100644 --- a/core/src/apps/solana/spl/spl_token_program.py +++ b/core/src/apps/solana/spl/spl_token_program.py @@ -350,11 +350,10 @@ async def parse(ctx: wire.Context, accounts: list[PublicKey], data: bytes) -> No await confirm_sol_token_transfer( ctx, - from_addr=str(params.source), - to_addr=str(params.dest), amount=f"{params.amount} Lamports Token", + from_ata_addr=str(params.source), + to_ata_addr=str(params.dest), source_owner=str(params.owner), - fee_payer=str(params.owner), ) elif instruction_type == InstructionType.APPROVE: parsed_data = AMOUNT_LAYOUT.parse(data) @@ -434,16 +433,30 @@ async def parse(ctx: wire.Context, accounts: list[PublicKey], data: bytes) -> No ) from trezor.ui.layouts import confirm_sol_token_transfer from ..utils.helpers import sol_format_amount, get_spl_token - + from ..spl.token_account import try_get_token_account_owner_address + from ..constents import SPL_TOKEN_PROGRAM_ID + + owner_address = None + if hasattr(ctx, "extra"): + owner_address = try_get_token_account_owner_address( + params.dest.get(), + SPL_TOKEN_PROGRAM_ID.get(), + params.mint.get(), + ctx.extra, + ) + # if owner_address is None: + # raise wire.DataError("Invalid ata params") await confirm_sol_token_transfer( ctx, - from_addr=str(params.source), - to_addr=str(params.dest), amount=sol_format_amount( params.amount, decimals=params.decimals, token_mint=params.mint ), + from_ata_addr=str(params.source), + to_ata_addr=str(params.dest), source_owner=str(params.owner), - fee_payer=str(params.owner), + destination_owner=str(PublicKey(owner_address)) + if owner_address is not None + else None, token_mint=None if get_spl_token(str(params.mint)) else str(params.mint), ) elif instruction_type == InstructionType.APPROVE2: diff --git a/core/src/apps/solana/spl/token_account.py b/core/src/apps/solana/spl/token_account.py new file mode 100644 index 0000000000..db306b7293 --- /dev/null +++ b/core/src/apps/solana/spl/token_account.py @@ -0,0 +1,64 @@ +from typing import TYPE_CHECKING + +from trezor.crypto import base58 + +from ..constents import SPL_ASSOCIATED_TOKEN_ACCOUNT_PROGRAM_ID + +if TYPE_CHECKING: + from trezor.messages import SolanaTxATADetails + +SEED_CONSTANT = "ProgramDerivedAddress" + + +def try_finding_associated_token_account( + owner_address: bytes, + program_id: bytes, + mint_address: bytes, + associated_token_address: bytes, +) -> bool: + from trezor.crypto.hashlib import sha256 + + # based on the following sources: + # https://spl.solana.com/associated-token-account#finding-the-associated-token-account-address + # https://github.com/solana-labs/solana/blob/8fbe033eaca693ed8c3e90b19bc3f61b32885e5e/sdk/program/src/pubkey.rs#L495 + for seed_bump in range(255, 0, -1): + seed = ( + owner_address + + program_id + + mint_address + + bytes([seed_bump]) + + SPL_ASSOCIATED_TOKEN_ACCOUNT_PROGRAM_ID.get() + + SEED_CONSTANT.encode("utf-8") + ) + + account = sha256(seed).digest() + + if account == associated_token_address: + return True + + return False + + +def try_get_token_account_owner_address( + token_account_address: bytes, + token_program: bytes, + token_mint: bytes, + ata_details: list[SolanaTxATADetails], +) -> bytes | None: + for ata_detail in ata_details: + if ( + base58.decode(ata_detail.associated_token_address) == token_account_address + and base58.decode(ata_detail.program_id) == token_program + and base58.decode(ata_detail.mint_address) == token_mint + ): + owner_address = base58.decode(ata_detail.owner_address) + + if try_finding_associated_token_account( + owner_address, + token_program, + token_mint, + token_account_address, + ): + return owner_address + + return None diff --git a/core/src/storage/device.py b/core/src/storage/device.py index 6bd84d308c..2805a25766 100644 --- a/core/src/storage/device.py +++ b/core/src/storage/device.py @@ -816,7 +816,7 @@ def set_device_name_display_enabled(enable: bool) -> None: _DEVICE_NAME_DISPLAY_ENABLED_VALUE = enable -def keyboard_haptic_enabled() -> bool: +def haptic_enabled() -> bool: global _KEYBOARD_HAPTIC_VALUE if _KEYBOARD_HAPTIC_VALUE is None: haptic_enabled = common.get(_NAMESPACE, _KEYBOARD_HAPTIC, public=True) diff --git a/core/src/trezor/lvglui/i18n/keys.py b/core/src/trezor/lvglui/i18n/keys.py index dff3ccc082..87d43dc9a2 100644 --- a/core/src/trezor/lvglui/i18n/keys.py +++ b/core/src/trezor/lvglui/i18n/keys.py @@ -687,11 +687,11 @@ TITLE__STR_TRANSFER = 311 # Fee Payer LIST_KEY__FEE_PAYER__COLON = 312 -# New Token Account: +# New Token Account LIST_KEY__NEW_TOKEN_ACCOUNT = 313 -# Mint Address: +# Mint Address LIST_KEY__MINT_ADDRESS = 314 -# Owner: +# Owner LIST_KEY__OWNER = 315 # Funded by LIST_KEY__FUNDED_BY__COLON = 316 @@ -2321,20 +2321,20 @@ BUTTON__USE_MULTIPLE_SOURCES_OF_ENTROPY_DESC = 1050 # Set Language TITLE__SET_LANGUAGE = 1051 -# Do you want to change language to {language}? +# Do you want to change language to {}? SUBTITLE__SET_LANGUAGE = 1052 # Set Auto-Lock -TITLE_SET_AUTO_LOCK = 1053 -# Do you want to change Auto-Lock time to {time}? -SUBTITLE_SET_AUTO_LOCK = 1054 +TITLE__SET_AUTO_LOCK = 1053 +# Do you want to change Auto-Lock time to {}? +SUBTITLE__SET_AUTO_LOCK = 1054 # Set Auto Shutdown -TITLE_SET_AUTO_SHUTDOWN = 1055 -# Do you want to change Auto-Shutdown time to {time}? -SUBTITLE_SET_AUTO_SHUTDOWN = 1056 +TITLE__SET_AUTO_SHUTDOWN = 1055 +# Do you want to change Auto-Shutdown time to {}? +SUBTITLE__SET_AUTO_SHUTDOWN = 1056 # Do you want to open Haptic ? -SUBTITLE_OPEN_VIBRATION_HAPTIC = 1057 +SUBTITLE__OPEN_VIBRATION_HAPTIC = 1057 # Do you want to close Haptic ? -SUBTITLE_CLOSE_VIBRATION_HAPTIC = 1058 +SUBTITLE__CLOSE_VIBRATION_HAPTIC = 1058 # Do you want to switch PIN Keypad to default ? (The numbers on PIN keypad arr # anged in sequence) SUBTITLE_SWITCH_PIN_KEYPAD_DEFAULT = 1059 @@ -2364,7 +2364,7 @@ # Apply BUTTON__APPLY = 1070 # Open -BUTTON_OPEN = 1071 +BUTTON__OPEN = 1071 # Set Brightness TITLE__SET_BRIGHTNESS = 1072 # fmt: on diff --git a/core/src/trezor/lvglui/i18n/locales/de.py b/core/src/trezor/lvglui/i18n/locales/de.py index ee8e09e17f..8eafc22b82 100644 --- a/core/src/trezor/lvglui/i18n/locales/de.py +++ b/core/src/trezor/lvglui/i18n/locales/de.py @@ -313,9 +313,9 @@ "Merken Sie sich Ihre Passphrase: Bei Verlust nicht wiederherstellbar.", "{} Transfer", "Gebührenzahler", - "Neues Token-Konto:", - "Neuwertige Adresse:", - "Eigentümer:", + "Neues Token-Konto", + "Neuwertige Adresse", + "Eigentümer", "Gefördert durch", "Erstellen Sie ein Token-Konto", "Unterzeichner", @@ -1052,11 +1052,11 @@ "Verwenden Sie mehrere Entropiequellen", "Standardmäßig verwendet das Gerät das Secure Element, um mnemonische Phrasen zu erzeugen. Wenn aktiviert, wird Entropie sowohl vom MCU als auch vom Secure Element kombiniert. Beide Methoden erfüllen kryptografische Sicherheitsstandards.", "Sprache festlegen", - "Möchten Sie die Sprache ändern zu {language}?", + "Möchten Sie die Sprache in {} ändern?", "Automatische Sperre einstellen", - "Möchtest du die Auto-Sperrzeit auf {time} ändern?", + "Möchtest du die Auto-Sperrzeit auf {} ändern?", "Automatische Abschaltung festlegen", - "Möchten Sie die automatische Abschaltzeit auf {time} ändern?", + "Möchten Sie die automatische Abschaltzeit auf {} ändern?", "Möchten Sie die Haptik öffnen?", "Möchten Sie die Haptik schließen?", "Möchten Sie die PIN-Tastatur auf die Standardeinstellung umstellen? (Die Zahlen auf der PIN-Tastatur sind der Reihe nach angeordnet)", diff --git a/core/src/trezor/lvglui/i18n/locales/en.py b/core/src/trezor/lvglui/i18n/locales/en.py index eb43fda5db..08fedd32cd 100644 --- a/core/src/trezor/lvglui/i18n/locales/en.py +++ b/core/src/trezor/lvglui/i18n/locales/en.py @@ -313,9 +313,9 @@ "Remember your passphrase: Irrecoverable if lost.", "{} Transfer", "Fee Payer", - "New Token Account:", - "Mint Address:", - "Owner:", + "New Token Account", + "Mint Address", + "Owner", "Funded by", "Create Token Account", "Signer", @@ -1052,11 +1052,11 @@ "Use multiple sources of entropy", "By default, the device uses the secure element to generate mnemonic phrases. When enabled, entropy from both the MCU and secure element will be combined. Both methods meet cryptographic security standards.", "Set Language", - "Do you want to change language to {language}?", + "Do you want to change language to {}?", "Set Auto-Lock", - "Do you want to change Auto-Lock time to {time}?", + "Do you want to change Auto-Lock time to {}?", "Set Auto Shutdown", - "Do you want to change Auto-Shutdown time to {time}?", + "Do you want to change Auto-Shutdown time to {}?", "Do you want to open Haptic ?", "Do you want to close Haptic ?", "Do you want to switch PIN Keypad to default ? (The numbers on PIN keypad arranged in sequence)", diff --git a/core/src/trezor/lvglui/i18n/locales/es.py b/core/src/trezor/lvglui/i18n/locales/es.py index 53801a9e36..1e1503156e 100644 --- a/core/src/trezor/lvglui/i18n/locales/es.py +++ b/core/src/trezor/lvglui/i18n/locales/es.py @@ -313,9 +313,9 @@ "Recuerda tu frase de contraseña: Irrecuperable si la pierdes.", "{} Transferir", "Pagador de la tarifa", - "Nueva cuenta de tokens:", - "Dirección de creador de la moneda:", - "Dueño:", + "Nueva cuenta de tokens", + "Dirección de creador de la moneda", + "Dueño", "Fundado por", "Crear cuenta de token", "Firmante", @@ -1052,11 +1052,11 @@ "Usa múltiples fuentes de entropía", "De forma predeterminada, el dispositivo utiliza el elemento seguro para generar frases mnemotécnicas. Cuando está habilitado, se combinará la entropía tanto del MCU como del elemento seguro. Ambos métodos cumplen con los estándares de seguridad criptográfica.", "Configurar idioma", - "¿Quieres cambiar el idioma a {language}?", + "¿Quieres cambiar el idioma a {}?", "Configurar bloqueo automático", - "¿Quieres cambiar el tiempo de Bloqueo automático a {time}?", + "¿Quieres cambiar el tiempo de Bloqueo automático a {}?", "Configurar apagado automático", - "¿Deseas cambiar el tiempo de apagado automático a {time}?", + "¿Deseas cambiar el tiempo de apagado automático a {}?", "¿Quieres abrir la respuesta háptica?", "¿Quieres cerrar la respuesta háptica?", "¿Deseas cambiar el teclado PIN al predeterminado? (Los números en el teclado PIN están ordenados en secuencia)", diff --git a/core/src/trezor/lvglui/i18n/locales/fr.py b/core/src/trezor/lvglui/i18n/locales/fr.py index 49e2dc93f5..72caf68468 100644 --- a/core/src/trezor/lvglui/i18n/locales/fr.py +++ b/core/src/trezor/lvglui/i18n/locales/fr.py @@ -313,9 +313,9 @@ "N'oubliez pas votre phrase secrète : irrécupérable si perdue.", "{} Transférer", "Payeur de frais", - "Nouveau compte de jeton:", - "Adresse de l'atelier:", - "Propriétaire:", + "Nouveau compte de jeton", + "Adresse de l'atelier", + "Propriétaire", "Financé par", "Créer un compte de jeton", "Signataire", @@ -1052,11 +1052,11 @@ "Utiliser plusieurs sources d'entropie", "Par défaut, l'appareil utilise l'élément sécurisé pour générer des phrases mnémoniques. Lorsque cette option est activée, l'entropie du MCU et de l'élément sécurisé sera combinée. Les deux méthodes répondent aux normes de sécurité cryptographique.", "Définir la langue", - "Voulez-vous changer la langue en {language} ?", + "Voulez-vous changer la langue en {} ?", "Définir le verrouillage automatique", - "Voulez-vous modifier le délai de verrouillage automatique à {time} ?", + "Voulez-vous modifier le délai de verrouillage automatique à {} ?", "Définir l'arrêt automatique", - "Voulez-vous modifier l'heure d'arrêt automatique à {time} ?", + "Voulez-vous modifier l'heure d'arrêt automatique à {} ?", "Voulez-vous ouvrir le retour haptique ?", "Voulez-vous fermer le retour haptique ?", "Voulez-vous rétablir le clavier PIN par défaut ? (Les chiffres du clavier PIN sont disposés en séquence)", diff --git a/core/src/trezor/lvglui/i18n/locales/it.py b/core/src/trezor/lvglui/i18n/locales/it.py index f1f3918be1..94c1a1e657 100644 --- a/core/src/trezor/lvglui/i18n/locales/it.py +++ b/core/src/trezor/lvglui/i18n/locales/it.py @@ -313,9 +313,9 @@ "Ricorda la tua passphrase: Irrecuperabile se persa.", "{} Trasferimento", "Pagatore di tasse", - "Nuovo Conto Gettoni:", - "Indirizzo di zecca:", - "Proprietario:", + "Nuovo Conto Gettoni", + "Indirizzo di zecca", + "Proprietario", "Fondato da", "Crea un account token", "Firmatario", @@ -1052,11 +1052,11 @@ "Usa più fonti di entropia", "Per impostazione predefinita, il dispositivo utilizza l’elemento sicuro per generare frasi mnemoniche. Quando abilitata, l’entropia sia della MCU che dell’elemento sicuro verrà combinata. Entrambi i metodi soddisfano gli standard di sicurezza crittografica.", "Imposta lingua", - "Vuoi cambiare lingua in {language}?", + "Vuoi cambiare lingua in {}?", "Imposta Blocco automatico", - "Vuoi cambiare il tempo di Blocco automatico a {time}?", + "Vuoi cambiare il tempo di Blocco automatico a {}?", "Imposta spegnimento automatico", - "Vuoi cambiare il tempo di spegnimento automatico a {time}?", + "Vuoi cambiare il tempo di spegnimento automatico a {}?", "Vuoi aprire il feedback aptico?", "Vuoi chiudere il feedback aptico?", "Vuoi ripristinare il tastierino PIN alle impostazioni predefinite? (I numeri sul tastierino PIN sono disposti in sequenza)", diff --git a/core/src/trezor/lvglui/i18n/locales/ja.py b/core/src/trezor/lvglui/i18n/locales/ja.py index 95bf8046a6..64c80e4c06 100644 --- a/core/src/trezor/lvglui/i18n/locales/ja.py +++ b/core/src/trezor/lvglui/i18n/locales/ja.py @@ -313,9 +313,9 @@ "パスフレーズを忘れずに:紛失すると復元できません。", "{} 送信", "料金支払人", - "新しいトークン アカウント:", - "ミントアドレス:", - "所有者:", + "新しいトークン アカウント", + "ミントアドレス", + "所有者", "供給者", "トークン アカウント作成", "署名者", @@ -1052,11 +1052,11 @@ "複数のエントロピー源を使用する", "デフォルトでは、デバイスはセキュアエレメントを使用してニーモニックフレーズを生成します。有効にすると、MCUとセキュアエレメントの両方からのエントロピーが組み合わされます。どちらの方法も暗号セキュリティ標準を満たしています。", "言語を設定", - "言語を{language}に変更しますか?", + "言語を{}に変更しますか?", "自動ロックを設定", - "自動ロック時間を{time}に変更しますか?", + "自動ロック時間を{}に変更しますか?", "自動シャットダウンを設定", - "自動シャットダウン時刻を{time}に変更しますか?", + "自動シャットダウン時刻を{}に変更しますか?", "触覚フィードバックを開きますか?", "触覚フィードバックを閉じますか?", "PINキーパッドをデフォルトに切り替えますか?(PINキーパッドの数字が順番に配置されます)", diff --git a/core/src/trezor/lvglui/i18n/locales/ko.py b/core/src/trezor/lvglui/i18n/locales/ko.py index 3c1041f9fb..5fe8741295 100644 --- a/core/src/trezor/lvglui/i18n/locales/ko.py +++ b/core/src/trezor/lvglui/i18n/locales/ko.py @@ -314,8 +314,8 @@ "{} 옮기다", "수수료 납부자", "새 토큰 계정:", - "민트 주소:", - "소유자:", + "민트 주소", + "소유자", "의해 설립되다", "토큰 계정 생성", "서명자", @@ -1052,11 +1052,11 @@ "여러 출처의 엔트로피를 사용하세요", "기본적으로 기기는 보안 요소를 사용해 니모닉 구문을 생성합니다. 이 기능을 활성화하면 MCU와 보안 요소의 엔트로피가 결합됩니다. 두 방식 모두 암호학적 보안 표준을 충족합니다.", "언어 설정", - "언어를 {language}(으)로 변경하시겠습니까?", + "언어를 {}(으)로 변경하시겠습니까?", "자동 잠금 설정", - "자동 잠금 시간을 {time}(으)로 변경하시겠습니까?", + "자동 잠금 시간을 {}(으)로 변경하시겠습니까?", "자동 종료 설정", - "자동 종료 시간을 {time}(으)로 변경하시겠습니까?", + "자동 종료 시간을 {}(으)로 변경하시겠습니까?", "햅틱을 여시겠습니까?", "햅틱을 닫으시겠습니까?", "PIN 키패드를 기본 설정으로 전환하시겠습니까? (PIN 키패드의 숫자가 순서대로 배열됩니다)", diff --git a/core/src/trezor/lvglui/i18n/locales/pt_br.py b/core/src/trezor/lvglui/i18n/locales/pt_br.py index 28cbd6eec6..bc3645de72 100644 --- a/core/src/trezor/lvglui/i18n/locales/pt_br.py +++ b/core/src/trezor/lvglui/i18n/locales/pt_br.py @@ -313,9 +313,9 @@ "Lembre-se da sua frase-senha: Irrecuperável se perdida.", "{} Transferência", "Pagador de Taxas", - "Nova Conta de Token:", - "Endereço Mint:", - "Proprietário:", + "Nova Conta de Token", + "Endereço Mint", + "Proprietário", "Financiado por", "Criar Conta de Token", "Assinante", @@ -1052,11 +1052,11 @@ "Use múltiplas fontes de entropia", "Por padrão, o dispositivo usa o elemento seguro para gerar frases mnemônicas. Quando ativado, a entropia do MCU e do elemento seguro serão combinadas. Ambos os métodos atendem aos padrões de segurança criptográfica.", "Definir Idioma", - "Deseja alterar o idioma para {language}?", + "Deseja alterar o idioma para {}?", "Definir Bloqueio Automático", - "Deseja alterar o tempo de Bloqueio Automático para {time}?", + "Deseja alterar o tempo de Bloqueio Automático para {}?", "Definir Desligamento Automático", - "Deseja alterar o tempo de Desligamento Automático para {time}?", + "Deseja alterar o tempo de Desligamento Automático para {}?", "Deseja abrir o Háptico?", "Deseja fechar o Háptico?", "Deseja alternar o Teclado de PIN para o padrão? (Os números no teclado de PIN organizados em sequência)", diff --git a/core/src/trezor/lvglui/i18n/locales/ru.py b/core/src/trezor/lvglui/i18n/locales/ru.py index 2a15f4f283..9089a8a39c 100644 --- a/core/src/trezor/lvglui/i18n/locales/ru.py +++ b/core/src/trezor/lvglui/i18n/locales/ru.py @@ -313,9 +313,9 @@ "Запомните свою кодовую фразу: восстановить невозможно при утере.", "{} Передача", "Плательщик сбора", - "Новый токен-счет:", - "Монетный адрес:", - "Владелец:", + "Новый токен-счет", + "Монетный адрес", + "Владелец", "Финансируется", "Создать учетную запись токена", "Подписант", @@ -1052,11 +1052,11 @@ "Используйте несколько источников энтропии", "По умолчанию устройство использует защищенный элемент для генерации мнемонических фраз. При включении энтропия от MCU и защищенного элемента будет объединена. Оба метода соответствуют стандартам криптографической безопасности.", "Установить язык", - "Вы хотите изменить язык на {language}?", + "Вы хотите изменить язык на {}?", "Установить автоблокировку", - "Вы хотите изменить время автоблокировки на {time}?", + "Вы хотите изменить время автоблокировки на {}?", "Установить автоматическое выключение", - "Вы хотите изменить время автоматического выключения на {time}?", + "Вы хотите изменить время автоматического выключения на {}?", "Вы хотите открыть тактильную обратную связь?", "Вы хотите закрыть тактильную обратную связь?", "Вы хотите переключить клавиатуру PIN-кода на стандартную? (Цифры на клавиатуре PIN-кода расположены последовательно)", diff --git a/core/src/trezor/lvglui/i18n/locales/zh_cn.py b/core/src/trezor/lvglui/i18n/locales/zh_cn.py index a68ee92111..e18940eabd 100644 --- a/core/src/trezor/lvglui/i18n/locales/zh_cn.py +++ b/core/src/trezor/lvglui/i18n/locales/zh_cn.py @@ -313,9 +313,9 @@ "请记住您的密码短语:一旦丢失将无法恢复。", "{} 转账", "交易费支付方", - "新的代币账户:", - "铸币地址:", - "拥有者:", + "新的代币账户", + "铸币地址", + "拥有者", "资助方", "创建代币账户", "签名者", @@ -1052,11 +1052,11 @@ "使用多个熵源", "默认情况下,设备使用安全芯片生成助记词。启用该选项后,设备会同时结合 MCU 与安全芯片的熵源。两种方式都符合加密安全标准。", "设置语言", - "您想将语言更改为{language}吗?", + "您想将语言更改为{}吗?", "设置自动锁定", - "您要将自动锁定时间更改为 {time} 吗?", + "您要将自动锁定时间更改为 {} 吗?", "设置自动关机", - "您想将自动关机时间更改为 {time} 吗?", + "您想将自动关机时间更改为 {} 吗?", "您要打开触感反馈吗?", "您要关闭触感反馈吗?", "您要将 PIN 键盘切换为默认设置吗?(PIN 键盘上的数字按顺序排列)", diff --git a/core/src/trezor/lvglui/i18n/locales/zh_hk.py b/core/src/trezor/lvglui/i18n/locales/zh_hk.py index 90730653cb..4de5afb47f 100644 --- a/core/src/trezor/lvglui/i18n/locales/zh_hk.py +++ b/core/src/trezor/lvglui/i18n/locales/zh_hk.py @@ -313,9 +313,9 @@ "請記住您的密碼短語:一旦遺失將無法恢復。", "{} 轉賬", "交易費支付方", - "新的代幣賬戶:", - "鑄幣地址:", - "擁有者:", + "新的代幣賬戶", + "鑄幣地址", + "擁有者", "資助方", "創建代幣賬戶", "簽名者", @@ -1052,11 +1052,11 @@ "使用多個熵源", "預設情況下,裝置使用安全芯片生成助記詞。啟用此選項後,裝置會同時結合 MCU 與安全芯片的熵源。兩種方式皆符合加密安全標準。", "設定語言", - "你想將語言更改為{language}嗎?", + "你想將語言更改為{}嗎?", "設定自動鎖定", - "您想將自動鎖定時間更改為 {time} 嗎?", + "您想將自動鎖定時間更改為 {} 嗎?", "設定自動關機", - "您想將自動關機時間更改為 {time} 嗎?", + "您想將自動關機時間更改為 {} 嗎?", "你想開啟觸感回饋嗎?", "你想關閉觸感回饋嗎?", "您想將 PIN 鍵盤切換為預設嗎?(PIN 鍵盤上的數字按順序排列)", diff --git a/core/src/trezor/lvglui/res/evm-pros.png b/core/src/trezor/lvglui/res/evm-pros.png new file mode 100644 index 0000000000..d54f0358eb Binary files /dev/null and b/core/src/trezor/lvglui/res/evm-pros.png differ diff --git a/core/src/trezor/lvglui/scrs/homescreen.py b/core/src/trezor/lvglui/scrs/homescreen.py index d762493d8c..d13fd71d9a 100644 --- a/core/src/trezor/lvglui/scrs/homescreen.py +++ b/core/src/trezor/lvglui/scrs/homescreen.py @@ -455,10 +455,6 @@ def _matches(candidate: str | None) -> bool: return replaced -def brightness2_percent_str(brightness: int) -> str: - return f"{int(brightness / style.BACKLIGHT_MAX * 100)}%" - - GRID_CELL_SIZE_ROWS = const(240) GRID_CELL_SIZE_COLS = const(144) @@ -3541,7 +3537,7 @@ def __init__(self, prev_scr=None): self._init = True else: self.backlight.label_right.set_text( - brightness2_percent_str(storage_device.get_brightness()) + utils.brightness2_percent_str(storage_device.get_brightness()) ) self.autolock.label_right.set_text(get_autolock_delay_str()) self.shutdown.label_right.set_text(get_autoshutdown_delay_str()) @@ -3567,7 +3563,7 @@ def __init__(self, prev_scr=None): self.backlight = ListItemBtn( self.container, _(i18n_keys.ITEM__BRIGHTNESS), - brightness2_percent_str(storage_device.get_brightness()), + utils.brightness2_percent_str(storage_device.get_brightness()), ) # Auto lock and shutdown container @@ -5034,226 +5030,199 @@ def eventhandler(self, event_obj): self.on_click(event_obj) -class ShutdownSetting(AnimScreen): - cur_auto_shutdown = "" - cur_auto_shutdown_ms = 0 - - def collect_animation_targets(self) -> list: - targets = [] - if hasattr(self, "container") and self.container: - targets.append(self.container) - return targets - - def __init__(self, prev_scr=None): - ShutdownSetting.cur_auto_shutdown_ms = ( - storage_device.get_autoshutdown_delay_ms() - ) - ShutdownSetting.cur_auto_shutdown = self.get_str_from_ms( - ShutdownSetting.cur_auto_shutdown_ms - ) - if not hasattr(self, "_init"): - self._init = True - else: - if self.cur_auto_shutdown: - self.auto_shutdown.label_right.set_text( - ShutdownSetting.cur_auto_shutdown - ) - self.refresh_text() - return - - super().__init__(prev_scr=prev_scr, title="自动关机", nav_back=True) - - self.container = ContainerFlexCol( - self.content_area, self.title, padding_row=2, pos=(0, 40) - ) - - self.auto_shutdown = ListItemBtn( - self.container, - "自动关机", - ShutdownSetting.cur_auto_shutdown, - ) - - self.container.add_event_cb(self.on_click, lv.EVENT.CLICKED, None) - self.load_screen(self) - gc.collect() - - def get_str_from_ms(self, delay_ms: int) -> str: - if delay_ms in (0, storage_device.AUTOSHUTDOWN_DELAY_MAXIMUM): - return "从不" - elif delay_ms < 60000: - return f"{delay_ms // 1000}秒" - elif delay_ms < 3600000: - return f"{delay_ms // 60000}分钟" - else: - return f"{delay_ms // 3600000}小时" - - def refresh_text(self): - self.title.set_text("自动关机") - self.auto_shutdown.label_left.set_text("自动关机") - - def on_click(self, event_obj): - code = event_obj.code - target = event_obj.get_target() - if code == lv.EVENT.CLICKED: - if utils.lcd_resume(): - return - if target == self.auto_shutdown: - # Use the direct auto-shutdown setting screen - AutoShutDownSetting(self) +# class ShutdownSetting(AnimScreen): +# cur_auto_shutdown = "" +# cur_auto_shutdown_ms = 0 + +# def collect_animation_targets(self) -> list: +# targets = [] +# if hasattr(self, "container") and self.container: +# targets.append(self.container) +# return targets + +# def __init__(self, prev_scr=None): +# ShutdownSetting.cur_auto_shutdown_ms = ( +# storage_device.get_autoshutdown_delay_ms() +# ) +# ShutdownSetting.cur_auto_shutdown = self.get_str_from_ms( +# ShutdownSetting.cur_auto_shutdown_ms +# ) +# if not hasattr(self, "_init"): +# self._init = True +# else: +# if self.cur_auto_shutdown: +# self.auto_shutdown.label_right.set_text( +# ShutdownSetting.cur_auto_shutdown +# ) +# self.refresh_text() +# return + +# super().__init__(prev_scr=prev_scr, title="自动关机", nav_back=True) + +# self.container = ContainerFlexCol( +# self.content_area, self.title, padding_row=2, pos=(0, 40) +# ) + +# self.auto_shutdown = ListItemBtn( +# self.container, +# "自动关机", +# ShutdownSetting.cur_auto_shutdown, +# ) + +# self.container.add_event_cb(self.on_click, lv.EVENT.CLICKED, None) +# self.load_screen(self) +# gc.collect() + +# def get_str_from_ms(self, delay_ms: int) -> str: +# if delay_ms in (0, storage_device.AUTOSHUTDOWN_DELAY_MAXIMUM): +# return "从不" +# elif delay_ms < 60000: +# return f"{delay_ms // 1000}秒" +# elif delay_ms < 3600000: +# return f"{delay_ms // 60000}分钟" +# else: +# return f"{delay_ms // 3600000}小时" + +# def refresh_text(self): +# self.title.set_text("自动关机") +# self.auto_shutdown.label_left.set_text("自动关机") + +# def on_click(self, event_obj): +# code = event_obj.code +# target = event_obj.get_target() +# if code == lv.EVENT.CLICKED: +# if utils.lcd_resume(): +# return +# if target == self.auto_shutdown: +# # Use the direct auto-shutdown setting screen +# AutoShutDownSetting(self) def get_autolock_delay_str() -> str: + from trezor.strings import format_duration_ms + delay_ms = storage_device.get_autolock_delay_ms() - if delay_ms in (0, storage_device.AUTOLOCK_DELAY_MAXIMUM): - result = _(i18n_keys.OPTION__NEVER) - elif delay_ms < 60000: - seconds = delay_ms // 1000 - result = _(i18n_keys.OPTION__STR_SECONDS).format(seconds) - elif delay_ms < 3600000: - minutes = delay_ms // 60000 - result = _( - i18n_keys.OPTION__STR_MINUTE - if minutes == 1 - else i18n_keys.OPTION__STR_MINUTES - ).format(minutes) - else: - hours = delay_ms // 3600000 - result = _( - i18n_keys.OPTION__STR_HOUR if hours == 1 else i18n_keys.OPTION__STR_HOURS - ).format(hours) - - return result + return format_duration_ms(delay_ms, storage_device.AUTOLOCK_DELAY_MAXIMUM) def get_autoshutdown_delay_str() -> str: - delay_ms = storage_device.get_autoshutdown_delay_ms() - if delay_ms in (0, storage_device.AUTOSHUTDOWN_DELAY_MAXIMUM): - return _(i18n_keys.OPTION__NEVER) - elif delay_ms < 60000: - seconds = delay_ms // 1000 - return _(i18n_keys.OPTION__STR_SECONDS).format(seconds) - elif delay_ms < 3600000: - minutes = delay_ms // 60000 - return _( - i18n_keys.OPTION__STR_MINUTE - if minutes == 1 - else i18n_keys.OPTION__STR_MINUTES - ).format(minutes) - else: - hours = delay_ms // 3600000 - return _( - i18n_keys.OPTION__STR_HOUR if hours == 1 else i18n_keys.OPTION__STR_HOURS - ).format(hours) - - -class Autolock_and_ShutingDown(AnimScreen): - cur_auto_lock = "" - cur_auto_lock_ms = 0 - cur_auto_shutdown = "" - cur_auto_shutdown_ms = 0 - - def collect_animation_targets(self) -> list: - targets = [] - if hasattr(self, "container") and self.container: - targets.append(self.container) - return targets + from trezor.strings import format_duration_ms - def __init__(self, prev_scr=None): - Autolock_and_ShutingDown.cur_auto_lock_ms = ( - storage_device.get_autolock_delay_ms() - ) - Autolock_and_ShutingDown.cur_auto_shutdown_ms = ( - storage_device.get_autoshutdown_delay_ms() - ) - Autolock_and_ShutingDown.cur_auto_lock = ( - Autolock_and_ShutingDown.get_str_from_ms( - Autolock_and_ShutingDown.cur_auto_lock_ms - ) - ) - Autolock_and_ShutingDown.cur_auto_shutdown = ( - Autolock_and_ShutingDown.get_str_from_ms( - Autolock_and_ShutingDown.cur_auto_shutdown_ms - ) - ) - - if not hasattr(self, "_init"): - self._init = True - else: - if self.cur_auto_lock: - self.auto_lock.label_right.set_text( - Autolock_and_ShutingDown.cur_auto_lock - ) - if self.cur_auto_shutdown: - self.auto_shutdown.label_right.set_text( - Autolock_and_ShutingDown.cur_auto_shutdown - ) - self.refresh_text() - return - - super().__init__( - prev_scr=prev_scr, - title=_(i18n_keys.ITEM__AUTO_LOCK_AND_SHUTDOWN), - nav_back=True, - ) - self.container = ContainerFlexCol(self.content_area, self.title, padding_row=2) - self.auto_lock = ListItemBtn( - self.container, _(i18n_keys.ITEM__AUTO_LOCK), self.cur_auto_lock - ) - self.auto_shutdown = ListItemBtn( - self.container, _(i18n_keys.ITEM__SHUTDOWN), self.cur_auto_shutdown - ) - self.container.add_event_cb(self.on_click, lv.EVENT.CLICKED, None) - self.load_screen(self) - gc.collect() - - def refresh_text(self): - self.title.set_text(_(i18n_keys.ITEM__AUTO_LOCK_AND_SHUTDOWN)) - self.auto_lock.label_left.set_text(_(i18n_keys.ITEM__AUTO_LOCK)) - self.auto_shutdown.label_left.set_text(_(i18n_keys.ITEM__SHUTDOWN)) - - @staticmethod - def get_str_from_ms(time_ms) -> str: - - if time_ms == storage_device.AUTOLOCK_DELAY_MAXIMUM: - text = _(i18n_keys.ITEM__STATUS__NEVER) - else: - auto_lock_time = time_ms / 1000 // 60 - if auto_lock_time > 60: - value = str(auto_lock_time // 60).split(".")[0] - text = _( - i18n_keys.OPTION__STR_HOUR - if value == "1" - else i18n_keys.OPTION__STR_HOURS - ).format(value) - elif auto_lock_time < 1: - value = str(time_ms // 1000).split(".")[0] - text = _(i18n_keys.OPTION__STR_SECONDS).format(value) - else: - value = str(auto_lock_time).split(".")[0] - text = _( - i18n_keys.OPTION__STR_MINUTE - if value == "1" - else i18n_keys.OPTION__STR_MINUTES - ).format(value) - return text - - def on_click(self, event_obj): - code = event_obj.code - target = event_obj.get_target() - if code == lv.EVENT.CLICKED: - if utils.lcd_resume(): - return - if target == self.auto_lock: - AutoLockSetting(self) - elif target == self.auto_shutdown: - AutoShutDownSetting(self) - else: - pass + delay_ms = storage_device.get_autoshutdown_delay_ms() + return format_duration_ms(delay_ms, storage_device.AUTOSHUTDOWN_DELAY_MAXIMUM) + + +# class Autolock_and_ShutingDown(AnimScreen): +# cur_auto_lock = "" +# cur_auto_lock_ms = 0 +# cur_auto_shutdown = "" +# cur_auto_shutdown_ms = 0 + +# def collect_animation_targets(self) -> list: +# targets = [] +# if hasattr(self, "container") and self.container: +# targets.append(self.container) +# return targets + +# def __init__(self, prev_scr=None): +# Autolock_and_ShutingDown.cur_auto_lock_ms = ( +# storage_device.get_autolock_delay_ms() +# ) +# Autolock_and_ShutingDown.cur_auto_shutdown_ms = ( +# storage_device.get_autoshutdown_delay_ms() +# ) +# Autolock_and_ShutingDown.cur_auto_lock = ( +# Autolock_and_ShutingDown.get_str_from_ms( +# Autolock_and_ShutingDown.cur_auto_lock_ms +# ) +# ) +# Autolock_and_ShutingDown.cur_auto_shutdown = ( +# Autolock_and_ShutingDown.get_str_from_ms( +# Autolock_and_ShutingDown.cur_auto_shutdown_ms +# ) +# ) + +# if not hasattr(self, "_init"): +# self._init = True +# else: +# if self.cur_auto_lock: +# self.auto_lock.label_right.set_text( +# Autolock_and_ShutingDown.cur_auto_lock +# ) +# if self.cur_auto_shutdown: +# self.auto_shutdown.label_right.set_text( +# Autolock_and_ShutingDown.cur_auto_shutdown +# ) +# self.refresh_text() +# return + +# super().__init__( +# prev_scr=prev_scr, +# title=_(i18n_keys.ITEM__AUTO_LOCK_AND_SHUTDOWN), +# nav_back=True, +# ) +# self.container = ContainerFlexCol(self.content_area, self.title, padding_row=2) +# self.auto_lock = ListItemBtn( +# self.container, _(i18n_keys.ITEM__AUTO_LOCK), self.cur_auto_lock +# ) +# self.auto_shutdown = ListItemBtn( +# self.container, _(i18n_keys.ITEM__SHUTDOWN), self.cur_auto_shutdown +# ) +# self.container.add_event_cb(self.on_click, lv.EVENT.CLICKED, None) +# self.load_screen(self) +# gc.collect() + +# def refresh_text(self): +# self.title.set_text(_(i18n_keys.ITEM__AUTO_LOCK_AND_SHUTDOWN)) +# self.auto_lock.label_left.set_text(_(i18n_keys.ITEM__AUTO_LOCK)) +# self.auto_shutdown.label_left.set_text(_(i18n_keys.ITEM__SHUTDOWN)) + +# @staticmethod +# def get_str_from_ms(time_ms) -> str: + +# if time_ms == storage_device.AUTOLOCK_DELAY_MAXIMUM: +# text = _(i18n_keys.ITEM__STATUS__NEVER) +# else: +# auto_lock_time = time_ms / 1000 // 60 +# if auto_lock_time > 60: +# value = str(auto_lock_time // 60).split(".")[0] +# text = _( +# i18n_keys.OPTION__STR_HOUR +# if value == "1" +# else i18n_keys.OPTION__STR_HOURS +# ).format(value) +# elif auto_lock_time < 1: +# value = str(time_ms // 1000).split(".")[0] +# text = _(i18n_keys.OPTION__STR_SECONDS).format(value) +# else: +# value = str(auto_lock_time).split(".")[0] +# text = _( +# i18n_keys.OPTION__STR_MINUTE +# if value == "1" +# else i18n_keys.OPTION__STR_MINUTES +# ).format(value) +# return text + +# def on_click(self, event_obj): +# code = event_obj.code +# target = event_obj.get_target() +# if code == lv.EVENT.CLICKED: +# if utils.lcd_resume(): +# return +# if target == self.auto_lock: +# AutoLockSetting(self) +# elif target == self.auto_shutdown: +# AutoShutDownSetting(self) +# else: +# pass # pyright: off class AutoLockSetting(AnimScreen): + cur_auto_lock_ms = 0 + cur_auto_lock = "" + def collect_animation_targets(self) -> list: targets = [] if hasattr(self, "container") and self.container: @@ -5262,18 +5231,16 @@ def collect_animation_targets(self) -> list: targets.append(self.tips) return targets - # TODO: i18n def __init__(self, prev_scr=None): # Initialize the class variables first - Autolock_and_ShutingDown.cur_auto_lock_ms = ( - storage_device.get_autolock_delay_ms() - ) - Autolock_and_ShutingDown.cur_auto_lock = ( - Autolock_and_ShutingDown.get_str_from_ms( - Autolock_and_ShutingDown.cur_auto_lock_ms - ) - ) + cur_auto_lock_ms = storage_device.get_autolock_delay_ms() + from trezor.strings import format_duration_ms + cur_auto_lock = format_duration_ms( + cur_auto_lock_ms, storage_device.AUTOLOCK_DELAY_MAXIMUM + ) + AutoLockSetting.cur_auto_lock_ms = cur_auto_lock_ms + AutolockSetting.cur_auto_lock = cur_auto_lock if not hasattr(self, "_init"): self._init = True else: @@ -5308,7 +5275,7 @@ def __init__(self, prev_scr=None): ) self.btns[index].add_check_img() - if item == Autolock_and_ShutingDown.cur_auto_lock: + if item == cur_auto_lock: has_custom = False self.btns[index].set_checked() self.checked_index = index @@ -5317,7 +5284,7 @@ def __init__(self, prev_scr=None): self.custom = storage_device.get_autolock_delay_ms() self.btns[-1] = ListItemBtn( self.container, - f"{Autolock_and_ShutingDown.cur_auto_lock}({_(i18n_keys.OPTION__CUSTOM__INSERT)})", + f"{cur_auto_lock}({_(i18n_keys.OPTION__CUSTOM__INSERT)})", has_next=False, use_transition=False, ) @@ -5356,7 +5323,7 @@ def fresh_tips(self): else: self.tips.set_text( _(i18n_keys.CONTENT__SETTINGS_GENERAL_AUTO_LOCK_ON_HINT).format( - item_text or Autolock_and_ShutingDown.cur_auto_lock[:1] + item_text or AutolockSetting.cur_auto_lock[:1] ) ) @@ -5379,7 +5346,7 @@ def on_click(self, event_obj): else: auto_lock_time = self.setting_items[index] * 60 * 1000 storage_device.set_autolock_delay_ms(int(auto_lock_time)) - Autolock_and_ShutingDown.cur_auto_lock_ms = auto_lock_time + AutolockSetting.cur_auto_lock_ms = auto_lock_time self.fresh_tips() from apps.base import reload_settings_from_storage @@ -5482,11 +5449,12 @@ def __init__(self, prev_scr=None): self.slider.add_style( StyleWrapper().radius(0).bg_color(lv_colors.WHITE), lv.PART.INDICATOR ) - self.percent = lv.label(self.container) + self.percent = lv.label(self.slider) self.percent.add_style( StyleWrapper().text_font(font_GeistRegular30).text_color(lv_colors.BLACK), 0 ) - self.percent.set_text(brightness2_percent_str(self.current_brightness)) + self.percent.set_text(utils.brightness2_percent_str(self.current_brightness)) + self.percent.align(lv.ALIGN.TOP_LEFT, 24, 30) self.slider.add_event_cb(self.on_value_changed, lv.EVENT.VALUE_CHANGED, None) self.slider.clear_flag(lv.obj.FLAG.GESTURE_BUBBLE) self.load_screen(self) @@ -5504,7 +5472,7 @@ def on_value_changed(self, event_obj): value = target.get_value() self.temp_brightness = value display.backlight(value) - self.percent.set_text(brightness2_percent_str(value)) + self.percent.set_text(utils.brightness2_percent_str(value)) def eventhandler(self, event_obj): event = event_obj.code @@ -5643,7 +5611,7 @@ def __init__(self, prev_scr=None): self.description.align_to(self.tap_awake, lv.ALIGN.OUT_BOTTOM_LEFT, 0, 16) # Set keyboard haptic state - if storage_device.keyboard_haptic_enabled(): + if storage_device.haptic_enabled(): self.keyboard.add_state() else: self.keyboard.clear_state() diff --git a/core/src/trezor/lvglui/scrs/template.py b/core/src/trezor/lvglui/scrs/template.py index 4f8f6b72f7..a57914b4ba 100644 --- a/core/src/trezor/lvglui/scrs/template.py +++ b/core/src/trezor/lvglui/scrs/template.py @@ -602,7 +602,7 @@ def on_click(self, event_obj): ) -class TransactionOverview(FullSizeWindow): +class TransactionOverviewBase(FullSizeWindow): def __init__( self, title, @@ -659,23 +659,37 @@ def on_click(self, event_obj): self.channel.publish(2) -class TransactionOverviewSend(TransactionOverview): +class TransactionOverview(TransactionOverviewBase): def __init__( self, title, primary_color, icon_path, address, - has_details=False, + group_header: str | None = None, + group_icon: str | None = None, + use_default_group_item: bool = True, + additional_group_items: tuple[tuple[str, str], ...] = None, + banner_text: str | None = None, + banner_level: int = 2, + has_details: bool = False, + is_send: bool = True, ): + group_items = () + if use_default_group_item: + group_items = ((_(i18n_keys.LIST_KEY__TO__COLON), address),) + if additional_group_items: + group_items += additional_group_items super().__init__( title, primary_color, - "A:/res/icon-send.png", - icon_path or "A:/res/evm-eth.png", - _(i18n_keys.FORM__DIRECTIONS), - "A:/res/group-icon-directions.png", - items=((_(i18n_keys.LIST_KEY__TO__COLON), address),), + "A:/res/icon-send.png" if is_send else icon_path, + (icon_path or "A:/res/evm-eth.png") if is_send else None, + _(i18n_keys.FORM__DIRECTIONS) if group_header is None else group_header, + "A:/res/group-icon-directions.png" if group_icon is None else group_icon, + items=group_items, + banner_text=banner_text, + banner_level=banner_level, has_details=has_details, ) @@ -3409,6 +3423,7 @@ def __init__( associated_token_account: str, wallet_address: str, token_mint: str, + icon_path: str, primary_color, ): super().__init__( @@ -3417,6 +3432,7 @@ def __init__( confirm_text=_(i18n_keys.BUTTON__CONTINUE), cancel_text=_(i18n_keys.BUTTON__REJECT), primary_color=primary_color, + icon_path=icon_path, ) self.container = ContainerFlexCol(self.content_area, self.title, pos=(0, 40)) self.group_directions = ContainerFlexCol( @@ -3463,11 +3479,11 @@ class SolTokenTransfer(FullSizeWindow): def __init__( self, title, - from_addr: str, - to: str, amount: str, + from_ata_addr: str, + to_ata_addr: str, source_owner: str, - fee_payer: str, + destination_owner: str | None, primary_color, icon_path, token_mint: str | None = None, @@ -3483,21 +3499,19 @@ def __init__( sub_icon_path=icon_path, ) - if token_mint: - self.banner = Banner( - self.content_area, - level=LEVEL.WARNING, - text=_(i18n_keys.WARNING_UNRECOGNIZED_TOKEN), - ) - self.banner.align_to(self.title, lv.ALIGN.OUT_BOTTOM_MID, 0, 30) - self.container = ContainerFlexCol( - self.content_area, self.banner, pos=(0, 8), padding_row=8 - ) + # if token_mint: + # self.banner = Banner( + # self.content_area, + # level=LEVEL.WARNING, + # text=_(i18n_keys.WARNING_UNRECOGNIZED_TOKEN), + # ) + # self.banner.align_to(self.title, lv.ALIGN.OUT_BOTTOM_MID, 0, 30) + # self.container = ContainerFlexCol( + # self.content_area, self.banner, pos=(0, 8), padding_row=8 + # ) - else: - self.container = ContainerFlexCol( - self.content_area, self.title, pos=(0, 40) - ) + # else: + self.container = ContainerFlexCol(self.content_area, self.title, pos=(0, 40)) if striped: self.group_amounts = ContainerFlexCol( @@ -3525,13 +3539,17 @@ def __init__( ) self.item_group_body_to_ata_addr = DisplayItem( self.group_directions, - _(i18n_keys.LIST_KEY__TO_TOKEN_ACCOUNT__COLON), - to, + _(i18n_keys.LIST_KEY__TO_TOKEN_ACCOUNT__COLON) + if destination_owner is None + else _(i18n_keys.LIST_KEY__TO__COLON), + to_ata_addr if destination_owner is None else destination_owner, ) self.item_group_body_from_ata_addr = DisplayItem( self.group_directions, - _(i18n_keys.LIST_KEY__FROM_TOKEN_ACCOUNT__COLON), - from_addr, + _(i18n_keys.LIST_KEY__FROM_TOKEN_ACCOUNT__COLON) + if destination_owner is None + else _(i18n_keys.LIST_KEY__FROM__COLON), + from_ata_addr if destination_owner is None else source_owner, ) self.group_directions.add_dummy() @@ -3542,7 +3560,7 @@ def __init__( self.group_fees, _(i18n_keys.FORM__FEES), "A:/res/group-icon-fees.png" ) self.item_group_body_fee_payer = DisplayItem( - self.group_fees, _(i18n_keys.LIST_KEY__FEE_PAYER__COLON), fee_payer + self.group_fees, _(i18n_keys.LIST_KEY__FEE_PAYER__COLON), source_owner ) self.group_fees.add_dummy() @@ -3552,13 +3570,25 @@ def __init__( self.item_group_header = CardHeader( self.group_more, _(i18n_keys.FORM__MORE), "A:/res/group-icon-more.png" ) - self.item_group_body_signer = DisplayItem( - self.group_more, _(i18n_keys.LIST_KEY__SIGNER__COLON), source_owner - ) if token_mint: self.item_group_body_mint_addr = DisplayItem( self.group_more, _(i18n_keys.LIST_KEY__MINT_ADDRESS), token_mint ) + if destination_owner is None: + self.item_group_body_signer = DisplayItem( + self.group_more, _(i18n_keys.LIST_KEY__SIGNER__COLON), source_owner + ) + else: + self.item_group_body_to_ata_addr = DisplayItem( + self.group_more, + _(i18n_keys.LIST_KEY__TO_TOKEN_ACCOUNT__COLON), + to_ata_addr, + ) + self.item_group_body_from_ata_addr = DisplayItem( + self.group_more, + _(i18n_keys.LIST_KEY__FROM_TOKEN_ACCOUNT__COLON), + from_ata_addr, + ) self.group_more.add_dummy() @@ -6065,3 +6095,74 @@ def on_nav_back(self, event_obj): _dir = lv.indev_get_act().get_gesture_dir() if _dir == lv.DIR.RIGHT: lv.event_send(self.nav_back.nav_btn, lv.EVENT.CLICKED, None) + + +class BrightnessControl(FullSizeWindow): + def __init__(self): + super().__init__( + _(i18n_keys.TITLE__SET_BRIGHTNESS), + None, + _(i18n_keys.BUTTON__APPLY), + _(i18n_keys.BUTTON__CANCEL), + ) + from trezor.ui import style + import storage.device as storage_device + + current_brightness = storage_device.get_brightness() + slider = lv.slider(self.content_area) + slider.align_to(self.title, lv.ALIGN.OUT_BOTTOM_LEFT, 0, 40) + slider.set_size(456, 94) + slider.set_ext_click_area(100) + slider.set_range(style.BACKLIGHT_MIN, style.BACKLIGHT_MAX) + slider.set_value(current_brightness, lv.ANIM.OFF) + slider.add_style( + StyleWrapper().border_width(0).radius(40).bg_color(lv_colors.GRAY_1), 0 + ) + slider.add_style( + StyleWrapper().bg_color(lv_colors.WHITE).pad_all(-50), lv.PART.KNOB + ) + slider.add_style( + StyleWrapper().radius(0).bg_color(lv_colors.WHITE), lv.PART.INDICATOR + ) + percent = lv.label(slider) + percent.add_style( + StyleWrapper().text_font(font_GeistRegular30).text_color(lv_colors.BLACK), 0 + ) + percent.set_text(utils.brightness2_percent_str(current_brightness)) + percent.align(lv.ALIGN.TOP_LEFT, 24, 30) + slider.add_event_cb(self.on_value_changed, lv.EVENT.VALUE_CHANGED, None) + slider.clear_flag(lv.obj.FLAG.GESTURE_BUBBLE) + self.slider = slider + self.percent = percent + self.current_brightness = current_brightness + self.brightness_backup = current_brightness + + def on_value_changed(self, event_obj): + target = event_obj.get_target() + if target == self.slider: + from trezor.ui import display + + value = target.get_value() + self.current_brightness = value + display.backlight(value) + self.percent.set_text(utils.brightness2_percent_str(value)) + + def destroy(self, _delay_ms=10): + return self.delete() + + def eventhandler(self, event_obj): + code = event_obj.code + target = event_obj.get_target() + if code == lv.EVENT.CLICKED: + if target == self.btn_no: + from trezor.ui import display + + self.show_dismiss_anim() + display.backlight(self.brightness_backup) + self.channel.publish(0) + elif target == self.btn_yes: + import storage.device as storage_device + + self.show_unload_anim() + self.channel.publish(1) + storage_device.set_brightness(self.current_brightness) diff --git a/core/src/trezor/messages.py b/core/src/trezor/messages.py index 5232efa6f8..5c8fab0541 100644 --- a/core/src/trezor/messages.py +++ b/core/src/trezor/messages.py @@ -2994,6 +2994,7 @@ class Features(protobuf.MessageType): auto_lock_delay_ms: "int | None" display_rotation: "int | None" experimental_features: "bool | None" + busy: "bool | None" offset: "int | None" ble_name: "str | None" ble_ver: "str | None" @@ -3013,7 +3014,9 @@ class Features(protobuf.MessageType): coin_switch: "int | None" build_id: "bytes | None" boardloader_version: "str | None" - busy: "bool | None" + brightness_percent: "int | None" + haptic_feedback: "bool | None" + auto_shutdown_delay_ms: "int | None" onekey_device_type: "OneKeyDeviceType | None" onekey_se_type: "OneKeySeType | None" onekey_board_version: "str | None" @@ -3082,6 +3085,7 @@ def __init__( auto_lock_delay_ms: "int | None" = None, display_rotation: "int | None" = None, experimental_features: "bool | None" = None, + busy: "bool | None" = None, offset: "int | None" = None, ble_name: "str | None" = None, ble_ver: "str | None" = None, @@ -3101,7 +3105,9 @@ def __init__( coin_switch: "int | None" = None, build_id: "bytes | None" = None, boardloader_version: "str | None" = None, - busy: "bool | None" = None, + brightness_percent: "int | None" = None, + haptic_feedback: "bool | None" = None, + auto_shutdown_delay_ms: "int | None" = None, onekey_device_type: "OneKeyDeviceType | None" = None, onekey_se_type: "OneKeySeType | None" = None, onekey_board_version: "str | None" = None, @@ -3272,6 +3278,9 @@ class ApplySettings(protobuf.MessageType): passphrase_always_on_device: "bool | None" safety_checks: "SafetyCheckLevel | None" experimental_features: "bool | None" + auto_shutdown_delay_ms: "int | None" + change_brightness: "bool | None" + haptic_feedback: "bool | None" def __init__( self, @@ -3285,6 +3294,9 @@ def __init__( passphrase_always_on_device: "bool | None" = None, safety_checks: "SafetyCheckLevel | None" = None, experimental_features: "bool | None" = None, + auto_shutdown_delay_ms: "int | None" = None, + change_brightness: "bool | None" = None, + haptic_feedback: "bool | None" = None, ) -> None: pass @@ -8136,15 +8148,51 @@ def __init__( def is_type_of(cls, msg: Any) -> TypeGuard["SolanaAddress"]: return isinstance(msg, cls) + class SolanaTxATADetails(protobuf.MessageType): + owner_address: "str" + program_id: "str" + mint_address: "str" + associated_token_address: "str" + + def __init__( + self, + *, + owner_address: "str", + program_id: "str", + mint_address: "str", + associated_token_address: "str", + ) -> None: + pass + + @classmethod + def is_type_of(cls, msg: Any) -> TypeGuard["SolanaTxATADetails"]: + return isinstance(msg, cls) + + class SolanaTxExtraInfo(protobuf.MessageType): + ata_details: "list[SolanaTxATADetails]" + + def __init__( + self, + *, + ata_details: "list[SolanaTxATADetails] | None" = None, + ) -> None: + pass + + @classmethod + def is_type_of(cls, msg: Any) -> TypeGuard["SolanaTxExtraInfo"]: + return isinstance(msg, cls) + class SolanaSignTx(protobuf.MessageType): address_n: "list[int]" raw_tx: "bytes" + extra_info: "SolanaTxExtraInfo | None" def __init__( self, *, raw_tx: "bytes", address_n: "list[int] | None" = None, + extra_info: "SolanaTxExtraInfo | None" = None, ) -> None: pass diff --git a/core/src/trezor/motor.py b/core/src/trezor/motor.py index 0976828002..1a9befead4 100644 --- a/core/src/trezor/motor.py +++ b/core/src/trezor/motor.py @@ -24,7 +24,7 @@ def vibrate( style: VIBRATE_STYLE = LIGHT, force: bool = False ) -> VIBRATE_STYLE | None: - if not storage_device.keyboard_haptic_enabled() and not force: + if not storage_device.haptic_enabled() and not force: return None if style == WHISPER: MOTOR_CTL.play_whisper() diff --git a/core/src/trezor/strings.py b/core/src/trezor/strings.py index e55fad95f2..77dc38432a 100644 --- a/core/src/trezor/strings.py +++ b/core/src/trezor/strings.py @@ -73,23 +73,30 @@ def format_plural(string: str, count: int, plural: str) -> str: return string.format(count=count, plural=plural) -def format_duration_ms(milliseconds: int) -> str: +def format_duration_ms(milliseconds: int, maximum_ms: int | None) -> str: """ Returns human-friendly representation of a duration. Truncates all decimals. """ - units = ( - ("hour", 60 * 60 * 1000), - ("minute", 60 * 1000), - ("second", 1000), - ) - for unit, divisor in units: - if milliseconds >= divisor: - break + from trezor.lvglui.i18n import gettext as _, keys as i18n_keys + + if maximum_ms and milliseconds == maximum_ms: + return _(i18n_keys.OPTION__NEVER) + if milliseconds < 60000: + seconds = milliseconds // 1000 + formated = _(i18n_keys.OPTION__STR_SECONDS).format(seconds) + elif milliseconds < 3600000: + minutes = milliseconds // 60000 + formated = _( + i18n_keys.OPTION__STR_MINUTE + if minutes == 1 + else i18n_keys.OPTION__STR_MINUTES + ).format(minutes) else: - unit = "millisecond" - divisor = 1 - - return format_plural("{count} {plural}", milliseconds // divisor, unit) + hours = milliseconds // 3600000 + formated = _( + i18n_keys.OPTION__STR_HOUR if hours == 1 else i18n_keys.OPTION__STR_HOURS + ).format(hours) + return formated def format_timestamp(timestamp: int) -> str: @@ -117,8 +124,10 @@ def format_customer_data(data: bytes | None) -> str: return "" try: formatted = data.decode() - if all((c <= 0x20 or c == 0x7F) for c in data[:33]): - raise UnicodeError # non-printable characters + if all((c in (0x20, 0x0A, 0x0D)) for c in formatted[:10]): + raise UnicodeError # whitespace only + elif any((ord(c) < 0x20 or ord(c) == 0x7F) for c in formatted[:10]): + raise UnicodeError # contains non-printable characters except UnicodeError: # mp has no UnicodeDecodeError from binascii import hexlify diff --git a/core/src/trezor/ui/layouts/lvgl/__init__.py b/core/src/trezor/ui/layouts/lvgl/__init__.py index c0b11dc509..baecbfef11 100644 --- a/core/src/trezor/ui/layouts/lvgl/__init__.py +++ b/core/src/trezor/ui/layouts/lvgl/__init__.py @@ -92,6 +92,7 @@ "confirm_safe_exec_transaction", "should_show_details_eip7702", "request_pin_onboarding", + "request_change_brightness", ) @@ -622,13 +623,13 @@ async def confirm_output( br_code: ButtonRequestType = ButtonRequestType.ConfirmOutput, icon: str = ui.ICON_SEND, ) -> None: - from trezor.lvglui.scrs.template import TransactionOverviewSend + from trezor.lvglui.scrs.template import TransactionOverview from trezor.strings import strip_amount await raise_if_cancelled( interact( ctx, - TransactionOverviewSend( + TransactionOverview( _(i18n_keys.TITLE__SEND_MULTILINE).format(strip_amount(amount)[0]), primary_color=ctx.primary_color, icon_path=ctx.icon_path, @@ -645,17 +646,31 @@ async def should_show_details( address: str, title: str, br_code: ButtonRequestType = ButtonRequestType.ConfirmOutput, + group_header: str | None = None, + group_icon: str | None = None, + use_default_group_item: bool = True, + additional_group_items: tuple[tuple[str, str], ...] | None = None, + banner_text: str | None = None, + banner_level: int = 2, + is_send: bool = True, ) -> bool: - from trezor.lvglui.scrs.template import TransactionOverviewSend + from trezor.lvglui.scrs.template import TransactionOverview res = await interact( ctx, - TransactionOverviewSend( + TransactionOverview( title, - primary_color=ctx.primary_color, - icon_path=ctx.icon_path, + ctx.primary_color, + ctx.icon_path, + address, + group_header=group_header, + group_icon=group_icon, + use_default_group_item=use_default_group_item, + additional_group_items=additional_group_items, + banner_text=banner_text, + banner_level=banner_level, has_details=True, - address=address, + is_send=is_send, ), "confirm_output", br_code, @@ -1460,7 +1475,7 @@ async def confirm_sol_transfer( striped_amount, striped = strip_amount(amount) title = _(i18n_keys.TITLE__SEND_MULTILINE).format(striped_amount) - if should_show_details(ctx, to_addr, title): + if await should_show_details(ctx, to_addr, title): from trezor.lvglui.scrs.template import SolTransfer screen = SolTransfer( @@ -1504,44 +1519,73 @@ async def confirm_sol_create_ata( wallet_address: str, token_mint: str, ): - from trezor.lvglui.scrs.template import SolCreateAssociatedTokenAccount - - screen = SolCreateAssociatedTokenAccount( - fee_payer, - funding_account, - associated_token_account, - wallet_address, - token_mint, - primary_color=ctx.primary_color, - ) - await raise_if_cancelled( - interact(ctx, screen, "sol_create_ata", ButtonRequestType.ProtectCall) - ) + if await should_show_details( + ctx, + None, + _(i18n_keys.TITLE__CREATE_TOKEN_ACCOUNT), + br_code=ButtonRequestType.ProtectCall, + group_header=_(i18n_keys.OVERVIEW), + group_icon="A:/res/group-icon-directions.png", + use_default_group_item=False, + additional_group_items=( + (_(i18n_keys.LIST_KEY__NEW_TOKEN_ACCOUNT), associated_token_account), + (_(i18n_keys.LIST_KEY__OWNER), wallet_address), + ), + is_send=False, + ): + from trezor.lvglui.scrs.template import SolCreateAssociatedTokenAccount + + screen = SolCreateAssociatedTokenAccount( + fee_payer, + funding_account, + associated_token_account, + wallet_address, + token_mint, + primary_color=ctx.primary_color, + icon_path=ctx.icon_path, + ) + await raise_if_cancelled( + interact(ctx, screen, "sol_create_ata", ButtonRequestType.ProtectCall) + ) async def confirm_sol_token_transfer( ctx: wire.GenericContext, - from_addr: str, - to_addr: str, amount: str, + from_ata_addr: str, + to_ata_addr: str, source_owner: str, - fee_payer: str, - token_mint: str = None, + destination_owner: str | None = None, + token_mint: str | None = None, ): from trezor.strings import strip_amount striped_amount, striped = strip_amount(amount) title = _(i18n_keys.TITLE__SEND_MULTILINE).format(striped_amount) - if should_show_details(ctx, to_addr, title): + additional_group_items = () + if destination_owner is None: + additional_group_items += ( + (_(i18n_keys.LIST_KEY__TO_TOKEN_ACCOUNT__COLON), to_ata_addr), + ) + if token_mint: + additional_group_items = ((_(i18n_keys.LIST_KEY__MINT_ADDRESS), token_mint),) + if await should_show_details( + ctx, + destination_owner, + title, + use_default_group_item=True if destination_owner else False, + additional_group_items=additional_group_items or None, + banner_text=_(i18n_keys.WARNING_UNRECOGNIZED_TOKEN) if token_mint else None, + ): from trezor.lvglui.scrs.template import SolTokenTransfer screen = SolTokenTransfer( title, - from_addr, - to_addr, amount, + from_ata_addr, + to_ata_addr, source_owner, - fee_payer, + destination_owner, primary_color=ctx.primary_color, icon_path=ctx.icon_path, token_mint=token_mint, @@ -2836,3 +2880,17 @@ async def confirm_safe_exec_transaction( ctx, screen, "confirm_safe_exec_transaction", ButtonRequestType.ProtectCall ) ) + + +async def request_change_brightness(ctx: wire.GenericContext) -> None: + + from trezor.lvglui.scrs.template import BrightnessControl + + await raise_if_cancelled( + interact( + ctx, + BrightnessControl(), + "request_change_brightness", + ButtonRequestType.ProtectCall, + ) + ) diff --git a/core/src/trezor/utils.py b/core/src/trezor/utils.py index ff3d8b3f51..255a077cd2 100644 --- a/core/src/trezor/utils.py +++ b/core/src/trezor/utils.py @@ -386,6 +386,12 @@ def get_default_wallpaper(): return "A:/res/wallpaper-7.jpg" +def brightness2_percent_str(brightness: int) -> str: + from trezor.ui import style + + return f"{int(brightness / style.BACKLIGHT_MAX * 100)}%" + + def unimport_begin() -> set[str]: return set(sys.modules) diff --git a/core/src/trezor/wire/__init__.py b/core/src/trezor/wire/__init__.py index f6f23c9ea2..8c974e79f3 100644 --- a/core/src/trezor/wire/__init__.py +++ b/core/src/trezor/wire/__init__.py @@ -241,6 +241,7 @@ def __init__(self, iface: WireInterface, sid: int, buffer: bytearray) -> None: self.primary_color = None self.icon_path = "" self.name = "" + self.extra: Any | None = None async def _call( self, diff --git a/python/requirements.txt b/python/requirements.txt index 7db8ac10d7..9ea8fb62b3 100644 --- a/python/requirements.txt +++ b/python/requirements.txt @@ -4,6 +4,6 @@ requests>=2.4.0 click>=7,<8.2 libusb1>=1.6.4 construct>=2.9,!=2.10.55 -typing_extensions>=3.10 +typing_extensions>=4.12.2 dataclasses ; python_version<'3.7' rlp>=1.1.0 ; python_version<'3.7' diff --git a/python/src/trezorlib/cli/settings.py b/python/src/trezorlib/cli/settings.py index a9e94a938e..a85df11602 100644 --- a/python/src/trezorlib/cli/settings.py +++ b/python/src/trezorlib/cli/settings.py @@ -286,3 +286,46 @@ def passphrase_off(client: "TrezorClient") -> str: "enabled": passphrase_on, "disabled": passphrase_off, } + +@cli.command() +@click.argument("delay", type=str) +@with_client +def auto_shutdown_delay(client: "TrezorClient", delay: str) -> str: + """Set auto-shutdown delay (in seconds).""" + + if not client.features.pin_protection: + raise click.ClickException("Set up a PIN first") + + value, unit = delay[:-1], delay[-1:] + units = {"s": 1, "m": 60, "h": 3600} + if unit in units: + seconds = float(value) * units[unit] + else: + seconds = float(delay) # assume seconds if no unit is specified + return device.apply_settings(client, auto_shutdown_delay_ms=int(seconds * 1000)) + + +@cli.command() +@click.argument("lang_code", type=str) +@with_client +def language(client: "TrezorClient", lang_code: str) -> str: + """Set Language by a ISO_639-1 language key.""" + if lang_code == "en-US": + lang_code = "en" + return device.apply_settings(client, language=lang_code) + + +@cli.command() +@with_client +def brightness(client: "TrezorClient") -> str: + """Request change brightness.""" + return device.apply_settings(client, change_brightness=True) + + +@cli.command() +@click.argument("enable", type=ChoiceType({"on": True, "off": False})) +@with_client +def haptic_feedback(client: "TrezorClient", enable: bool) -> str: + """Enable or disable Haptic feedback. + """ + return device.apply_settings(client, haptic_feedback=enable) diff --git a/python/src/trezorlib/cli/sol.py b/python/src/trezorlib/cli/sol.py index 2b21b35756..3bb40a7c6d 100644 --- a/python/src/trezorlib/cli/sol.py +++ b/python/src/trezorlib/cli/sol.py @@ -17,7 +17,7 @@ from typing import TYPE_CHECKING -from typing import Optional +from typing import Optional, List, Any, Tuple import base58 import click @@ -40,6 +40,30 @@ "utf8": messages.SolanaOffChainMessageFormat.V0_LIMITED_UTF8, } + +def _parse_ata_details_list( + ctx: click.Context, param: Any, value: Tuple[str, ...] +) -> List[messages.SolanaTxATADetails]: + return [_parse_ata_details_list_item(val) for val in value] + + +def _parse_ata_details_list_item(value: str) -> messages.SolanaTxATADetails: + try: + arr = value.split(":") + owner_address, program_id, mint_address, associated_token_address = arr + assert owner_address, "owner_address is required" + assert program_id, "program_id is requierd" + assert mint_address, "mint_address is required" + return messages.SolanaTxATADetails( + owner_address=owner_address, + program_id=program_id, + mint_address=mint_address, + associated_token_address=associated_token_address, + ) + except Exception: + raise click.BadParameter("Associated token account details format is invlaid") + + @click.group(name="sol") def cli(): """Solana commands.""" @@ -58,13 +82,31 @@ def get_address(client: "TrezorClient", address: str, show_display: bool) -> str @cli.command() @click.option("-n", "--address", required=True, help=PATH_HELP) @click.option("-d", "--raw_tx", required=True, help=PATH_RAW_TX) +@click.option( + "-e", + "--ata_deatils_list", + help="ATA deatils List", + callback=_parse_ata_details_list, + multiple=True, +) @with_client -def sign_tx(client: "TrezorClient", address: str, raw_tx: str) -> str: +def sign_tx( + client: "TrezorClient", + address: str, + raw_tx: str, + ata_deatils_list: List[messages.SolanaTxATADetails], +) -> str: """Sign Solala transaction.""" address_n = tools.parse_path(address) - transaction = solana.sign_tx(client, address_n, base58.b58decode(raw_tx)) + transaction = solana.sign_tx( + client, + address_n, + base58.b58decode(raw_tx), + messages.SolanaTxExtraInfo(ata_details=ata_deatils_list), + ) return transaction.signature.hex() + @cli.command() @click.option("-n", "--address", required=True, help=PATH_HELP) @click.option("-v", "--message-version", type=ChoiceType(MESSAGE_VERSIONS), default="v0") diff --git a/python/src/trezorlib/device.py b/python/src/trezorlib/device.py index d0abf372af..dc0084e8be 100644 --- a/python/src/trezorlib/device.py +++ b/python/src/trezorlib/device.py @@ -44,6 +44,9 @@ def apply_settings( display_rotation: Optional[int] = None, safety_checks: Optional[messages.SafetyCheckLevel] = None, experimental_features: Optional[bool] = None, + auto_shutdown_delay_ms: Optional[int] = None, + haptic_feedback: Optional[bool] = None, + change_brightness: Optional[bool] = None, ) -> "MessageType": settings = messages.ApplySettings( label=label, @@ -55,6 +58,9 @@ def apply_settings( display_rotation=display_rotation, safety_checks=safety_checks, experimental_features=experimental_features, + auto_shutdown_delay_ms=auto_shutdown_delay_ms, + haptic_feedback=haptic_feedback, + change_brightness=change_brightness, ) out = client.call(settings) diff --git a/python/src/trezorlib/messages.py b/python/src/trezorlib/messages.py index 1d2478828a..593c4f5808 100644 --- a/python/src/trezorlib/messages.py +++ b/python/src/trezorlib/messages.py @@ -4433,6 +4433,7 @@ class Features(protobuf.MessageType): 38: protobuf.Field("auto_lock_delay_ms", "uint32", repeated=False, required=False), 39: protobuf.Field("display_rotation", "uint32", repeated=False, required=False), 40: protobuf.Field("experimental_features", "bool", repeated=False, required=False), + 41: protobuf.Field("busy", "bool", repeated=False, required=False), 500: protobuf.Field("offset", "uint32", repeated=False, required=False), 501: protobuf.Field("ble_name", "string", repeated=False, required=False), 502: protobuf.Field("ble_ver", "string", repeated=False, required=False), @@ -4452,7 +4453,9 @@ class Features(protobuf.MessageType): 517: protobuf.Field("coin_switch", "uint32", repeated=False, required=False), 518: protobuf.Field("build_id", "bytes", repeated=False, required=False), 519: protobuf.Field("boardloader_version", "string", repeated=False, required=False), - 41: protobuf.Field("busy", "bool", repeated=False, required=False), + 522: protobuf.Field("brightness_percent", "uint32", repeated=False, required=False), + 523: protobuf.Field("haptic_feedback", "bool", repeated=False, required=False), + 524: protobuf.Field("auto_shutdown_delay_ms", "uint32", repeated=False, required=False), 600: protobuf.Field("onekey_device_type", "OneKeyDeviceType", repeated=False, required=False), 601: protobuf.Field("onekey_se_type", "OneKeySeType", repeated=False, required=False), 602: protobuf.Field("onekey_board_version", "string", repeated=False, required=False), @@ -4523,6 +4526,7 @@ def __init__( auto_lock_delay_ms: Optional["int"] = None, display_rotation: Optional["int"] = None, experimental_features: Optional["bool"] = None, + busy: Optional["bool"] = None, offset: Optional["int"] = None, ble_name: Optional["str"] = None, ble_ver: Optional["str"] = None, @@ -4542,7 +4546,9 @@ def __init__( coin_switch: Optional["int"] = None, build_id: Optional["bytes"] = None, boardloader_version: Optional["str"] = None, - busy: Optional["bool"] = None, + brightness_percent: Optional["int"] = None, + haptic_feedback: Optional["bool"] = None, + auto_shutdown_delay_ms: Optional["int"] = None, onekey_device_type: Optional["OneKeyDeviceType"] = None, onekey_se_type: Optional["OneKeySeType"] = None, onekey_board_version: Optional["str"] = None, @@ -4609,6 +4615,7 @@ def __init__( self.auto_lock_delay_ms = auto_lock_delay_ms self.display_rotation = display_rotation self.experimental_features = experimental_features + self.busy = busy self.offset = offset self.ble_name = ble_name self.ble_ver = ble_ver @@ -4628,7 +4635,9 @@ def __init__( self.coin_switch = coin_switch self.build_id = build_id self.boardloader_version = boardloader_version - self.busy = busy + self.brightness_percent = brightness_percent + self.haptic_feedback = haptic_feedback + self.auto_shutdown_delay_ms = auto_shutdown_delay_ms self.onekey_device_type = onekey_device_type self.onekey_se_type = onekey_se_type self.onekey_board_version = onekey_board_version @@ -4836,6 +4845,9 @@ class ApplySettings(protobuf.MessageType): 8: protobuf.Field("passphrase_always_on_device", "bool", repeated=False, required=False), 9: protobuf.Field("safety_checks", "SafetyCheckLevel", repeated=False, required=False), 10: protobuf.Field("experimental_features", "bool", repeated=False, required=False), + 500: protobuf.Field("auto_shutdown_delay_ms", "uint32", repeated=False, required=False), + 501: protobuf.Field("change_brightness", "bool", repeated=False, required=False), + 502: protobuf.Field("haptic_feedback", "bool", repeated=False, required=False), } def __init__( @@ -4851,6 +4863,9 @@ def __init__( passphrase_always_on_device: Optional["bool"] = None, safety_checks: Optional["SafetyCheckLevel"] = None, experimental_features: Optional["bool"] = None, + auto_shutdown_delay_ms: Optional["int"] = None, + change_brightness: Optional["bool"] = None, + haptic_feedback: Optional["bool"] = None, ) -> None: self.language = language self.label = label @@ -4862,6 +4877,9 @@ def __init__( self.passphrase_always_on_device = passphrase_always_on_device self.safety_checks = safety_checks self.experimental_features = experimental_features + self.auto_shutdown_delay_ms = auto_shutdown_delay_ms + self.change_brightness = change_brightness + self.haptic_feedback = haptic_feedback class ApplyFlags(protobuf.MessageType): @@ -10209,11 +10227,49 @@ def __init__( self.address = address +class SolanaTxATADetails(protobuf.MessageType): + MESSAGE_WIRE_TYPE = None + FIELDS = { + 1: protobuf.Field("owner_address", "string", repeated=False, required=True), + 2: protobuf.Field("program_id", "string", repeated=False, required=True), + 3: protobuf.Field("mint_address", "string", repeated=False, required=True), + 4: protobuf.Field("associated_token_address", "string", repeated=False, required=True), + } + + def __init__( + self, + *, + owner_address: "str", + program_id: "str", + mint_address: "str", + associated_token_address: "str", + ) -> None: + self.owner_address = owner_address + self.program_id = program_id + self.mint_address = mint_address + self.associated_token_address = associated_token_address + + +class SolanaTxExtraInfo(protobuf.MessageType): + MESSAGE_WIRE_TYPE = None + FIELDS = { + 1: protobuf.Field("ata_details", "SolanaTxATADetails", repeated=True, required=False), + } + + def __init__( + self, + *, + ata_details: Optional[Sequence["SolanaTxATADetails"]] = None, + ) -> None: + self.ata_details: Sequence["SolanaTxATADetails"] = ata_details if ata_details is not None else [] + + class SolanaSignTx(protobuf.MessageType): MESSAGE_WIRE_TYPE = 10102 FIELDS = { 1: protobuf.Field("address_n", "uint32", repeated=True, required=False), 2: protobuf.Field("raw_tx", "bytes", repeated=False, required=True), + 3: protobuf.Field("extra_info", "SolanaTxExtraInfo", repeated=False, required=False), } def __init__( @@ -10221,9 +10277,11 @@ def __init__( *, raw_tx: "bytes", address_n: Optional[Sequence["int"]] = None, + extra_info: Optional["SolanaTxExtraInfo"] = None, ) -> None: self.address_n: Sequence["int"] = address_n if address_n is not None else [] self.raw_tx = raw_tx + self.extra_info = extra_info class SolanaSignedTx(protobuf.MessageType): diff --git a/python/src/trezorlib/solana.py b/python/src/trezorlib/solana.py index 9224f5c509..ca38eac77e 100644 --- a/python/src/trezorlib/solana.py +++ b/python/src/trezorlib/solana.py @@ -22,13 +22,16 @@ def sign_tx( client: "TrezorClient", n: "Address", # fee_payer raw_tx: bytes, + extra_info: Optional[messages.SolanaTxExtraInfo], ): msg = messages.SolanaSignTx( raw_tx=raw_tx, address_n=n, + extra_info=extra_info, ) return client.call(msg) + @expect(messages.SolanaMessageSignature) def sign_offchain_message( client: "TrezorClient",