What changed, and why it matters
This is a routine internal code cleanup. It replaces loose groups of payment-request data (tuples) with named data classes (Refund and Trade) and moves shared helper code into a new file. There is no change to what the device shows or signs, and no security fix or vulnerability is present.
No security action needed; treat as normal refactoring review.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The commit refactors SLIP-24 payment-request handling across Bitcoin, Cardano, Ethereum, Ripple, Solana, Stellar, and the misc payment-notification app. Raw tuples representing refund/trade memos are replaced by typed Refund and Trade dataclasses defined in a new trezor.ui.layouts.slip24 module. Layout implementations (bolt, caesar, delizia, eckhart) are updated to consume the dataclasses and use a shared is_swap() helper. The new Trade class adds runtime assertions that sell_amount starts with ‘-’ when present and buy_amount starts with ‘+’. No cryptographic, protocol, or authorization behavior changes.
Changed components
core/src/apps/bitcoin/sign_tx/layout.pycore/src/apps/cardano/layout.pycore/src/apps/ethereum/layout.pycore/src/apps/misc/payment_notification.pycore/src/apps/ripple/layout.pycore/src/apps/solana/layout.pycore/src/apps/stellar/layout.pycore/src/trezor/ui/layouts/bolt/__init__.pycore/src/trezor/ui/layouts/caesar/__init__.pycore/src/trezor/ui/layouts/delizia/__init__.pycore/src/trezor/ui/layouts/eckhart/__init__.pycore/src/trezor/ui/layouts/slip24.pycore/embed/upymod/qstrdefsport.hInspect captured patch +184 / −161
diff --git a/core/embed/upymod/qstrdefsport.h b/core/embed/upymod/qstrdefsport.h
index b073ad18..110160fe 100644
--- a/core/embed/upymod/qstrdefsport.h
+++ b/core/embed/upymod/qstrdefsport.h
@@ -298,6 +298,7 @@ Q(sign_message)
Q(sign_registration_request)
Q(sign_tx)
Q(signverify)
+Q(slip24)
Q(slip39)
Q(storage)
Q(storage.cache)
@@ -386,6 +387,7 @@ Q(trezor.ui.layouts.menu)
Q(trezor.ui.layouts.progress)
Q(trezor.ui.layouts.recovery)
Q(trezor.ui.layouts.reset)
+Q(trezor.ui.layouts.slip24)
Q(trezor.utils)
Q(trezor.wire)
Q(trezor.wire.codec)
diff --git a/core/src/apps/bitcoin/sign_tx/layout.py b/core/src/apps/bitcoin/sign_tx/layout.py
index 663710df..d088bb05 100644
--- a/core/src/apps/bitcoin/sign_tx/layout.py
+++ b/core/src/apps/bitcoin/sign_tx/layout.py
@@ -162,6 +162,7 @@ async def show_payment_request_details(
address_n: Bip32Path | None,
) -> None:
from trezor import wire
+ from trezor.ui.layouts.slip24 import Refund, Trade
from apps.common.payment_request import parse_amount
@@ -182,7 +183,7 @@ async def show_payment_request_details(
address_n_to_str(refund_address_n) if refund_address_n else None
)
refunds.append(
- (memo.refund_memo.address, refund_account, refund_account_path)
+ Refund(memo.refund_memo.address, refund_account, refund_account_path)
)
elif memo.coin_purchase_memo:
coin_purchase_address_n = memo.coin_purchase_memo.address_n
@@ -193,7 +194,7 @@ async def show_payment_request_details(
else None
)
trades.append(
- (
+ Trade(
f"-\u00a0{total_amount}",
f"+\u00a0{memo.coin_purchase_memo.amount}",
memo.coin_purchase_memo.address,
diff --git a/core/src/apps/cardano/layout.py b/core/src/apps/cardano/layout.py
index 7a16936e..bc4eaf0d 100644
--- a/core/src/apps/cardano/layout.py
+++ b/core/src/apps/cardano/layout.py
@@ -1214,6 +1214,7 @@ async def require_confirm_payment_request(
network_id: int,
) -> None:
from trezor.ui.layouts import confirm_payment_request
+ from trezor.ui.layouts.slip24 import Refund, Trade
from apps.common.payment_request import parse_amount
@@ -1222,8 +1223,8 @@ async def require_confirm_payment_request(
)
texts: list[tuple[str | None, str]] = []
- refunds: list[tuple[str, str | None, str | None]] = []
- trades: list[tuple[str | None, str, str, str | None, str | None]] = []
+ refunds = []
+ trades = []
for memo in verified_payment_request.memos:
if memo.text_memo is not None:
texts.append((None, memo.text_memo.text))
@@ -1231,13 +1232,13 @@ async def require_confirm_payment_request(
texts.append((memo.text_details_memo.title, memo.text_details_memo.text))
elif memo.refund_memo:
refund_account_path = address_n_to_str(memo.refund_memo.address_n)
- refunds.append((memo.refund_memo.address, None, refund_account_path))
+ refunds.append(Refund(memo.refund_memo.address, None, refund_account_path))
elif memo.coin_purchase_memo:
coin_purchase_account_path = address_n_to_str(
memo.coin_purchase_memo.address_n
)
trades.append(
- (
+ Trade(
f"-\u00a0{total_amount}",
f"+\u00a0{memo.coin_purchase_memo.amount}",
memo.coin_purchase_memo.address,
diff --git a/core/src/apps/ethereum/layout.py b/core/src/apps/ethereum/layout.py
index aa13d9ec..892910fb 100644
--- a/core/src/apps/ethereum/layout.py
+++ b/core/src/apps/ethereum/layout.py
@@ -123,6 +123,7 @@ async def require_confirm_payment_request(
) -> None:
from trezor import wire
from trezor.ui.layouts import confirm_payment_request
+ from trezor.ui.layouts.slip24 import Refund, Trade
from apps.common.payment_request import parse_amount
@@ -143,14 +144,14 @@ async def require_confirm_payment_request(
memo.refund_memo.address_n
)
refunds.append(
- (memo.refund_memo.address, refund_account, refund_account_path)
+ Refund(memo.refund_memo.address, refund_account, refund_account_path)
)
elif memo.coin_purchase_memo:
coin_purchase_account, coin_purchase_account_path = get_account_and_path(
memo.coin_purchase_memo.address_n
)
trades.append(
- (
+ Trade(
f"- {total_amount}",
f"+ {memo.coin_purchase_memo.amount}",
memo.coin_purchase_memo.address,
diff --git a/core/src/apps/misc/payment_notification.py b/core/src/apps/misc/payment_notification.py
index f48ea83f..4701d440 100644
--- a/core/src/apps/misc/payment_notification.py
+++ b/core/src/apps/misc/payment_notification.py
@@ -11,6 +11,7 @@ from apps.common.paths import address_n_to_str
async def payment_notification(msg: PaymentNotification) -> Success:
from trezor.messages import Success
from trezor.ui.layouts import confirm_payment_request
+ from trezor.ui.layouts.slip24 import Trade
from trezor.wire import DataError
from apps.common.keychain import get_keychain
@@ -28,7 +29,7 @@ async def payment_notification(msg: PaymentNotification) -> Success:
verified_payment_request = msg.payment_req
texts: list[tuple[str | None, str]] = []
- trades: list[tuple[str | None, str, str, str | None, str | None]] = []
+ trades = []
for memo in verified_payment_request.memos:
# Note: we do not process RefundMemo here:
# if the swap fails, the fiat amount just remains in your custodial account, it does not get refunded anywhere
@@ -41,7 +42,7 @@ async def payment_notification(msg: PaymentNotification) -> Success:
memo.coin_purchase_memo.address_n
)
trades.append(
- (
+ Trade(
None, # if we later decide to somehow pass the fiat amount (and currency!) as part of the payment request in a more structured fashion,
# we should include it here so it gets shown on the trade screen, but for now we just have the fiat amount ad-hoc as part of a text memo.
f"+\u00a0{memo.coin_purchase_memo.amount}", # amount of crypto purchased
diff --git a/core/src/apps/ripple/layout.py b/core/src/apps/ripple/layout.py
index 980993c1..301a6f4e 100644
--- a/core/src/apps/ripple/layout.py
+++ b/core/src/apps/ripple/layout.py
@@ -47,6 +47,7 @@ async def require_confirm_payment_request(
address_n: Bip32Path | None,
) -> None:
from trezor.ui.layouts import confirm_payment_request
+ from trezor.ui.layouts.slip24 import Refund, Trade
from apps.common.paths import address_n_to_str
from apps.common.payment_request import parse_amount
@@ -66,13 +67,13 @@ async def require_confirm_payment_request(
texts.append((memo.text_details_memo.title, memo.text_details_memo.text))
elif memo.refund_memo:
refund_account_path = address_n_to_str(memo.refund_memo.address_n)
- refunds.append((memo.refund_memo.address, None, refund_account_path))
+ refunds.append(Refund(memo.refund_memo.address, None, refund_account_path))
elif memo.coin_purchase_memo:
coin_purchase_account_path = address_n_to_str(
memo.coin_purchase_memo.address_n
)
trades.append(
- (
+ Trade(
f"-\u00a0{total_amount}",
f"+\u00a0{memo.coin_purchase_memo.amount}",
memo.coin_purchase_memo.address,
diff --git a/core/src/apps/solana/layout.py b/core/src/apps/solana/layout.py
index c1afd243..794ea4be 100644
--- a/core/src/apps/solana/layout.py
+++ b/core/src/apps/solana/layout.py
@@ -540,12 +540,13 @@ async def confirm_payment_request(
verified_payment_request: PaymentRequest,
) -> None:
from trezor.ui.layouts import confirm_payment_request
+ from trezor.ui.layouts.slip24 import Refund, Trade
total_amount = format_amount_unit(format_amount(amount, decimals), token.symbol)
texts: list[tuple[str | None, str]] = []
- refunds: list[tuple[str, str | None, str | None]] = []
- trades: list[tuple[str | None, str, str, str | None, str | None]] = []
+ refunds = []
+ trades = []
for memo in verified_payment_request.memos:
if memo.text_memo is not None:
texts.append((None, memo.text_memo.text))
@@ -553,13 +554,13 @@ async def confirm_payment_request(
texts.append((memo.text_details_memo.title, memo.text_details_memo.text))
elif memo.refund_memo:
refund_account_path = address_n_to_str(memo.refund_memo.address_n)
- refunds.append((memo.refund_memo.address, None, refund_account_path))
+ refunds.append(Refund(memo.refund_memo.address, None, refund_account_path))
elif memo.coin_purchase_memo:
coin_purchase_account_path = address_n_to_str(
memo.coin_purchase_memo.address_n
)
trades.append(
- (
+ Trade(
f"-\u00a0{total_amount}",
f"+\u00a0{memo.coin_purchase_memo.amount}",
memo.coin_purchase_memo.address,
diff --git a/core/src/apps/stellar/layout.py b/core/src/apps/stellar/layout.py
index 3cfc0893..e7c01cad 100644
--- a/core/src/apps/stellar/layout.py
+++ b/core/src/apps/stellar/layout.py
@@ -63,14 +63,15 @@ async def require_confirm_payment_request(
asset: StellarAsset,
) -> None:
from trezor.ui.layouts import confirm_payment_request
+ from trezor.ui.layouts.slip24 import Refund, Trade
from apps.common.payment_request import parse_amount
total_amount = format_amount(parse_amount(verified_payment_request), asset)
texts: list[tuple[str | None, str]] = []
- refunds: list[tuple[str, str | None, str | None]] = []
- trades: list[tuple[str | None, str, str, str | None, str | None]] = []
+ refunds = []
+ trades = []
for memo in verified_payment_request.memos:
if memo.text_memo is not None:
texts.append((None, memo.text_memo.text))
@@ -78,13 +79,13 @@ async def require_confirm_payment_request(
texts.append((memo.text_details_memo.title, memo.text_details_memo.text))
elif memo.refund_memo:
refund_account_path = address_n_to_str(memo.refund_memo.address_n)
- refunds.append((memo.refund_memo.address, None, refund_account_path))
+ refunds.append(Refund(memo.refund_memo.address, None, refund_account_path))
elif memo.coin_purchase_memo:
coin_purchase_account_path = address_n_to_str(
memo.coin_purchase_memo.address_n
)
trades.append(
- (
+ Trade(
f"-\u00a0{total_amount}",
f"+\u00a0{memo.coin_purchase_memo.amount}",
memo.coin_purchase_memo.address,
diff --git a/core/src/trezor/ui/layouts/bolt/__init__.py b/core/src/trezor/ui/layouts/bolt/__init__.py
index 720f8660..39e3273b 100644
--- a/core/src/trezor/ui/layouts/bolt/__init__.py
+++ b/core/src/trezor/ui/layouts/bolt/__init__.py
@@ -14,6 +14,7 @@ if TYPE_CHECKING:
from trezor.messages import StellarAsset
from ..common import ExceptionType, PropertyType
+ from ..slip24 import Refund, Trade
BR_CODE_OTHER = ButtonRequestType.Other # global_import_cache
@@ -489,21 +490,21 @@ async def confirm_payment_request(
recipient_name: str,
recipient_address: str | None,
texts: Iterable[tuple[str | None, str]],
- refunds: Iterable[tuple[str, str | None, str | None]],
- trades: list[tuple[str | None, str, str, str | None, str | None]],
+ refunds: Iterable[Refund],
+ trades: list[Trade],
account_items: list[PropertyType] | None,
transaction_fee: str | None,
fee_info_items: Iterable[PropertyType] | None,
extra_menu_items: list[tuple[str, str]] | None = None,
) -> None:
- is_swap = len(trades) != 0 and all(
- sell_amount is not None for sell_amount, _, _, _, _ in trades
- )
+ from ..slip24 import is_swap
+
+ title = TR.words__swap if is_swap(trades) else TR.words__confirm
- for title, text in texts:
+ for t, text in texts:
await raise_if_not_confirmed(
trezorui_api.confirm_value(
- title=(title or (TR.words__swap if is_swap else TR.words__confirm)),
+ title=t or title,
value=text,
is_data=False,
description=None,
@@ -514,18 +515,18 @@ async def confirm_payment_request(
menu_items: list[PropertyType] = []
if recipient_address is not None:
menu_items.append((TR.address__title_provider_address, recipient_address, None))
- for r_address, r_account, r_account_path in refunds:
- menu_items.append((TR.address__title_refund_address, r_address, None))
- if r_account:
- menu_items.append((TR.words__account, r_account, None))
- if r_account_path:
+ for refund in refunds:
+ menu_items.append((TR.address__title_refund_address, refund.address, None))
+ if refund.account:
+ menu_items.append((TR.words__account, refund.account, None))
+ if refund.account_path:
menu_items.append(
- (TR.address_details__derivation_path, r_account_path, None)
+ (TR.address_details__derivation_path, refund.account_path, None)
)
await with_info(
trezorui_api.confirm_value(
- title=(TR.words__swap if is_swap else TR.words__confirm),
+ title=title,
subtitle=TR.words__provider,
value=recipient_name,
description=None,
@@ -543,14 +544,10 @@ async def confirm_payment_request(
ButtonRequestType.SignTx,
)
- for sell_amount, buy_amount, t_address, t_account, t_account_path in trades:
+ for trade in trades:
await confirm_trade(
- TR.words__swap if is_swap else TR.words__confirm,
- sell_amount,
- buy_amount,
- t_address,
- t_account,
- t_account_path,
+ title,
+ trade,
extra_menu_items or [],
)
@@ -982,27 +979,25 @@ def _confirm_summary(
async def confirm_trade(
title: str,
- sell_amount: str | None,
- buy_amount: str,
- address: str,
- account: str | None,
- account_path: str | None,
+ trade: Trade,
extra_menu_items: list[tuple[str, str]],
) -> None:
menu_items: list[PropertyType] = [
- (TR.address__title_receive_address, address, None)
+ (TR.address__title_receive_address, trade.address, None)
]
- if account:
- menu_items.append((TR.words__account, account, None))
- if account_path:
- menu_items.append((TR.address_details__derivation_path, account_path, None))
+ if trade.account:
+ menu_items.append((TR.words__account, trade.account, None))
+ if trade.account_path:
+ menu_items.append(
+ (TR.address_details__derivation_path, trade.account_path, None)
+ )
for k, v in extra_menu_items:
menu_items.append((k, v, None))
items = []
- if sell_amount is not None:
- items.append(("", sell_amount, None))
- items.append(("", buy_amount, None))
+ if trade.sell_amount is not None:
+ items.append(("", trade.sell_amount, None))
+ items.append(("", trade.buy_amount, None))
await with_info(
trezorui_api.confirm_properties(
title=title,
diff --git a/core/src/trezor/ui/layouts/caesar/__init__.py b/core/src/trezor/ui/layouts/caesar/__init__.py
index 9fb04008..a90ffd80 100644
--- a/core/src/trezor/ui/layouts/caesar/__init__.py
+++ b/core/src/trezor/ui/layouts/caesar/__init__.py
@@ -15,6 +15,7 @@ if TYPE_CHECKING:
from ..common import ExceptionType, PropertyType
from ..menu import Details
+ from ..slip24 import Refund, Trade
CONFIRMED = trezorui_api.CONFIRMED
@@ -554,8 +555,8 @@ async def confirm_payment_request(
recipient_name: str,
recipient_address: str | None,
texts: Iterable[tuple[str | None, str]],
- refunds: Iterable[tuple[str, str | None, str | None]],
- trades: list[tuple[str | None, str, str, str | None, str | None]],
+ refunds: Iterable[Refund],
+ trades: list[Trade],
account_items: list[PropertyType],
transaction_fee: str | None,
fee_info_items: Iterable[PropertyType] | None,
@@ -563,14 +564,14 @@ async def confirm_payment_request(
) -> None:
from trezor.ui.layouts.menu import Menu, confirm_with_menu
- is_swap = len(trades) != 0 and all(
- sell_amount is not None for sell_amount, _, _, _, _ in trades
- )
+ from ..slip24 import is_swap
+
+ title = TR.words__swap if is_swap(trades) else TR.words__confirm
- for title, text in texts:
+ for t, text in texts:
await raise_if_not_confirmed(
trezorui_api.confirm_value(
- title=title or (TR.words__swap if is_swap else TR.words__confirm),
+ title=t or title,
value=text,
description=None,
),
@@ -582,13 +583,13 @@ async def confirm_payment_request(
menu_items.append(
create_details(TR.address__title_provider_address, recipient_address)
)
- for r_address, r_account, r_account_path in refunds:
- refund_account_items: list[PropertyType] = [("", r_address, None)]
- if r_account:
- refund_account_items.append((TR.words__account, r_account, None))
- if r_account_path:
+ for refund in refunds:
+ refund_account_items: list[PropertyType] = [("", refund.address, None)]
+ if refund.account:
+ refund_account_items.append((TR.words__account, refund.account, None))
+ if refund.account_path:
refund_account_items.append(
- (TR.address_details__derivation_path, r_account_path, None)
+ (TR.address_details__derivation_path, refund.account_path, None)
)
menu_items.append(
create_details(
@@ -600,7 +601,7 @@ async def confirm_payment_request(
menu = Menu.root(menu_items)
main_layout = trezorui_api.confirm_with_info(
- title=(TR.words__swap if is_swap else TR.words__confirm),
+ title=title,
items=[(TR.words__provider, True), (recipient_name, False)],
verb=TR.buttons__continue,
verb_info=TR.buttons__info,
@@ -611,18 +612,14 @@ async def confirm_payment_request(
else:
await confirm_properties(
"confirm_payment_request",
- (TR.words__swap if is_swap else TR.words__confirm),
+ title,
[(TR.words__provider, recipient_name, True)],
)
- for sell_amount, buy_amount, t_address, t_account, t_account_path in trades:
+ for trade in trades:
await confirm_trade(
- f"{TR.words__swap} {TR.words__assets}",
- sell_amount,
- buy_amount,
- t_address,
- t_account,
- t_account_path,
+ f"{title} {TR.words__assets}",
+ trade,
extra_menu_items or [],
)
@@ -1034,19 +1031,15 @@ def confirm_total(
async def confirm_trade(
title: str,
- sell_amount: str | None,
- buy_amount: str,
- address: str,
- account: str | None,
- account_path: str | None,
+ trade: Trade,
extra_menu_items: list[tuple[str, str]],
) -> None:
from trezor.ui.layouts.menu import Menu, confirm_with_menu
items = []
- if sell_amount is not None:
- items.append(("", sell_amount, True))
- items.append(("", buy_amount, True))
+ if trade.sell_amount is not None:
+ items.append(("", trade.sell_amount, True))
+ items.append(("", trade.buy_amount, True))
trade_layout = trezorui_api.confirm_properties(
title=title,
items=items,
@@ -1054,11 +1047,13 @@ async def confirm_trade(
external_menu=True,
)
- account_items: list[PropertyType] = [("", address, None)]
- if account:
- account_items.append((TR.words__account, account, None))
- if account_path:
- account_items.append((TR.address_details__derivation_path, account_path, None))
+ account_items: list[PropertyType] = [("", trade.address, None)]
+ if trade.account:
+ account_items.append((TR.words__account, trade.account, None))
+ if trade.account_path:
+ account_items.append(
+ (TR.address_details__derivation_path, trade.account_path, None)
+ )
menu_items = [create_details(TR.address__title_receive_address, account_items)]
for k, v in extra_menu_items:
menu_items.append(create_details(k, v))
diff --git a/core/src/trezor/ui/layouts/delizia/__init__.py b/core/src/trezor/ui/layouts/delizia/__init__.py
index 34e7ef5f..7a61aac6 100644
--- a/core/src/trezor/ui/layouts/delizia/__init__.py
+++ b/core/src/trezor/ui/layouts/delizia/__init__.py
@@ -15,6 +15,7 @@ if TYPE_CHECKING:
from ..common import ExceptionType, PropertyType
from ..menu import Details
+ from ..slip24 import Refund, Trade
T = TypeVar("T")
@@ -487,8 +488,8 @@ async def confirm_payment_request(
recipient_name: str,
recipient_address: str | None,
texts: Iterable[tuple[str | None, str]],
- refunds: Iterable[tuple[str, str | None, str | None]],
- trades: list[tuple[str | None, str, str, str | None, str | None]],
+ refunds: Iterable[Refund],
+ trades: list[Trade],
account_items: list[PropertyType] | None,
transaction_fee: str | None,
fee_info_items: Iterable[PropertyType] | None,
@@ -496,14 +497,14 @@ async def confirm_payment_request(
) -> None:
from trezor.ui.layouts.menu import Menu, confirm_with_menu
- is_swap = len(trades) != 0 and all(
- sell_amount is not None for sell_amount, _, _, _, _ in trades
- )
+ from ..slip24 import is_swap
+
+ title = TR.words__swap if is_swap(trades) else TR.words__confirm
- for title, text in texts:
+ for t, text in texts:
await raise_if_not_confirmed(
trezorui_api.confirm_value(
- title=(title or (TR.words__swap if is_swap else TR.words__confirm)),
+ title=t or title,
value=text,
is_data=False,
description=None,
@@ -512,7 +513,7 @@ async def confirm_payment_request(
)
main_layout = trezorui_api.confirm_value(
- title=(TR.words__swap if is_swap else TR.words__confirm),
+ title=title,
subtitle=TR.words__provider,
value=recipient_name,
description=None,
@@ -527,13 +528,13 @@ async def confirm_payment_request(
menu_items.append(
create_details(TR.address__title_provider_address, recipient_address)
)
- for r_address, r_account, r_account_path in refunds:
- refund_account_items: list[PropertyType] = [("", r_address, None)]
- if r_account:
- refund_account_items.append((TR.words__account, r_account, None))
- if r_account_path:
+ for refund in refunds:
+ refund_account_items: list[PropertyType] = [("", refund.address, None)]
+ if refund.account:
+ refund_account_items.append((TR.words__account, refund.account, None))
+ if refund.account_path:
refund_account_items.append(
- (TR.address_details__derivation_path, r_account_path, None)
+ (TR.address_details__derivation_path, refund.account_path, None)
)
menu_items.append(
create_details(
@@ -545,15 +546,11 @@ async def confirm_payment_request(
await confirm_with_menu(main_layout, menu, "confirm_payment_request")
- for sell_amount, buy_amount, t_address, t_account, t_account_path in trades:
+ for trade in trades:
await confirm_trade(
- TR.words__swap if is_swap else TR.words__confirm,
+ title,
TR.words__assets,
- sell_amount,
- buy_amount,
- t_address,
- t_account,
- t_account_path,
+ trade,
extra_menu_items or [],
)
@@ -928,11 +925,7 @@ def _confirm_summary(
async def confirm_trade(
title: str,
subtitle: str,
- sell_amount: str | None,
- buy_amount: str,
- address: str,
- account: str | None,
- account_path: str | None,
+ trade: Trade,
extra_menu_items: list[tuple[str, str]],
) -> None:
from trezor.ui.layouts.menu import Menu, confirm_with_menu
@@ -940,15 +933,17 @@ async def confirm_trade(
trade_layout = trezorui_api.confirm_trade(
title=title,
subtitle=subtitle,
- sell_amount=sell_amount,
- buy_amount=buy_amount,
+ sell_amount=trade.sell_amount,
+ buy_amount=trade.buy_amount,
)
- account_items: list[PropertyType] = [("", address, None)]
- if account:
- account_items.append((TR.words__account, account, None))
- if account_path:
- account_items.append((TR.address_details__derivation_path, account_path, None))
+ account_items: list[PropertyType] = [("", trade.address, None)]
+ if trade.account:
+ account_items.append((TR.words__account, trade.account, None))
+ if trade.account_path:
+ account_items.append(
+ (TR.address_details__derivation_path, trade.account_path, None)
+ )
menu_items = [create_details(TR.address__title_receive_address, account_items)]
for k, v in extra_menu_items:
menu_items.append(create_details(k, v))
diff --git a/core/src/trezor/ui/layouts/eckhart/__init__.py b/core/src/trezor/ui/layouts/eckhart/__init__.py
index f174cfc7..fa2fbe21 100644
--- a/core/src/trezor/ui/layouts/eckhart/__init__.py
+++ b/core/src/trezor/ui/layouts/eckhart/__init__.py
@@ -15,6 +15,7 @@ if TYPE_CHECKING:
from trezor.ui.layouts.menu import Details
from ..common import ExceptionType, PropertyType
+ from ..slip24 import Refund, Trade
T = TypeVar("T")
@@ -448,8 +449,8 @@ async def confirm_payment_request(
recipient_name: str,
recipient_address: str | None,
texts: Iterable[tuple[str | None, str]],
- refunds: Iterable[tuple[str, str | None, str | None]],
- trades: list[tuple[str | None, str, str, str | None, str | None]],
+ refunds: Iterable[Refund],
+ trades: list[Trade],
account_items: list[PropertyType],
transaction_fee: str | None,
fee_info_items: Iterable[PropertyType] | None,
@@ -457,14 +458,18 @@ async def confirm_payment_request(
) -> None:
from trezor.ui.layouts.menu import Menu, confirm_with_menu
- is_swap = len(trades) != 0 and all(
- sell_amount is not None for sell_amount, _, _, _, _ in trades
+ from ..slip24 import is_swap
+
+ (title, summary_title) = (
+ (TR.words__swap, TR.words__swap)
+ if is_swap(trades)
+ else (TR.words__confirm, TR.words__title_summary)
)
- for title, text in texts:
+ for t, text in texts:
await raise_if_not_confirmed(
trezorui_api.confirm_value(
- title=(title or (TR.words__swap if is_swap else TR.words__confirm)),
+ title=t or title,
value=text,
description=None,
verb=TR.buttons__confirm,
@@ -473,7 +478,7 @@ async def confirm_payment_request(
)
main_layout = trezorui_api.confirm_value(
- title=(TR.words__swap if is_swap else TR.words__confirm),
+ title=title,
subtitle=TR.words__provider,
value=recipient_name,
description=None,
@@ -488,13 +493,13 @@ async def confirm_payment_request(
menu_items.append(
create_details(TR.address__title_provider_address, recipient_address)
)
- for r_address, r_account, r_account_path in refunds:
- refund_account_info: list[PropertyType] = [(str(""), r_address, True)]
- if r_account:
- refund_account_info.append((TR.words__account, r_account, True))
- if r_account_path:
+ for refund in refunds:
+ refund_account_info: list[PropertyType] = [(str(""), refund.address, True)]
+ if refund.account:
+ refund_account_info.append((TR.words__account, refund.account, True))
+ if refund.account_path:
refund_account_info.append(
- (TR.address_details__derivation_path, r_account_path, True)
+ (TR.address_details__derivation_path, refund.account_path, True)
)
menu_items.append(
create_details(
@@ -517,21 +522,11 @@ async def confirm_payment_request(
# but in theory it can (since SLIP-24 supports multiple CoinPurchaseMemos...)
can_go_back_from_trade = len(trades) == 1
- for (
- sell_amount,
- buy_amount,
- t_address,
- t_account,
- t_account_path,
- ) in trades:
+ for trade in trades:
res = await confirm_trade(
- TR.words__swap if is_swap else TR.words__confirm,
+ title,
TR.words__assets,
- sell_amount,
- buy_amount,
- t_address,
- t_account,
- t_account_path,
+ trade,
extra_menu_items or [],
can_go_back_from_trade,
)
@@ -548,7 +543,7 @@ async def confirm_payment_request(
amount_label=None,
fee=transaction_fee,
fee_label=TR.words__transaction_fee,
- title=TR.words__swap if is_swap else TR.words__title_summary,
+ title=summary_title,
account_items=account_items,
extra_items=fee_info_items,
extra_title=TR.confirm_total__title_fee,
@@ -931,11 +926,7 @@ def _confirm_summary(
def confirm_trade(
title: str,
subtitle: str,
- sell_amount: str | None,
- buy_amount: str,
- address: str,
- account: str | None,
- account_path: str | None,
+ trade: Trade,
extra_menu_items: list[tuple[str, str]],
back_button: bool,
) -> Awaitable[ui.UiResult]:
@@ -944,16 +935,18 @@ def confirm_trade(
trade_layout = trezorui_api.confirm_trade(
title=title,
subtitle=subtitle,
- sell_amount=sell_amount,
- buy_amount=buy_amount,
+ sell_amount=trade.sell_amount,
+ buy_amount=trade.buy_amount,
back_button=back_button,
)
- account_info: list[PropertyType] = [("", address, True)]
- if account:
- account_info.append((TR.words__account, account, True))
- if account_path:
- account_info.append((TR.address_details__derivation_path, account_path, True))
+ account_info: list[PropertyType] = [("", trade.address, True)]
+ if trade.account:
+ account_info.append((TR.words__account, trade.account, True))
+ if trade.account_path:
+ account_info.append(
+ (TR.address_details__derivation_path, trade.account_path, True)
+ )
menu_items = [create_details(TR.address__title_receive_address, account_info)]
for k, v in extra_menu_items:
menu_items.append(create_details(k, v))
diff --git a/core/src/trezor/ui/layouts/slip24.py b/core/src/trezor/ui/layouts/slip24.py
new file mode 100644
index 00000000..97ea81c6
--- /dev/null
+++ b/core/src/trezor/ui/layouts/slip24.py
@@ -0,0 +1,36 @@
+class Refund:
+ def __init__(
+ self,
+ address: str,
+ account: str | None,
+ account_path: str | None,
+ ) -> None:
+ self.address = address
+ self.account = account
+ self.account_path = account_path
+
+
+class Trade:
+ def __init__(
+ self,
+ sell_amount: str | None,
+ buy_amount: str,
+ address: str,
+ account: str | None,
+ account_path: str | None,
+ ) -> None:
+ if sell_amount is not None:
+ assert sell_amount.startswith("-")
+ assert buy_amount.startswith("+")
+
+ self.sell_amount = sell_amount
+ self.buy_amount = buy_amount
+ self.address = address
+ self.account = account
+ self.account_path = account_path
+
+
+def is_swap(trades: list[Trade]) -> bool:
+ assert sum(t.sell_amount is None for t in trades) in (0, len(trades))
+
+ return any(t.sell_amount is not None for t in trades)
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.