You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
989 lines
29 KiB
989 lines
29 KiB
from typing import TYPE_CHECKING
|
|
|
|
from trezor import TR, ui
|
|
from trezor.enums import (
|
|
ButtonRequestType,
|
|
CardanoAddressType,
|
|
CardanoCertificateType,
|
|
CardanoNativeScriptType,
|
|
)
|
|
from trezor.strings import format_amount
|
|
from trezor.ui import layouts
|
|
from trezor.ui.layouts import confirm_metadata, confirm_properties
|
|
|
|
from apps.common.paths import address_n_to_str
|
|
|
|
from . import addresses
|
|
from .helpers import bech32, protocol_magics
|
|
from .helpers.utils import (
|
|
format_account_number,
|
|
format_asset_fingerprint,
|
|
format_optional_int,
|
|
format_stake_pool_id,
|
|
)
|
|
|
|
if TYPE_CHECKING:
|
|
from typing import Literal
|
|
|
|
from trezor import messages
|
|
from trezor.enums import CardanoNativeScriptHashDisplayFormat
|
|
from trezor.ui.layouts import PropertyType
|
|
|
|
from .helpers.credential import Credential
|
|
from .seed import Keychain
|
|
|
|
|
|
ADDRESS_TYPE_NAMES = {
|
|
CardanoAddressType.BYRON: TR.cardano__addr_legacy,
|
|
CardanoAddressType.BASE: TR.cardano__addr_base,
|
|
CardanoAddressType.BASE_SCRIPT_KEY: TR.cardano__addr_base,
|
|
CardanoAddressType.BASE_KEY_SCRIPT: TR.cardano__addr_base,
|
|
CardanoAddressType.BASE_SCRIPT_SCRIPT: TR.cardano__addr_base,
|
|
CardanoAddressType.POINTER: TR.cardano__addr_pointer,
|
|
CardanoAddressType.POINTER_SCRIPT: TR.cardano__addr_pointer,
|
|
CardanoAddressType.ENTERPRISE: TR.cardano__addr_enterprise,
|
|
CardanoAddressType.ENTERPRISE_SCRIPT: TR.cardano__addr_enterprise,
|
|
CardanoAddressType.REWARD: TR.cardano__addr_reward,
|
|
CardanoAddressType.REWARD_SCRIPT: TR.cardano__addr_reward,
|
|
}
|
|
|
|
SCRIPT_TYPE_NAMES = {
|
|
CardanoNativeScriptType.PUB_KEY: TR.cardano__script_key,
|
|
CardanoNativeScriptType.ALL: TR.cardano__script_all,
|
|
CardanoNativeScriptType.ANY: TR.cardano__script_any,
|
|
CardanoNativeScriptType.N_OF_K: TR.cardano__script_n_of_k,
|
|
CardanoNativeScriptType.INVALID_BEFORE: TR.cardano__script_invalid_before,
|
|
CardanoNativeScriptType.INVALID_HEREAFTER: TR.cardano__script_invalid_hereafter,
|
|
}
|
|
|
|
CERTIFICATE_TYPE_NAMES = {
|
|
CardanoCertificateType.STAKE_REGISTRATION: TR.cardano__stake_registration,
|
|
CardanoCertificateType.STAKE_DEREGISTRATION: TR.cardano__stake_deregistration,
|
|
CardanoCertificateType.STAKE_DELEGATION: TR.cardano__stake_delegation,
|
|
CardanoCertificateType.STAKE_POOL_REGISTRATION: TR.cardano__stake_pool_registration,
|
|
}
|
|
|
|
BRT_Other = ButtonRequestType.Other # global_import_cache
|
|
|
|
CVOTE_REWARD_ELIGIBILITY_WARNING = TR.cardano__reward_eligibility_warning
|
|
|
|
|
|
def format_coin_amount(amount: int, network_id: int) -> str:
|
|
from .helpers import network_ids
|
|
|
|
currency = "ADA" if network_ids.is_mainnet(network_id) else "tADA"
|
|
return f"{format_amount(amount, 6)} {currency}"
|
|
|
|
|
|
async def show_native_script(
|
|
script: messages.CardanoNativeScript,
|
|
indices: list[int] | None = None,
|
|
) -> None:
|
|
CNST = CardanoNativeScriptType # local_cache_global
|
|
script_type = script.type # local_cache_attribute
|
|
key_path = script.key_path # local_cache_attribute
|
|
key_hash = script.key_hash # local_cache_attribute
|
|
scripts = script.scripts # local_cache_attribute
|
|
|
|
script_heading = "Script"
|
|
if indices is None:
|
|
indices = []
|
|
if indices:
|
|
script_heading += " " + ".".join(str(i) for i in indices)
|
|
|
|
script_type_name_suffix = ""
|
|
if script_type == CNST.PUB_KEY:
|
|
if key_path:
|
|
script_type_name_suffix = "path"
|
|
elif key_hash:
|
|
script_type_name_suffix = "hash"
|
|
|
|
props: list[PropertyType] = [
|
|
(
|
|
f"{script_heading} - {SCRIPT_TYPE_NAMES[script_type]} {script_type_name_suffix}:",
|
|
None,
|
|
)
|
|
]
|
|
append = props.append # local_cache_attribute
|
|
|
|
if script_type == CNST.PUB_KEY:
|
|
assert key_hash is not None or key_path # validate_script
|
|
if key_hash:
|
|
append((None, bech32.encode(bech32.HRP_SHARED_KEY_HASH, key_hash)))
|
|
elif key_path:
|
|
append((address_n_to_str(key_path), None))
|
|
elif script_type == CNST.N_OF_K:
|
|
assert script.required_signatures_count is not None # validate_script
|
|
append(
|
|
(
|
|
TR.cardano__x_of_y_signatures_template.format(
|
|
script.required_signatures_count, len(scripts)
|
|
),
|
|
None,
|
|
)
|
|
)
|
|
elif script_type == CNST.INVALID_BEFORE:
|
|
assert script.invalid_before is not None # validate_script
|
|
append((str(script.invalid_before), None))
|
|
elif script_type == CNST.INVALID_HEREAFTER:
|
|
assert script.invalid_hereafter is not None # validate_script
|
|
append((str(script.invalid_hereafter), None))
|
|
|
|
if script_type in (
|
|
CNST.ALL,
|
|
CNST.ANY,
|
|
CNST.N_OF_K,
|
|
):
|
|
assert scripts # validate_script
|
|
append((TR.cardano__nested_scripts_template.format(len(scripts)), None))
|
|
|
|
await confirm_properties(
|
|
"verify_script",
|
|
TR.cardano__verify_script,
|
|
props,
|
|
br_code=BRT_Other,
|
|
)
|
|
|
|
for i, sub_script in enumerate(scripts):
|
|
await show_native_script(sub_script, indices + [i + 1])
|
|
|
|
|
|
async def show_script_hash(
|
|
script_hash: bytes,
|
|
display_format: CardanoNativeScriptHashDisplayFormat,
|
|
) -> None:
|
|
from trezor.enums import CardanoNativeScriptHashDisplayFormat
|
|
|
|
assert display_format in (
|
|
CardanoNativeScriptHashDisplayFormat.BECH32,
|
|
CardanoNativeScriptHashDisplayFormat.POLICY_ID,
|
|
)
|
|
|
|
if display_format == CardanoNativeScriptHashDisplayFormat.BECH32:
|
|
await confirm_properties(
|
|
"verify_script",
|
|
TR.cardano__verify_script,
|
|
(
|
|
(
|
|
TR.cardano__script_hash,
|
|
bech32.encode(bech32.HRP_SCRIPT_HASH, script_hash),
|
|
),
|
|
),
|
|
br_code=BRT_Other,
|
|
)
|
|
elif display_format == CardanoNativeScriptHashDisplayFormat.POLICY_ID:
|
|
await layouts.confirm_blob(
|
|
"verify_script",
|
|
TR.cardano__verify_script,
|
|
script_hash,
|
|
TR.cardano__policy_id,
|
|
br_code=BRT_Other,
|
|
)
|
|
|
|
|
|
async def show_tx_init(title: str) -> bool:
|
|
should_show_details = await layouts.should_show_more(
|
|
TR.cardano__confirm_transaction,
|
|
(
|
|
(
|
|
ui.DEMIBOLD,
|
|
title,
|
|
),
|
|
(ui.NORMAL, TR.cardano__choose_level_of_details),
|
|
),
|
|
TR.buttons__show_all,
|
|
confirm=TR.cardano__show_simple,
|
|
)
|
|
|
|
return should_show_details
|
|
|
|
|
|
async def confirm_input(input: messages.CardanoTxInput) -> None:
|
|
await confirm_properties(
|
|
"confirm_input",
|
|
TR.cardano__confirm_transaction,
|
|
(
|
|
(TR.cardano__input_id, input.prev_hash),
|
|
(TR.cardano__input_index, str(input.prev_index)),
|
|
),
|
|
br_code=BRT_Other,
|
|
)
|
|
|
|
|
|
async def confirm_sending(
|
|
ada_amount: int,
|
|
to: str,
|
|
output_type: Literal["address", "change", "collateral-return"],
|
|
network_id: int,
|
|
chunkify: bool,
|
|
) -> None:
|
|
if output_type == "address":
|
|
title = TR.cardano__sending
|
|
elif output_type == "change":
|
|
title = TR.cardano__change_output
|
|
elif output_type == "collateral-return":
|
|
title = TR.cardano__collateral_return
|
|
else:
|
|
raise RuntimeError # should be unreachable
|
|
|
|
await layouts.confirm_output(
|
|
to,
|
|
format_coin_amount(ada_amount, network_id),
|
|
title,
|
|
br_code=ButtonRequestType.Other,
|
|
chunkify=chunkify,
|
|
)
|
|
|
|
|
|
async def confirm_sending_token(policy_id: bytes, token: messages.CardanoToken) -> None:
|
|
assert token.amount is not None # _validate_token
|
|
|
|
await confirm_properties(
|
|
"confirm_token",
|
|
TR.cardano__confirm_transaction,
|
|
(
|
|
(
|
|
TR.cardano__asset_fingerprint,
|
|
format_asset_fingerprint(
|
|
policy_id=policy_id,
|
|
asset_name_bytes=token.asset_name_bytes,
|
|
),
|
|
),
|
|
(TR.cardano__amount_sent_decimals_unknown, format_amount(token.amount, 0)),
|
|
),
|
|
br_code=BRT_Other,
|
|
)
|
|
|
|
|
|
async def confirm_datum_hash(datum_hash: bytes) -> None:
|
|
await confirm_properties(
|
|
"confirm_datum_hash",
|
|
TR.cardano__confirm_transaction,
|
|
(
|
|
(
|
|
TR.cardano__datum_hash,
|
|
bech32.encode(bech32.HRP_OUTPUT_DATUM_HASH, datum_hash),
|
|
),
|
|
),
|
|
br_code=BRT_Other,
|
|
)
|
|
|
|
|
|
async def confirm_inline_datum(first_chunk: bytes, inline_datum_size: int) -> None:
|
|
await _confirm_data_chunk(
|
|
"confirm_inline_datum",
|
|
TR.cardano__inline_datum,
|
|
first_chunk,
|
|
inline_datum_size,
|
|
)
|
|
|
|
|
|
async def confirm_reference_script(
|
|
first_chunk: bytes, reference_script_size: int
|
|
) -> None:
|
|
await _confirm_data_chunk(
|
|
"confirm_reference_script",
|
|
TR.cardano__reference_script,
|
|
first_chunk,
|
|
reference_script_size,
|
|
)
|
|
|
|
|
|
async def _confirm_data_chunk(
|
|
br_type: str, title: str, first_chunk: bytes, data_size: int
|
|
) -> None:
|
|
MAX_DISPLAYED_SIZE = 56
|
|
displayed_bytes = first_chunk[:MAX_DISPLAYED_SIZE]
|
|
bytes_optional_plural = "byte" if data_size == 1 else "bytes"
|
|
props: list[tuple[str, bytes | None]] = [
|
|
(
|
|
f"{title} ({data_size} {bytes_optional_plural}):",
|
|
displayed_bytes,
|
|
)
|
|
]
|
|
if data_size > MAX_DISPLAYED_SIZE:
|
|
props.append(("...", None))
|
|
await confirm_properties(
|
|
br_type,
|
|
title=TR.cardano__confirm_transaction,
|
|
props=props,
|
|
br_code=BRT_Other,
|
|
)
|
|
|
|
|
|
async def show_credentials(
|
|
payment_credential: Credential,
|
|
stake_credential: Credential,
|
|
) -> None:
|
|
intro_text = TR.words__address
|
|
await _show_credential(payment_credential, intro_text, purpose="address")
|
|
await _show_credential(stake_credential, intro_text, purpose="address")
|
|
|
|
|
|
async def show_change_output_credentials(
|
|
payment_credential: Credential,
|
|
stake_credential: Credential,
|
|
) -> None:
|
|
intro_text = TR.cardano__intro_text_change
|
|
await _show_credential(payment_credential, intro_text, purpose="output")
|
|
await _show_credential(stake_credential, intro_text, purpose="output")
|
|
|
|
|
|
async def show_device_owned_output_credentials(
|
|
payment_credential: Credential,
|
|
stake_credential: Credential,
|
|
show_both_credentials: bool,
|
|
) -> None:
|
|
intro_text = TR.cardano__intro_text_owned_by_device
|
|
await _show_credential(payment_credential, intro_text, purpose="output")
|
|
if show_both_credentials:
|
|
await _show_credential(stake_credential, intro_text, purpose="output")
|
|
|
|
|
|
async def show_cvote_registration_payment_credentials(
|
|
payment_credential: Credential,
|
|
stake_credential: Credential,
|
|
show_both_credentials: bool,
|
|
show_payment_warning: bool,
|
|
) -> None:
|
|
intro_text = TR.cardano__intro_text_registration_payment
|
|
|
|
await _show_credential(
|
|
payment_credential, intro_text, purpose="cvote_reg_payment_address"
|
|
)
|
|
if show_both_credentials or show_payment_warning:
|
|
extra_text = CVOTE_REWARD_ELIGIBILITY_WARNING if show_payment_warning else None
|
|
await _show_credential(
|
|
stake_credential,
|
|
intro_text,
|
|
purpose="cvote_reg_payment_address",
|
|
extra_text=extra_text,
|
|
)
|
|
|
|
|
|
async def _show_credential(
|
|
credential: Credential,
|
|
intro_text: str,
|
|
purpose: Literal["address", "output", "cvote_reg_payment_address"],
|
|
extra_text: str | None = None,
|
|
) -> None:
|
|
title = {
|
|
"address": f"{ADDRESS_TYPE_NAMES[credential.address_type]} address",
|
|
"output": TR.cardano__confirm_transaction,
|
|
"cvote_reg_payment_address": TR.cardano__confirm_transaction,
|
|
}[purpose]
|
|
|
|
props: list[PropertyType] = []
|
|
append = props.append # local_cache_attribute
|
|
|
|
# Credential can be empty in case of enterprise address stake credential
|
|
# and reward address payment credential. In that case we don't want to
|
|
# show some of the "props".
|
|
if credential.is_set():
|
|
credential_title = credential.get_title()
|
|
# TODO: handle translation
|
|
append(
|
|
(
|
|
f"{intro_text} {credential.type_name} credential is a {credential_title}:",
|
|
None,
|
|
)
|
|
)
|
|
props.extend(credential.format())
|
|
|
|
if credential.is_unusual_path:
|
|
append((None, TR.cardano__unusual_path))
|
|
if credential.is_mismatch:
|
|
append((None, TR.cardano__credential_mismatch))
|
|
if credential.is_reward and purpose != "cvote_reg_payment_address":
|
|
# for cvote registrations, this is handled by extra_text at the end
|
|
append((TR.cardano__reward_address, None))
|
|
if credential.is_no_staking:
|
|
append(
|
|
(
|
|
f"{ADDRESS_TYPE_NAMES[credential.address_type]} {TR.cardano__address_no_staking}",
|
|
None,
|
|
)
|
|
)
|
|
|
|
if extra_text:
|
|
append((extra_text, None))
|
|
|
|
if len(props) > 0:
|
|
await confirm_properties(
|
|
"confirm_credential",
|
|
title,
|
|
props,
|
|
br_code=BRT_Other,
|
|
)
|
|
|
|
|
|
async def warn_path(path: list[int], title: str) -> None:
|
|
await layouts.confirm_path_warning(address_n_to_str(path), path_type=title)
|
|
|
|
|
|
async def warn_tx_output_contains_tokens(is_collateral_return: bool = False) -> None:
|
|
content = (
|
|
TR.cardano__collateral_output_contains_tokens
|
|
if is_collateral_return
|
|
else TR.cardano__transaction_output_contains_tokens
|
|
)
|
|
await confirm_metadata(
|
|
"confirm_tokens",
|
|
TR.cardano__confirm_transaction,
|
|
content,
|
|
br_code=BRT_Other,
|
|
)
|
|
|
|
|
|
async def warn_tx_contains_mint() -> None:
|
|
await confirm_metadata(
|
|
"confirm_tokens",
|
|
TR.cardano__confirm_transaction,
|
|
TR.cardano__transaction_contains_minting_or_burning,
|
|
br_code=BRT_Other,
|
|
)
|
|
|
|
|
|
async def warn_tx_output_no_datum() -> None:
|
|
await confirm_metadata(
|
|
"confirm_no_datum_hash",
|
|
TR.cardano__confirm_transaction,
|
|
TR.cardano__transaction_contains_script_address_no_datum,
|
|
br_code=BRT_Other,
|
|
)
|
|
|
|
|
|
async def warn_no_script_data_hash() -> None:
|
|
await confirm_metadata(
|
|
"confirm_no_script_data_hash",
|
|
TR.cardano__confirm_transaction,
|
|
TR.cardano__transaction_no_script_data_hash,
|
|
br_code=BRT_Other,
|
|
)
|
|
|
|
|
|
async def warn_no_collateral_inputs() -> None:
|
|
await confirm_metadata(
|
|
"confirm_no_collateral_inputs",
|
|
TR.cardano__confirm_transaction,
|
|
TR.cardano__transaction_no_collateral_input,
|
|
br_code=BRT_Other,
|
|
)
|
|
|
|
|
|
async def warn_unknown_total_collateral() -> None:
|
|
await layouts.show_warning(
|
|
"confirm_unknown_total_collateral",
|
|
TR.cardano__unknown_collateral_amount,
|
|
TR.cardano__check_all_items,
|
|
br_code=BRT_Other,
|
|
)
|
|
|
|
|
|
async def confirm_witness_request(
|
|
witness_path: list[int],
|
|
) -> None:
|
|
from . import seed
|
|
|
|
if seed.is_multisig_path(witness_path):
|
|
path_title = TR.cardano__multisig_path
|
|
elif seed.is_minting_path(witness_path):
|
|
path_title = TR.cardano__token_minting_path
|
|
else:
|
|
path_title = TR.cardano__path
|
|
|
|
await layouts.confirm_text(
|
|
"confirm_total",
|
|
TR.cardano__confirm_transaction,
|
|
address_n_to_str(witness_path),
|
|
TR.cardano__sign_tx_path_template.format(path_title),
|
|
BRT_Other,
|
|
)
|
|
|
|
|
|
async def confirm_tx(
|
|
fee: int,
|
|
network_id: int,
|
|
protocol_magic: int,
|
|
ttl: int | None,
|
|
validity_interval_start: int | None,
|
|
total_collateral: int | None,
|
|
is_network_id_verifiable: bool,
|
|
tx_hash: bytes | None,
|
|
) -> None:
|
|
props: list[PropertyType] = [
|
|
(TR.cardano__transaction_fee, format_coin_amount(fee, network_id)),
|
|
]
|
|
append = props.append # local_cache_attribute
|
|
|
|
if total_collateral is not None:
|
|
append(
|
|
(
|
|
TR.cardano__total_collateral,
|
|
format_coin_amount(total_collateral, network_id),
|
|
)
|
|
)
|
|
|
|
if is_network_id_verifiable:
|
|
append(
|
|
(
|
|
f"{TR.cardano__network} {protocol_magics.to_ui_string(protocol_magic)}",
|
|
None,
|
|
)
|
|
)
|
|
|
|
append(
|
|
(
|
|
f"{TR.cardano__valid_since} {format_optional_int(validity_interval_start)}",
|
|
None,
|
|
)
|
|
)
|
|
append((f"{TR.cardano__ttl} {format_optional_int(ttl)}", None))
|
|
|
|
if tx_hash:
|
|
append((TR.cardano__transaction_id, tx_hash))
|
|
|
|
await confirm_properties(
|
|
"confirm_total",
|
|
TR.cardano__confirm_transaction,
|
|
props,
|
|
hold=True,
|
|
br_code=BRT_Other,
|
|
)
|
|
|
|
|
|
async def confirm_certificate(certificate: messages.CardanoTxCertificate) -> None:
|
|
# stake pool registration requires custom confirmation logic not covered
|
|
# in this call
|
|
assert certificate.type != CardanoCertificateType.STAKE_POOL_REGISTRATION
|
|
|
|
props: list[PropertyType] = [
|
|
(TR.cardano__confirm, CERTIFICATE_TYPE_NAMES[certificate.type]),
|
|
_format_stake_credential(
|
|
certificate.path, certificate.script_hash, certificate.key_hash
|
|
),
|
|
]
|
|
|
|
if certificate.type == CardanoCertificateType.STAKE_DELEGATION:
|
|
assert certificate.pool is not None # validate_certificate
|
|
props.append((TR.cardano__to_pool, format_stake_pool_id(certificate.pool)))
|
|
|
|
await confirm_properties(
|
|
"confirm_certificate",
|
|
TR.cardano__confirm_transaction,
|
|
props,
|
|
br_code=BRT_Other,
|
|
)
|
|
|
|
|
|
async def confirm_stake_pool_parameters(
|
|
pool_parameters: messages.CardanoPoolParametersType,
|
|
network_id: int,
|
|
) -> None:
|
|
margin_percentage = (
|
|
100.0 * pool_parameters.margin_numerator / pool_parameters.margin_denominator
|
|
)
|
|
percentage_formatted = str(float(margin_percentage)).rstrip("0").rstrip(".")
|
|
await confirm_properties(
|
|
"confirm_pool_registration",
|
|
TR.cardano__confirm_transaction,
|
|
(
|
|
(
|
|
TR.cardano__stake_pool_registration_pool_id,
|
|
format_stake_pool_id(pool_parameters.pool_id),
|
|
),
|
|
(TR.cardano__pool_reward_account, pool_parameters.reward_account),
|
|
(
|
|
f"{TR.cardano__pledge}: {format_coin_amount(pool_parameters.pledge, network_id)}\n"
|
|
+ f"{TR.cardano__cost}: {format_coin_amount(pool_parameters.cost, network_id)}\n"
|
|
+ f"{TR.cardano__margin}: {percentage_formatted}%",
|
|
None,
|
|
),
|
|
),
|
|
br_code=BRT_Other,
|
|
)
|
|
|
|
|
|
async def confirm_stake_pool_owner(
|
|
keychain: Keychain,
|
|
owner: messages.CardanoPoolOwner,
|
|
protocol_magic: int,
|
|
network_id: int,
|
|
) -> None:
|
|
from trezor import messages
|
|
|
|
props: list[tuple[str, str | None]] = []
|
|
if owner.staking_key_path:
|
|
props.append((TR.cardano__pool_owner, address_n_to_str(owner.staking_key_path)))
|
|
props.append(
|
|
(
|
|
addresses.derive_human_readable(
|
|
keychain,
|
|
messages.CardanoAddressParametersType(
|
|
address_type=CardanoAddressType.REWARD,
|
|
address_n=owner.staking_key_path,
|
|
),
|
|
protocol_magic,
|
|
network_id,
|
|
),
|
|
None,
|
|
)
|
|
)
|
|
else:
|
|
assert owner.staking_key_hash is not None # validate_pool_owners
|
|
props.append(
|
|
(
|
|
TR.cardano__pool_owner,
|
|
addresses.derive_human_readable(
|
|
keychain,
|
|
messages.CardanoAddressParametersType(
|
|
address_type=CardanoAddressType.REWARD,
|
|
staking_key_hash=owner.staking_key_hash,
|
|
),
|
|
protocol_magic,
|
|
network_id,
|
|
),
|
|
)
|
|
)
|
|
|
|
await confirm_properties(
|
|
"confirm_pool_owners",
|
|
TR.cardano__confirm_transaction,
|
|
props,
|
|
br_code=BRT_Other,
|
|
)
|
|
|
|
|
|
async def confirm_stake_pool_metadata(
|
|
metadata: messages.CardanoPoolMetadataType | None,
|
|
) -> None:
|
|
if metadata is None:
|
|
await confirm_properties(
|
|
"confirm_pool_metadata",
|
|
TR.cardano__confirm_transaction,
|
|
((TR.cardano__anonymous_pool, None),),
|
|
br_code=BRT_Other,
|
|
)
|
|
return
|
|
|
|
await confirm_properties(
|
|
"confirm_pool_metadata",
|
|
TR.cardano__confirm_transaction,
|
|
(
|
|
(TR.cardano__pool_metadata_url, metadata.url),
|
|
(TR.cardano__pool_metadata_hash, metadata.hash),
|
|
),
|
|
br_code=BRT_Other,
|
|
)
|
|
|
|
|
|
async def confirm_stake_pool_registration_final(
|
|
protocol_magic: int,
|
|
ttl: int | None,
|
|
validity_interval_start: int | None,
|
|
) -> None:
|
|
await confirm_properties(
|
|
"confirm_pool_final",
|
|
TR.cardano__confirm_transaction,
|
|
(
|
|
(TR.cardano__confirm_signing_stake_pool, None),
|
|
(TR.cardano__network, protocol_magics.to_ui_string(protocol_magic)),
|
|
(
|
|
TR.cardano__valid_since,
|
|
format_optional_int(validity_interval_start),
|
|
),
|
|
(TR.cardano__ttl, format_optional_int(ttl)),
|
|
),
|
|
hold=True,
|
|
br_code=BRT_Other,
|
|
)
|
|
|
|
|
|
async def confirm_withdrawal(
|
|
withdrawal: messages.CardanoTxWithdrawal,
|
|
address_bytes: bytes,
|
|
network_id: int,
|
|
) -> None:
|
|
address_type_name = (
|
|
TR.cardano__script_reward if withdrawal.script_hash else TR.cardano__reward
|
|
)
|
|
address = addresses.encode_human_readable(address_bytes)
|
|
props: list[PropertyType] = [
|
|
(
|
|
TR.cardano__withdrawal_for_address_template.format(address_type_name),
|
|
address,
|
|
),
|
|
]
|
|
|
|
if withdrawal.path:
|
|
props.append(
|
|
_format_stake_credential(
|
|
withdrawal.path, withdrawal.script_hash, withdrawal.key_hash
|
|
)
|
|
)
|
|
|
|
props.append(
|
|
(f"{TR.words__amount}:", format_coin_amount(withdrawal.amount, network_id))
|
|
)
|
|
|
|
await confirm_properties(
|
|
"confirm_withdrawal",
|
|
TR.cardano__confirm_transaction,
|
|
props,
|
|
br_code=BRT_Other,
|
|
)
|
|
|
|
|
|
def _format_stake_credential(
|
|
path: list[int], script_hash: bytes | None, key_hash: bytes | None
|
|
) -> tuple[str, str]:
|
|
from .helpers.paths import ADDRESS_INDEX_PATH_INDEX, RECOMMENDED_ADDRESS_INDEX
|
|
|
|
if path:
|
|
account_number = format_account_number(path)
|
|
address_index = path[ADDRESS_INDEX_PATH_INDEX]
|
|
if address_index == RECOMMENDED_ADDRESS_INDEX:
|
|
return (
|
|
TR.cardano__for_account_template.format(account_number),
|
|
address_n_to_str(path),
|
|
)
|
|
return (
|
|
TR.cardano__for_account_and_index_template.format(
|
|
account_number, address_index
|
|
),
|
|
address_n_to_str(path),
|
|
)
|
|
elif key_hash:
|
|
return (
|
|
TR.cardano__for_key_hash,
|
|
bech32.encode(bech32.HRP_STAKE_KEY_HASH, key_hash),
|
|
)
|
|
elif script_hash:
|
|
return (
|
|
TR.cardano__for_script,
|
|
bech32.encode(bech32.HRP_SCRIPT_HASH, script_hash),
|
|
)
|
|
else:
|
|
# should be unreachable unless there's a bug in validation
|
|
raise ValueError
|
|
|
|
|
|
async def confirm_cvote_registration_delegation(
|
|
public_key: str,
|
|
weight: int,
|
|
) -> None:
|
|
props: list[PropertyType] = [
|
|
(TR.cardano__vote_key_registration, None),
|
|
(TR.cardano__delegating_to, public_key),
|
|
]
|
|
if weight is not None:
|
|
props.append((TR.cardano__weight, str(weight)))
|
|
|
|
await confirm_properties(
|
|
"confirm_cvote_registration_delegation",
|
|
title=TR.cardano__confirm_transaction,
|
|
props=props,
|
|
br_code=ButtonRequestType.Other,
|
|
)
|
|
|
|
|
|
async def confirm_cvote_registration_payment_address(
|
|
payment_address: str,
|
|
should_show_payment_warning: bool,
|
|
) -> None:
|
|
props = [
|
|
(TR.cardano__vote_key_registration, None),
|
|
(TR.cardano__rewards_go_to, payment_address),
|
|
]
|
|
if should_show_payment_warning:
|
|
props.append((CVOTE_REWARD_ELIGIBILITY_WARNING, None))
|
|
await confirm_properties(
|
|
"confirm_cvote_registration_payment_address",
|
|
title=TR.cardano__confirm_transaction,
|
|
props=props,
|
|
br_code=ButtonRequestType.Other,
|
|
)
|
|
|
|
|
|
async def confirm_cvote_registration(
|
|
vote_public_key: str | None,
|
|
staking_path: list[int],
|
|
nonce: int,
|
|
voting_purpose: int | None,
|
|
) -> None:
|
|
props: list[PropertyType] = [(TR.cardano__vote_key_registration, None)]
|
|
if vote_public_key is not None:
|
|
props.append((TR.cardano__vote_public_key, vote_public_key))
|
|
props.extend(
|
|
[
|
|
(
|
|
f"{TR.cardano__staking_key_for_account} {format_account_number(staking_path)}:",
|
|
address_n_to_str(staking_path),
|
|
),
|
|
(TR.cardano__nonce, str(nonce)),
|
|
]
|
|
)
|
|
if voting_purpose is not None:
|
|
props.append(
|
|
(
|
|
TR.cardano__voting_purpose,
|
|
(
|
|
TR.cardano__catalyst
|
|
if voting_purpose == 0
|
|
else f"{voting_purpose} ({TR.cardano__other})"
|
|
),
|
|
)
|
|
)
|
|
|
|
await confirm_properties(
|
|
"confirm_cvote_registration",
|
|
title=TR.cardano__confirm_transaction,
|
|
props=props,
|
|
br_code=ButtonRequestType.Other,
|
|
)
|
|
|
|
|
|
async def show_auxiliary_data_hash(auxiliary_data_hash: bytes) -> None:
|
|
await confirm_properties(
|
|
"confirm_auxiliary_data",
|
|
TR.cardano__confirm_transaction,
|
|
((TR.cardano__auxiliary_data_hash, auxiliary_data_hash),),
|
|
br_code=BRT_Other,
|
|
)
|
|
|
|
|
|
async def confirm_token_minting(policy_id: bytes, token: messages.CardanoToken) -> None:
|
|
assert token.mint_amount is not None # _validate_token
|
|
await confirm_properties(
|
|
"confirm_mint",
|
|
TR.cardano__confirm_transaction,
|
|
(
|
|
(
|
|
TR.cardano__asset_fingerprint,
|
|
format_asset_fingerprint(
|
|
policy_id,
|
|
token.asset_name_bytes,
|
|
),
|
|
),
|
|
(
|
|
(
|
|
TR.cardano__amount_minted_decimals_unknown
|
|
if token.mint_amount >= 0
|
|
else TR.cardano__amount_burned_decimals_unknown
|
|
),
|
|
format_amount(token.mint_amount, 0),
|
|
),
|
|
),
|
|
br_code=BRT_Other,
|
|
)
|
|
|
|
|
|
async def warn_tx_network_unverifiable() -> None:
|
|
await confirm_metadata(
|
|
"warning_no_outputs",
|
|
TR.cardano__warning,
|
|
TR.cardano__no_output_tx,
|
|
br_code=BRT_Other,
|
|
)
|
|
|
|
|
|
async def confirm_script_data_hash(script_data_hash: bytes) -> None:
|
|
await confirm_properties(
|
|
"confirm_script_data_hash",
|
|
TR.cardano__confirm_transaction,
|
|
(
|
|
(
|
|
TR.cardano__script_data_hash,
|
|
bech32.encode(bech32.HRP_SCRIPT_DATA_HASH, script_data_hash),
|
|
),
|
|
),
|
|
br_code=BRT_Other,
|
|
)
|
|
|
|
|
|
async def confirm_collateral_input(
|
|
collateral_input: messages.CardanoTxCollateralInput,
|
|
) -> None:
|
|
await confirm_properties(
|
|
"confirm_collateral_input",
|
|
TR.cardano__confirm_transaction,
|
|
(
|
|
(TR.cardano__collateral_input_id, collateral_input.prev_hash),
|
|
(
|
|
TR.cardano__collateral_input_index,
|
|
str(collateral_input.prev_index),
|
|
),
|
|
),
|
|
br_code=BRT_Other,
|
|
)
|
|
|
|
|
|
async def confirm_reference_input(
|
|
reference_input: messages.CardanoTxReferenceInput,
|
|
) -> None:
|
|
await confirm_properties(
|
|
"confirm_reference_input",
|
|
TR.cardano__confirm_transaction,
|
|
(
|
|
(TR.cardano__reference_input_id, reference_input.prev_hash),
|
|
(TR.cardano__reference_input_index, str(reference_input.prev_index)),
|
|
),
|
|
br_code=BRT_Other,
|
|
)
|
|
|
|
|
|
async def confirm_required_signer(
|
|
required_signer: messages.CardanoTxRequiredSigner,
|
|
) -> None:
|
|
assert (
|
|
required_signer.key_hash is not None or required_signer.key_path
|
|
) # _validate_required_signer
|
|
formatted_signer = (
|
|
bech32.encode(bech32.HRP_REQUIRED_SIGNER_KEY_HASH, required_signer.key_hash)
|
|
if required_signer.key_hash is not None
|
|
else address_n_to_str(required_signer.key_path)
|
|
)
|
|
|
|
await confirm_properties(
|
|
"confirm_required_signer",
|
|
TR.cardano__confirm_transaction,
|
|
((TR.cardano__required_signer, formatted_signer),),
|
|
br_code=BRT_Other,
|
|
)
|
|
|
|
|
|
async def show_cardano_address(
|
|
address_parameters: messages.CardanoAddressParametersType,
|
|
address: str,
|
|
protocol_magic: int,
|
|
chunkify: bool,
|
|
) -> None:
|
|
CAT = CardanoAddressType # local_cache_global
|
|
|
|
network_name = None
|
|
if not protocol_magics.is_mainnet(protocol_magic):
|
|
network_name = protocol_magics.to_ui_string(protocol_magic)
|
|
|
|
path = None
|
|
account = ADDRESS_TYPE_NAMES[address_parameters.address_type]
|
|
if address_parameters.address_type in (
|
|
CAT.BYRON,
|
|
CAT.BASE,
|
|
CAT.BASE_KEY_SCRIPT,
|
|
CAT.POINTER,
|
|
CAT.ENTERPRISE,
|
|
CAT.REWARD,
|
|
):
|
|
if address_parameters.address_n:
|
|
path = address_n_to_str(address_parameters.address_n)
|
|
elif address_parameters.address_n_staking:
|
|
path = address_n_to_str(address_parameters.address_n_staking)
|
|
|
|
await layouts.show_address(
|
|
address,
|
|
path=path,
|
|
account=account,
|
|
network=network_name,
|
|
chunkify=chunkify,
|
|
)
|