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.
trezor-firmware/core/src/apps/cardano/layout.py

1004 lines
29 KiB

from typing import TYPE_CHECKING
from trezor import ui
from trezor.enums import (
ButtonRequestType,
CardanoAddressType,
CardanoCertificateType,
CardanoNativeScriptType,
)
from trezor.strings import format_amount
from trezor.ui import layouts
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,
)
confirm_metadata = layouts.confirm_metadata # global_import_cache
confirm_properties = layouts.confirm_properties # global_import_cache
if TYPE_CHECKING:
from typing import Literal
from trezor.wire import Context
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: "Legacy",
CardanoAddressType.BASE: "Base",
CardanoAddressType.BASE_SCRIPT_KEY: "Base",
CardanoAddressType.BASE_KEY_SCRIPT: "Base",
CardanoAddressType.BASE_SCRIPT_SCRIPT: "Base",
CardanoAddressType.POINTER: "Pointer",
CardanoAddressType.POINTER_SCRIPT: "Pointer",
CardanoAddressType.ENTERPRISE: "Enterprise",
CardanoAddressType.ENTERPRISE_SCRIPT: "Enterprise",
CardanoAddressType.REWARD: "Reward",
CardanoAddressType.REWARD_SCRIPT: "Reward",
}
SCRIPT_TYPE_NAMES = {
CardanoNativeScriptType.PUB_KEY: "Key",
CardanoNativeScriptType.ALL: "All",
CardanoNativeScriptType.ANY: "Any",
CardanoNativeScriptType.N_OF_K: "N of K",
CardanoNativeScriptType.INVALID_BEFORE: "Invalid before",
CardanoNativeScriptType.INVALID_HEREAFTER: "Invalid hereafter",
}
CERTIFICATE_TYPE_NAMES = {
CardanoCertificateType.STAKE_REGISTRATION: "Stake key registration",
CardanoCertificateType.STAKE_DEREGISTRATION: "Stake key deregistration",
CardanoCertificateType.STAKE_DELEGATION: "Stake delegation",
CardanoCertificateType.STAKE_POOL_REGISTRATION: "Stakepool registration",
}
BRT_Other = ButtonRequestType.Other # global_import_cache
CVOTE_REWARD_ELIGIBILITY_WARNING = (
"Warning: The address is not a payment address, it is not eligible for rewards."
)
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(
ctx: Context,
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(
(
f"Requires {script.required_signatures_count} out of {len(scripts)} signatures.",
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((f"Contains {len(scripts)} nested scripts.", None))
await confirm_properties(
ctx,
"verify_script",
"Verify script",
props,
br_code=BRT_Other,
)
for i, sub_script in enumerate(scripts):
await show_native_script(ctx, sub_script, indices + [i + 1])
async def show_script_hash(
ctx: Context,
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(
ctx,
"verify_script",
"Verify script",
(("Script hash:", bech32.encode(bech32.HRP_SCRIPT_HASH, script_hash)),),
br_code=BRT_Other,
)
elif display_format == CardanoNativeScriptHashDisplayFormat.POLICY_ID:
await layouts.confirm_blob(
ctx,
"verify_script",
"Verify script",
script_hash,
"Policy ID:",
br_code=BRT_Other,
)
async def show_tx_init(ctx: Context, title: str) -> bool:
should_show_details = await layouts.should_show_more(
ctx,
"Confirm transaction",
(
(
ui.BOLD,
title,
),
(ui.NORMAL, "Choose level of details:"),
),
"Show All",
confirm="Show Simple",
)
return should_show_details
async def confirm_input(ctx: Context, input: messages.CardanoTxInput) -> None:
await confirm_properties(
ctx,
"confirm_input",
"Confirm transaction",
(
("Input ID:", input.prev_hash),
("Input index:", str(input.prev_index)),
),
br_code=BRT_Other,
)
async def confirm_sending(
ctx: Context,
ada_amount: int,
to: str,
output_type: Literal["address", "change", "collateral-return"],
network_id: int,
) -> None:
if output_type == "address":
title = "Sending"
elif output_type == "change":
title = "Change output"
elif output_type == "collateral-return":
title = "Collateral return"
else:
raise RuntimeError # should be unreachable
await layouts.confirm_output(
ctx,
to,
format_coin_amount(ada_amount, network_id),
title,
br_code=ButtonRequestType.Other,
)
async def confirm_sending_token(
ctx: Context, policy_id: bytes, token: messages.CardanoToken
) -> None:
assert token.amount is not None # _validate_token
await confirm_properties(
ctx,
"confirm_token",
"Confirm transaction",
(
(
"Asset fingerprint:",
format_asset_fingerprint(
policy_id=policy_id,
asset_name_bytes=token.asset_name_bytes,
),
),
("Amount sent:", format_amount(token.amount, 0)),
),
br_code=BRT_Other,
)
async def confirm_datum_hash(ctx: Context, datum_hash: bytes) -> None:
await confirm_properties(
ctx,
"confirm_datum_hash",
"Confirm transaction",
(
(
"Datum hash:",
bech32.encode(bech32.HRP_OUTPUT_DATUM_HASH, datum_hash),
),
),
br_code=BRT_Other,
)
async def confirm_inline_datum(
ctx: Context, first_chunk: bytes, inline_datum_size: int
) -> None:
await _confirm_data_chunk(
ctx,
"confirm_inline_datum",
"Inline datum",
first_chunk,
inline_datum_size,
)
async def confirm_reference_script(
ctx: Context, first_chunk: bytes, reference_script_size: int
) -> None:
await _confirm_data_chunk(
ctx,
"confirm_reference_script",
"Reference script",
first_chunk,
reference_script_size,
)
async def _confirm_data_chunk(
ctx: Context, 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(
ctx,
br_type,
title="Confirm transaction",
props=props,
br_code=BRT_Other,
)
async def show_credentials(
ctx: Context,
payment_credential: Credential,
stake_credential: Credential,
) -> None:
intro_text = "Address"
await _show_credential(ctx, payment_credential, intro_text, purpose="address")
await _show_credential(ctx, stake_credential, intro_text, purpose="address")
async def show_change_output_credentials(
ctx: Context,
payment_credential: Credential,
stake_credential: Credential,
) -> None:
intro_text = "The following address is a change address. Its"
await _show_credential(ctx, payment_credential, intro_text, purpose="output")
await _show_credential(ctx, stake_credential, intro_text, purpose="output")
async def show_device_owned_output_credentials(
ctx: Context,
payment_credential: Credential,
stake_credential: Credential,
show_both_credentials: bool,
) -> None:
intro_text = "The following address is owned by this device. Its"
await _show_credential(ctx, payment_credential, intro_text, purpose="output")
if show_both_credentials:
await _show_credential(ctx, stake_credential, intro_text, purpose="output")
async def show_cvote_registration_payment_credentials(
ctx: Context,
payment_credential: Credential,
stake_credential: Credential,
show_both_credentials: bool,
show_payment_warning: bool,
) -> None:
intro_text = (
"The vote key registration payment address is owned by this device. Its"
)
await _show_credential(
ctx, 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(
ctx,
stake_credential,
intro_text,
purpose="cvote_reg_payment_address",
extra_text=extra_text,
)
async def _show_credential(
ctx: Context,
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": "Confirm transaction",
"cvote_reg_payment_address": "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()
append(
(
f"{intro_text} {credential.type_name} credential is a {credential_title}:",
None,
)
)
props.extend(credential.format())
if credential.is_unusual_path:
append((None, "Path is unusual."))
if credential.is_mismatch:
append((None, "Credential doesn't match payment credential."))
if credential.is_reward and purpose != "cvote_reg_payment_address":
# for cvote registrations, this is handled by extra_text at the end
append(("Address is a reward address.", None))
if credential.is_no_staking:
append(
(
f"{ADDRESS_TYPE_NAMES[credential.address_type]} address - no staking rewards.",
None,
)
)
if extra_text:
append((extra_text, None))
if len(props) > 0:
await confirm_properties(
ctx,
"confirm_credential",
title,
props,
br_code=BRT_Other,
)
async def warn_path(ctx: Context, path: list[int], title: str) -> None:
await layouts.confirm_path_warning(ctx, address_n_to_str(path), path_type=title)
async def warn_tx_output_contains_tokens(
ctx: Context, is_collateral_return: bool = False
) -> None:
content = (
"The collateral return output contains tokens."
if is_collateral_return
else "The following transaction output contains tokens."
)
await confirm_metadata(
ctx,
"confirm_tokens",
"Confirm transaction",
content,
br_code=BRT_Other,
)
async def warn_tx_contains_mint(ctx: Context) -> None:
await confirm_metadata(
ctx,
"confirm_tokens",
"Confirm transaction",
"The transaction contains minting or burning of tokens.",
br_code=BRT_Other,
)
async def warn_tx_output_no_datum(ctx: Context) -> None:
await confirm_metadata(
ctx,
"confirm_no_datum_hash",
"Confirm transaction",
"The following transaction output contains a script address, but does not contain a datum.",
br_code=BRT_Other,
)
async def warn_no_script_data_hash(ctx: Context) -> None:
await confirm_metadata(
ctx,
"confirm_no_script_data_hash",
"Confirm transaction",
"The transaction contains no script data hash. Plutus script will not be able to run.",
br_code=BRT_Other,
)
async def warn_no_collateral_inputs(ctx: Context) -> None:
await confirm_metadata(
ctx,
"confirm_no_collateral_inputs",
"Confirm transaction",
"The transaction contains no collateral inputs. Plutus script will not be able to run.",
br_code=BRT_Other,
)
async def warn_unknown_total_collateral(ctx: Context) -> None:
await confirm_metadata(
ctx,
"confirm_unknown_total_collateral",
"Warning",
"Unknown collateral amount, check all items carefully.",
br_code=BRT_Other,
)
async def confirm_witness_request(
ctx: Context,
witness_path: list[int],
) -> None:
from . import seed
if seed.is_multisig_path(witness_path):
path_title = "multi-sig path"
elif seed.is_minting_path(witness_path):
path_title = "token minting path"
else:
path_title = "path"
await layouts.confirm_text(
ctx,
"confirm_total",
"Confirm transaction",
address_n_to_str(witness_path),
f"Sign transaction with {path_title}:",
BRT_Other,
)
async def confirm_tx(
ctx: Context,
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] = [
("Transaction fee:", format_coin_amount(fee, network_id)),
]
append = props.append # local_cache_attribute
if total_collateral is not None:
append(("Total collateral:", format_coin_amount(total_collateral, network_id)))
if is_network_id_verifiable:
append((f"Network: {protocol_magics.to_ui_string(protocol_magic)}", None))
append((f"Valid since: {format_optional_int(validity_interval_start)}", None))
append((f"TTL: {format_optional_int(ttl)}", None))
if tx_hash:
append(("Transaction ID:", tx_hash))
await confirm_properties(
ctx,
"confirm_total",
"Confirm transaction",
props,
hold=True,
br_code=BRT_Other,
)
async def confirm_certificate(
ctx: Context, 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] = [
("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(("to pool:", format_stake_pool_id(certificate.pool)))
await confirm_properties(
ctx,
"confirm_certificate",
"Confirm transaction",
props,
br_code=BRT_Other,
)
async def confirm_stake_pool_parameters(
ctx: Context,
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(
ctx,
"confirm_pool_registration",
"Confirm transaction",
(
(
"Stake pool registration\nPool ID:",
format_stake_pool_id(pool_parameters.pool_id),
),
("Pool reward account:", pool_parameters.reward_account),
(
f"Pledge: {format_coin_amount(pool_parameters.pledge, network_id)}\n"
+ f"Cost: {format_coin_amount(pool_parameters.cost, network_id)}\n"
+ f"Margin: {percentage_formatted}%",
None,
),
),
br_code=BRT_Other,
)
async def confirm_stake_pool_owner(
ctx: Context,
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(("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(
(
"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(
ctx,
"confirm_pool_owners",
"Confirm transaction",
props,
br_code=BRT_Other,
)
async def confirm_stake_pool_metadata(
ctx: Context,
metadata: messages.CardanoPoolMetadataType | None,
) -> None:
if metadata is None:
await confirm_properties(
ctx,
"confirm_pool_metadata",
"Confirm transaction",
(("Pool has no metadata (anonymous pool)", None),),
br_code=BRT_Other,
)
return
await confirm_properties(
ctx,
"confirm_pool_metadata",
"Confirm transaction",
(
("Pool metadata url:", metadata.url),
("Pool metadata hash:", metadata.hash),
),
br_code=BRT_Other,
)
async def confirm_stake_pool_registration_final(
ctx: Context,
protocol_magic: int,
ttl: int | None,
validity_interval_start: int | None,
) -> None:
await confirm_properties(
ctx,
"confirm_pool_final",
"Confirm transaction",
(
("Confirm signing the stake pool registration as an owner.", None),
("Network:", protocol_magics.to_ui_string(protocol_magic)),
("Valid since:", format_optional_int(validity_interval_start)),
("TTL:", format_optional_int(ttl)),
),
hold=True,
br_code=BRT_Other,
)
async def confirm_withdrawal(
ctx: Context,
withdrawal: messages.CardanoTxWithdrawal,
address_bytes: bytes,
network_id: int,
) -> None:
address_type_name = "script reward" if withdrawal.script_hash else "reward"
address = addresses.encode_human_readable(address_bytes)
props: list[PropertyType] = [
(f"Confirm withdrawal for {address_type_name} address:", address),
]
if withdrawal.path:
props.append(
_format_stake_credential(
withdrawal.path, withdrawal.script_hash, withdrawal.key_hash
)
)
props.append(("Amount:", format_coin_amount(withdrawal.amount, network_id)))
await confirm_properties(
ctx,
"confirm_withdrawal",
"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.utils import to_account_path
if path:
return (
f"for account {format_account_number(path)}:",
address_n_to_str(to_account_path(path)),
)
elif key_hash:
return ("for key hash:", bech32.encode(bech32.HRP_STAKE_KEY_HASH, key_hash))
elif script_hash:
return ("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(
ctx: Context,
public_key: str,
weight: int,
) -> None:
props: list[PropertyType] = [
("Vote key registration (CIP-36)", None),
("Delegating to:", public_key),
]
if weight is not None:
props.append(("Weight:", str(weight)))
await confirm_properties(
ctx,
"confirm_cvote_registration_delegation",
title="Confirm transaction",
props=props,
br_code=ButtonRequestType.Other,
)
async def confirm_cvote_registration_payment_address(
ctx: Context,
payment_address: str,
should_show_payment_warning: bool,
) -> None:
props = [
("Vote key registration (CIP-36)", None),
("Rewards go to:", payment_address),
]
if should_show_payment_warning:
props.append((CVOTE_REWARD_ELIGIBILITY_WARNING, None))
await confirm_properties(
ctx,
"confirm_cvote_registration_payment_address",
title="Confirm transaction",
props=props,
br_code=ButtonRequestType.Other,
)
async def confirm_cvote_registration(
ctx: Context,
vote_public_key: str | None,
staking_path: list[int],
nonce: int,
voting_purpose: int | None,
) -> None:
props: list[PropertyType] = [("Vote key registration (CIP-36)", None)]
if vote_public_key is not None:
props.append(("Vote public key:", vote_public_key))
props.extend(
[
(
f"Staking key for account {format_account_number(staking_path)}:",
address_n_to_str(staking_path),
),
("Nonce:", str(nonce)),
]
)
if voting_purpose is not None:
props.append(
(
"Voting purpose:",
"Catalyst" if voting_purpose == 0 else f"{voting_purpose} (other)",
)
)
await confirm_properties(
ctx,
"confirm_cvote_registration",
title="Confirm transaction",
props=props,
br_code=ButtonRequestType.Other,
)
async def show_auxiliary_data_hash(ctx: Context, auxiliary_data_hash: bytes) -> None:
await confirm_properties(
ctx,
"confirm_auxiliary_data",
"Confirm transaction",
(("Auxiliary data hash:", auxiliary_data_hash),),
br_code=BRT_Other,
)
async def confirm_token_minting(
ctx: Context, policy_id: bytes, token: messages.CardanoToken
) -> None:
assert token.mint_amount is not None # _validate_token
await confirm_properties(
ctx,
"confirm_mint",
"Confirm transaction",
(
(
"Asset fingerprint:",
format_asset_fingerprint(
policy_id,
token.asset_name_bytes,
),
),
(
"Amount minted:" if token.mint_amount >= 0 else "Amount burned:",
format_amount(token.mint_amount, 0),
),
),
br_code=BRT_Other,
)
async def warn_tx_network_unverifiable(ctx: Context) -> None:
await confirm_metadata(
ctx,
"warning_no_outputs",
"Warning",
"Transaction has no outputs, network cannot be verified.",
br_code=BRT_Other,
)
async def confirm_script_data_hash(ctx: Context, script_data_hash: bytes) -> None:
await confirm_properties(
ctx,
"confirm_script_data_hash",
"Confirm transaction",
(
(
"Script data hash:",
bech32.encode(bech32.HRP_SCRIPT_DATA_HASH, script_data_hash),
),
),
br_code=BRT_Other,
)
async def confirm_collateral_input(
ctx: Context, collateral_input: messages.CardanoTxCollateralInput
) -> None:
await confirm_properties(
ctx,
"confirm_collateral_input",
"Confirm transaction",
(
("Collateral input ID:", collateral_input.prev_hash),
("Collateral input index:", str(collateral_input.prev_index)),
),
br_code=BRT_Other,
)
async def confirm_reference_input(
ctx: Context, reference_input: messages.CardanoTxReferenceInput
) -> None:
await confirm_properties(
ctx,
"confirm_reference_input",
"Confirm transaction",
(
("Reference input ID:", reference_input.prev_hash),
("Reference input index:", str(reference_input.prev_index)),
),
br_code=BRT_Other,
)
async def confirm_required_signer(
ctx: Context, 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(
ctx,
"confirm_required_signer",
"Confirm transaction",
(("Required signer", formatted_signer),),
br_code=BRT_Other,
)
async def show_cardano_address(
ctx: Context,
address_parameters: messages.CardanoAddressParametersType,
address: str,
protocol_magic: int,
) -> 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)
title = f"{ADDRESS_TYPE_NAMES[address_parameters.address_type]} address"
address_extra = None
title_qr = title
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:
address_extra = address_n_to_str(address_parameters.address_n)
title_qr = address_n_to_str(address_parameters.address_n)
elif address_parameters.address_n_staking:
address_extra = address_n_to_str(address_parameters.address_n_staking)
title_qr = address_n_to_str(address_parameters.address_n_staking)
await layouts.show_address(
ctx,
address,
title=title,
network=network_name,
address_extra=address_extra,
title_qr=title_qr,
)