feat(stellar): show network in signing flows
What changed, and why it matters
This commit improves the Stellar signing experience on Trezor hardware wallets by letting users see which network (Mainnet, Testnet, or Futurenet) a transaction is for before approving it. Previously, the network was intentionally hidden from the main confirmation flow after a redesign, which could lead someone with accounts on both mainnet and testnet to accidentally approve a mainnet transaction they only intended for testnet. The fix adds the network name to an info menu on the final review screen for both regular Stellar transactions and Soroban authorizations. It also adds support for the Stellar Futurenet network passphrase. This is a security/usability improvement rather than a remote-exploitable vulnerability.
No urgent action required. This is a defensive improvement. Users should update to firmware containing this commit if they interact with Stellar on multiple networks, and verify the network shown in the signing info screen before approving transactions.
Security signals we found
UI/UX security improvement: prevents user confusion between mainnet and testnet during signing
Adds missing network indicator to transaction and authorization final confirmation screens
Adds support for previously unrecognized Futurenet network passphrase
Changelog explicitly tagged as security-relevant (.security fragment)
Fixes an internal issue about users with accounts on multiple networks
Evidence from the diff
The patch modifies the Stellar app in Trezor firmware. It adds NETWORK_PASSPHRASE_FUTURENET to core/src/apps/stellar/consts.py and introduces _get_network_name() in core/src/apps/stellar/layout.py to map known network passphrases to human-readable labels. The final confirmation helpers are renamed from require_confirm_final/require_confirm_signature_expiration_ledger to confirm_tx_final/confirm_auth_final and now accept and display the network_passphrase as an info item. The callers in sign_tx.py and sign_soroban_authorization.py pass msg.network_passphrase. Across four UI layout backends (bolt, caesar, delizia, eckhart), the extra_title parameter for confirm_stellar_tx is removed so the network/timebounds info can be shown via extra_items instead. A changelog fragment marks this as a security-relevant change.
Changed components
core/src/apps/stellar/consts.pycore/src/apps/stellar/layout.pycore/src/apps/stellar/sign_soroban_authorization.pycore/src/apps/stellar/sign_tx.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__.pyInspect captured patch +27 / −10
### core/.changelog.d/+stellar_network.security
@@ -0,0 +1 @@
+Stellar: allow viewing network in signing flows.
### core/src/apps/stellar/consts.py
@@ -69,9 +69,10 @@
# https://github.com/stellar/go/blob/3d2c1defe73dbfed00146ebe0e8d7e07ce4bb1b6/amount/main.go#L23
AMOUNT_DECIMALS = const(7)
-# https://github.com/stellar/go/blob/master/network/main.go
+# https://developers.stellar.org/docs/networks
NETWORK_PASSPHRASE_PUBLIC = "Public Global Stellar Network ; September 2015"
NETWORK_PASSPHRASE_TESTNET = "Test SDF Network ; September 2015"
+NETWORK_PASSPHRASE_FUTURENET = "Test SDF Future Network ; October 2022"
# https://www.stellar.org/developers/guides/concepts/accounts.html#flags
FLAG_AUTH_REQUIRED = const(1)
### core/src/apps/stellar/layout.py
@@ -134,11 +134,23 @@ async def require_confirm_payment_request(
)
-async def require_confirm_final(
+def _get_network_name(network_passphrase: str) -> str:
+ from . import consts
+
+ _KNOWN_NETWORKS = {
+ consts.NETWORK_PASSPHRASE_PUBLIC: "Mainnet",
+ consts.NETWORK_PASSPHRASE_TESTNET: "Testnet",
+ consts.NETWORK_PASSPHRASE_FUTURENET: "Futurenet",
+ }
+ return _KNOWN_NETWORKS.get(network_passphrase, f"Unknown ({network_passphrase})")
+
+
+async def confirm_tx_final(
address_n: list[int],
fee: int,
timebounds: tuple[int, int],
is_sending_from_trezor_account: bool,
+ network_passphrase: str,
) -> None:
from trezor.wire import DataError
@@ -166,6 +178,7 @@ async def require_confirm_final(
),
None,
),
+ (TR.words__network, _get_network_name(network_passphrase), None),
)
account_name = paths.get_account_name("Stellar", address_n, PATTERN, SLIP44_ID)
@@ -234,8 +247,9 @@ async def require_confirm_auth_on_behalf_of(address: str) -> None:
)
-async def require_confirm_signature_expiration_ledger(
+async def confirm_auth_final(
signature_expiration_ledger: int,
+ network_passphrase: str,
) -> None:
await layouts.confirm_value(
title=TR.stellar__sign_authorization,
@@ -245,6 +259,9 @@ async def require_confirm_signature_expiration_ledger(
br_code=ButtonRequestType.SignTx,
hold=True,
is_data=False,
+ info_items=[
+ (TR.words__network, _get_network_name(network_passphrase), None),
+ ],
)
### core/src/apps/stellar/sign_soroban_authorization.py
@@ -66,8 +66,9 @@ async def sign_soroban_authorization(
await layout.confirm_authorized_invocation(
auth.invocation, network_id, auth.address
)
- await layout.require_confirm_signature_expiration_ledger(
- auth.signature_expiration_ledger
+ await layout.confirm_auth_final(
+ auth.signature_expiration_ledger,
+ msg.network_passphrase,
)
payload = sha256(w).digest()
### core/src/apps/stellar/sign_tx.py
@@ -218,11 +218,12 @@ async def sign_tx(msg: StellarSignTx, keychain: Slip21Keychain) -> StellarSigned
)
# final confirm
- await layout.require_confirm_final(
+ await layout.confirm_tx_final(
msg.address_n,
msg.fee,
(msg.timebounds_start, msg.timebounds_end),
is_sending_from_trezor_account,
+ msg.network_passphrase,
)
# sign
### core/src/trezor/ui/layouts/bolt/__init__.py
@@ -1725,7 +1725,6 @@ def confirm_stellar_tx(
else TR.stellar__sign_with
),
extra_items=extra_items,
- extra_title=TR.words__title_information, # The performance on T2T1 is different from other devices, so we do not use TR.stellar__timebounds here.
br_name="confirm_stellar_tx",
br_code=ButtonRequestType.SignTx,
)
### core/src/trezor/ui/layouts/caesar/__init__.py
@@ -1815,7 +1815,6 @@ async def confirm_stellar_tx(
else TR.stellar__sign_with
),
extra_items=with_colon(extra_items),
- extra_title=TR.stellar__timebounds,
) as layout:
return await raise_if_not_confirmed(
layout,
### core/src/trezor/ui/layouts/delizia/__init__.py
@@ -1748,7 +1748,6 @@ def confirm_stellar_tx(
else TR.stellar__sign_with
),
extra_items=extra_items,
- extra_title=TR.stellar__timebounds,
br_name="confirm_stellar_tx",
br_code=ButtonRequestType.SignTx,
)
### core/src/trezor/ui/layouts/eckhart/__init__.py
@@ -1870,7 +1870,6 @@ def confirm_stellar_tx(
else TR.stellar__sign_with
),
extra_items=extra_items,
- extra_title=TR.stellar__timebounds,
br_name="confirm_stellar_tx",
br_code=ButtonRequestType.SignTx,
)Why this scored 30/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.