refactor(core/stellar): move generic Soroban writers to writers.py.
What changed, and why it matters
This commit is a straightforward code cleanup: it moves several Stellar Soroban serialization helper functions from an operation-specific file into a shared writers module so both transaction signing and standalone authorization signing can use the same code. No behavior changes, bug fixes, or security fixes are visible in the diff.
No security action required. Treat as normal refactoring review.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The change refactors Stellar Soroban XDR writer functions. Functions such as write_invoke_contract_args, write_sc_address, write_sc_val, write_soroban_authorized_invocation, and a generic write_vec helper are relocated from core/src/apps/stellar/operations/serialize.py to core/src/apps/stellar/writers.py. Call sites in operations/layout.py, operations/serialize.py, sign_soroban_authorization.py, and the test file are updated to import from the new location. The implementations are copied verbatim with only visibility changes (some previously private write functions become public write_).
Changed components
core/src/apps/stellar/operations/layout.pycore/src/apps/stellar/operations/serialize.pycore/src/apps/stellar/sign_soroban_authorization.pycore/src/apps/stellar/writers.pycore/tests/test_apps.stellar.writers.pyInspect captured patch +221 / −218
diff --git a/core/src/apps/stellar/operations/layout.py b/core/src/apps/stellar/operations/layout.py
index 86ad74a6..9d0cf101 100644
--- a/core/src/apps/stellar/operations/layout.py
+++ b/core/src/apps/stellar/operations/layout.py
@@ -472,7 +472,7 @@ def _is_root_auth_entry(
StellarSorobanAuthorizedFunctionType,
)
- from .serialize import write_invoke_contract_args
+ from ..writers import write_invoke_contract_args
auth_fn = auth_entry.root_invocation.function
diff --git a/core/src/apps/stellar/operations/serialize.py b/core/src/apps/stellar/operations/serialize.py
index 4dc91272..3dc53362 100644
--- a/core/src/apps/stellar/operations/serialize.py
+++ b/core/src/apps/stellar/operations/serialize.py
@@ -6,17 +6,20 @@ from trezor.wire import DataError, ProcessError
from ..writers import (
write_bool,
write_bytes_fixed,
- write_int32,
write_int64,
+ write_invoke_contract_args,
write_pubkey,
+ write_sc_address,
+ write_sc_val,
+ write_soroban_authorized_invocation,
write_string,
write_uint32,
write_uint64,
+ write_vec,
)
if TYPE_CHECKING:
from buffer_types import AnyBytes
- from typing import Callable, TypeVar
from trezor.messages import (
StellarAccountMergeOp,
@@ -28,9 +31,6 @@ if TYPE_CHECKING:
StellarCreateAccountOp,
StellarCreatePassiveSellOfferOp,
StellarHostFunction,
- StellarInt128Parts,
- StellarInt256Parts,
- StellarInvokeContractArgs,
StellarInvokeHostFunctionOp,
StellarManageBuyOfferOp,
StellarManageDataOp,
@@ -38,29 +38,13 @@ if TYPE_CHECKING:
StellarPathPaymentStrictReceiveOp,
StellarPathPaymentStrictSendOp,
StellarPaymentOp,
- StellarSCVal,
- StellarSCValMapEntry,
StellarSetOptionsOp,
StellarSorobanAddressCredentials,
StellarSorobanAuthorizationEntry,
- StellarSorobanAuthorizedFunction,
- StellarSorobanAuthorizedInvocation,
StellarSorobanCredentials,
- StellarUInt128Parts,
- StellarUInt256Parts,
)
from trezor.utils import Writer
- T = TypeVar("T")
-
-
-def _write_vec(
- w: Writer, items: list[T], write_item: Callable[[Writer, T], None]
-) -> None:
- write_uint32(w, len(items))
- for item in items:
- write_item(w, item)
-
def write_account_merge_op(w: Writer, msg: StellarAccountMergeOp) -> None:
write_pubkey(w, msg.destination_account)
@@ -136,7 +120,7 @@ def write_path_payment_strict_receive_op(
_write_asset(w, msg.destination_asset)
write_uint64(w, msg.destination_amount)
- _write_vec(w, msg.paths, _write_asset)
+ write_vec(w, msg.paths, _write_asset)
def write_path_payment_strict_send_op(
@@ -148,7 +132,7 @@ def write_path_payment_strict_send_op(
_write_asset(w, msg.destination_asset)
write_uint64(w, msg.destination_min)
- _write_vec(w, msg.paths, _write_asset)
+ write_vec(w, msg.paths, _write_asset)
def write_payment_op(w: Writer, msg: StellarPaymentOp) -> None:
@@ -265,7 +249,7 @@ def _write_claimable_balance_id(w: Writer, claimable_balance_id: AnyBytes) -> No
def write_invoke_host_function_op(w: Writer, msg: StellarInvokeHostFunctionOp) -> None:
_write_host_function(w, msg.function)
- _write_vec(w, msg.auth, _write_soroban_authorization_entry)
+ write_vec(w, msg.auth, _write_soroban_authorization_entry)
def _write_host_function(w: Writer, msg: StellarHostFunction) -> None:
@@ -280,166 +264,6 @@ def _write_host_function(w: Writer, msg: StellarHostFunction) -> None:
raise ProcessError("Stellar: unsupported host function type")
-def write_invoke_contract_args(w: Writer, msg: StellarInvokeContractArgs) -> None:
- write_sc_address(w, msg.contract_address)
- _write_sc_symbol(w, msg.function_name)
- _write_vec(w, msg.args, _write_sc_val)
-
-
-def write_sc_address(w: Writer, addr: str) -> None:
- from .. import helpers
-
- version, data = helpers.decode_strkey(addr)
-
- if version == helpers.STRKEY_ED25519_PUBLIC_KEY:
- # AccountID is a PublicKey: KEY_TYPE_ED25519 (0) + 32 bytes ed25519
- write_uint32(w, 0) # SC_ADDRESS_TYPE_ACCOUNT
- write_uint32(w, 0) # KEY_TYPE_ED25519
- write_bytes_fixed(w, data, 32)
- elif version == helpers.STRKEY_CONTRACT:
- # ContractID is a Hash (32 bytes)
- write_uint32(w, 1) # SC_ADDRESS_TYPE_CONTRACT
- write_bytes_fixed(w, data, 32)
- elif version == helpers.STRKEY_MUXED_ACCOUNT:
- # MuxedEd25519Account: { id: uint64, ed25519: uint256 }
- # address format: 32 bytes ed25519 + 8 bytes id
- write_uint32(w, 2) # SC_ADDRESS_TYPE_MUXED_ACCOUNT
- write_bytes_fixed(w, data[32:40], 8) # id (uint64)
- write_bytes_fixed(w, data[0:32], 32) # ed25519
- elif version == helpers.STRKEY_CLAIMABLE_BALANCE:
- # ClaimableBalanceID: { type: uint32, v0: Hash }
- # address format: 1 byte type + 32 bytes hash (from strkey decoding);
- # decode_strkey has already checked that the type byte is v0
- write_uint32(w, 3) # SC_ADDRESS_TYPE_CLAIMABLE_BALANCE
- write_uint32(w, 0) # CLAIMABLE_BALANCE_ID_TYPE_V0
- write_bytes_fixed(w, data[1:33], 32) # v0 hash
- elif version == helpers.STRKEY_LIQUIDITY_POOL:
- # PoolID is a Hash (32 bytes)
- write_uint32(w, 4) # SC_ADDRESS_TYPE_LIQUIDITY_POOL
- write_bytes_fixed(w, data, 32)
- else:
- raise ProcessError("Stellar: unsupported SC address type")
-
-
-def _write_sc_symbol(w: Writer, symbol: str) -> None:
- from .. import consts
-
- written = write_string(w, symbol)
- if written > consts.SCSYMBOL_MAX_SIZE:
- raise DataError("Stellar: symbol too long")
-
-
-def _write_sc_val(w: Writer, msg: StellarSCVal) -> None:
- from trezor.enums import StellarSCValType
-
- write_uint32(w, msg.type)
-
- if msg.type == StellarSCValType.SCV_BOOL:
- if msg.b is None:
- raise DataError("Stellar: missing bool value")
- write_bool(w, msg.b)
- elif msg.type == StellarSCValType.SCV_VOID:
- pass # no data
- elif msg.type == StellarSCValType.SCV_U32:
- if msg.u32 is None:
- raise DataError("Stellar: missing u32 value")
- write_uint32(w, msg.u32)
- elif msg.type == StellarSCValType.SCV_I32:
- if msg.i32 is None:
- raise DataError("Stellar: missing i32 value")
- write_int32(w, msg.i32)
- elif msg.type == StellarSCValType.SCV_U64:
- if msg.u64 is None:
- raise DataError("Stellar: missing u64 value")
- write_uint64(w, msg.u64)
- elif msg.type == StellarSCValType.SCV_I64:
- if msg.i64 is None:
- raise DataError("Stellar: missing i64 value")
- write_int64(w, msg.i64)
- elif msg.type == StellarSCValType.SCV_TIMEPOINT:
- if msg.timepoint is None:
- raise DataError("Stellar: missing timepoint value")
- write_uint64(w, msg.timepoint)
- elif msg.type == StellarSCValType.SCV_DURATION:
- if msg.duration is None:
- raise DataError("Stellar: missing duration value")
- write_uint64(w, msg.duration)
- elif msg.type == StellarSCValType.SCV_U128:
- if msg.u128 is None:
- raise DataError("Stellar: missing u128 value")
- _write_uint128_parts(w, msg.u128)
- elif msg.type == StellarSCValType.SCV_I128:
- if msg.i128 is None:
- raise DataError("Stellar: missing i128 value")
- _write_int128_parts(w, msg.i128)
- elif msg.type == StellarSCValType.SCV_U256:
- if msg.u256 is None:
- raise DataError("Stellar: missing u256 value")
- _write_uint256_parts(w, msg.u256)
- elif msg.type == StellarSCValType.SCV_I256:
- if msg.i256 is None:
- raise DataError("Stellar: missing i256 value")
- _write_int256_parts(w, msg.i256)
- elif msg.type == StellarSCValType.SCV_BYTES:
- if msg.bytes is None:
- raise DataError("Stellar: missing bytes value")
- write_string(w, msg.bytes)
- elif msg.type == StellarSCValType.SCV_STRING:
- if msg.string is None:
- raise DataError("Stellar: missing string value")
- write_string(w, msg.string)
- elif msg.type == StellarSCValType.SCV_SYMBOL:
- if msg.symbol is None:
- raise DataError("Stellar: missing symbol value")
- _write_sc_symbol(w, msg.symbol)
- elif msg.type == StellarSCValType.SCV_VEC:
- # In XDR the vector is a pointer (SCVec*), i.e. nullable, but a null vector
- # is not a valid Soroban value (only Some([...]), possibly empty). Here it
- # is a `repeated` field that is always a list, never None, so encoding it
- # as present is correct.
- write_bool(w, True) # present
- _write_vec(w, msg.vec, _write_sc_val)
- elif msg.type == StellarSCValType.SCV_MAP:
- # map is a pointer (SCMap*) in XDR; same reasoning as SCV_VEC above.
- write_bool(w, True) # present
- _write_vec(w, msg.map, _write_sc_map_entry)
- elif msg.type == StellarSCValType.SCV_ADDRESS:
- if msg.address is None:
- raise DataError("Stellar: missing address value")
- write_sc_address(w, msg.address)
- else:
- raise ProcessError("Stellar: unsupported SCVal type")
-
-
-def _write_sc_map_entry(w: Writer, entry: StellarSCValMapEntry) -> None:
- _write_sc_val(w, entry.key)
- _write_sc_val(w, entry.value)
-
-
-def _write_uint128_parts(w: Writer, msg: StellarUInt128Parts) -> None:
- write_uint64(w, msg.hi)
- write_uint64(w, msg.lo)
-
-
-def _write_int128_parts(w: Writer, msg: StellarInt128Parts) -> None:
- write_int64(w, msg.hi)
- write_uint64(w, msg.lo)
-
-
-def _write_uint256_parts(w: Writer, msg: StellarUInt256Parts) -> None:
- write_uint64(w, msg.hi_hi)
- write_uint64(w, msg.hi_lo)
- write_uint64(w, msg.lo_hi)
- write_uint64(w, msg.lo_lo)
-
-
-def _write_int256_parts(w: Writer, msg: StellarInt256Parts) -> None:
- write_int64(w, msg.hi_hi)
- write_uint64(w, msg.hi_lo)
- write_uint64(w, msg.lo_hi)
- write_uint64(w, msg.lo_lo)
-
-
def _write_soroban_authorization_entry(
w: Writer, msg: StellarSorobanAuthorizationEntry
) -> None:
@@ -467,28 +291,4 @@ def _write_soroban_address_credentials(
write_sc_address(w, msg.address)
write_int64(w, msg.nonce)
write_uint32(w, msg.signature_expiration_ledger)
- _write_sc_val(w, msg.signature)
-
-
-def write_soroban_authorized_invocation(
- w: Writer, msg: StellarSorobanAuthorizedInvocation
-) -> None:
- _write_soroban_authorized_function(w, msg.function)
- _write_vec(w, msg.sub_invocations, write_soroban_authorized_invocation)
-
-
-def _write_soroban_authorized_function(
- w: Writer, msg: StellarSorobanAuthorizedFunction
-) -> None:
- from trezor.enums import StellarSorobanAuthorizedFunctionType
-
- write_uint32(w, msg.type)
- if (
- msg.type
- == StellarSorobanAuthorizedFunctionType.SOROBAN_AUTHORIZED_FUNCTION_TYPE_CONTRACT_FN
- ):
- if msg.contract_fn is None:
- raise DataError("Stellar: missing contract_fn")
- write_invoke_contract_args(w, msg.contract_fn)
- else:
- raise ProcessError("Stellar: unsupported authorized function type")
+ write_sc_val(w, msg.signature)
diff --git a/core/src/apps/stellar/sign_soroban_authorization.py b/core/src/apps/stellar/sign_soroban_authorization.py
index 22bbeefd..468679bc 100644
--- a/core/src/apps/stellar/sign_soroban_authorization.py
+++ b/core/src/apps/stellar/sign_soroban_authorization.py
@@ -27,10 +27,6 @@ async def sign_soroban_authorization(
from . import helpers, layout, writers
from .operations.layout import confirm_authorized_invocation
- from .operations.serialize import (
- write_sc_address,
- write_soroban_authorized_invocation,
- )
# Only the address-bound preimage variant introduced in Protocol 27 is supported
if (
@@ -58,8 +54,8 @@ async def sign_soroban_authorization(
)
writers.write_int64(w, auth.nonce)
writers.write_uint32(w, auth.signature_expiration_ledger)
- write_sc_address(w, auth.address)
- write_soroban_authorized_invocation(w, auth.invocation)
+ writers.write_sc_address(w, auth.address)
+ writers.write_soroban_authorized_invocation(w, auth.invocation)
await layout.require_confirm_auth_signing_address(signing_address, msg.address_n)
diff --git a/core/src/apps/stellar/writers.py b/core/src/apps/stellar/writers.py
index 06b7bfe5..56bf16bc 100644
--- a/core/src/apps/stellar/writers.py
+++ b/core/src/apps/stellar/writers.py
@@ -1,6 +1,8 @@
from micropython import const
from typing import TYPE_CHECKING
+from trezor.wire import DataError, ProcessError
+
import apps.common.writers as writers
# Reexporting to other modules
@@ -11,9 +13,23 @@ write_uint64 = writers.write_uint64_be
if TYPE_CHECKING:
from buffer_types import StrOrBytes
+ from typing import Callable, TypeVar
+ from trezor.messages import (
+ StellarInt128Parts,
+ StellarInt256Parts,
+ StellarInvokeContractArgs,
+ StellarSCVal,
+ StellarSCValMapEntry,
+ StellarSorobanAuthorizedFunction,
+ StellarSorobanAuthorizedInvocation,
+ StellarUInt128Parts,
+ StellarUInt256Parts,
+ )
from trezor.utils import Writer
+ T = TypeVar("T")
+
def write_string(w: Writer, s: StrOrBytes) -> int:
"""Write XDR string padded to a multiple of 4 bytes.
@@ -64,3 +80,195 @@ def write_int64(w: Writer, value: int) -> None:
if value < _INT64_MIN or value > _INT64_MAX:
raise ValueError("int64 out of range")
write_uint64(w, value & _UINT64_MASK)
+
+
+def write_vec(
+ w: Writer, items: list[T], write_item: Callable[[Writer, T], None]
+) -> None:
+ write_uint32(w, len(items))
+ for item in items:
+ write_item(w, item)
+
+
+def write_invoke_contract_args(w: Writer, msg: StellarInvokeContractArgs) -> None:
+ write_sc_address(w, msg.contract_address)
+ _write_sc_symbol(w, msg.function_name)
+ write_vec(w, msg.args, write_sc_val)
+
+
+def write_sc_address(w: Writer, addr: str) -> None:
+ from . import helpers
+
+ version, data = helpers.decode_strkey(addr)
+
+ if version == helpers.STRKEY_ED25519_PUBLIC_KEY:
+ # AccountID is a PublicKey: KEY_TYPE_ED25519 (0) + 32 bytes ed25519
+ write_uint32(w, 0) # SC_ADDRESS_TYPE_ACCOUNT
+ write_uint32(w, 0) # KEY_TYPE_ED25519
+ write_bytes_fixed(w, data, 32)
+ elif version == helpers.STRKEY_CONTRACT:
+ # ContractID is a Hash (32 bytes)
+ write_uint32(w, 1) # SC_ADDRESS_TYPE_CONTRACT
+ write_bytes_fixed(w, data, 32)
+ elif version == helpers.STRKEY_MUXED_ACCOUNT:
+ # MuxedEd25519Account: { id: uint64, ed25519: uint256 }
+ # address format: 32 bytes ed25519 + 8 bytes id
+ write_uint32(w, 2) # SC_ADDRESS_TYPE_MUXED_ACCOUNT
+ write_bytes_fixed(w, data[32:40], 8) # id (uint64)
+ write_bytes_fixed(w, data[0:32], 32) # ed25519
+ elif version == helpers.STRKEY_CLAIMABLE_BALANCE:
+ # ClaimableBalanceID: { type: uint32, v0: Hash }
+ # address format: 1 byte type + 32 bytes hash (from strkey decoding);
+ # decode_strkey has already checked that the type byte is v0
+ write_uint32(w, 3) # SC_ADDRESS_TYPE_CLAIMABLE_BALANCE
+ write_uint32(w, 0) # CLAIMABLE_BALANCE_ID_TYPE_V0
+ write_bytes_fixed(w, data[1:33], 32) # v0 hash
+ elif version == helpers.STRKEY_LIQUIDITY_POOL:
+ # PoolID is a Hash (32 bytes)
+ write_uint32(w, 4) # SC_ADDRESS_TYPE_LIQUIDITY_POOL
+ write_bytes_fixed(w, data, 32)
+ else:
+ raise ProcessError("Stellar: unsupported SC address type")
+
+
+def _write_sc_symbol(w: Writer, symbol: str) -> None:
+ from . import consts
+
+ written = write_string(w, symbol)
+ if written > consts.SCSYMBOL_MAX_SIZE:
+ raise DataError("Stellar: symbol too long")
+
+
+def write_sc_val(w: Writer, msg: StellarSCVal) -> None:
+ from trezor.enums import StellarSCValType
+
+ write_uint32(w, msg.type)
+
+ if msg.type == StellarSCValType.SCV_BOOL:
+ if msg.b is None:
+ raise DataError("Stellar: missing bool value")
+ write_bool(w, msg.b)
+ elif msg.type == StellarSCValType.SCV_VOID:
+ pass # no data
+ elif msg.type == StellarSCValType.SCV_U32:
+ if msg.u32 is None:
+ raise DataError("Stellar: missing u32 value")
+ write_uint32(w, msg.u32)
+ elif msg.type == StellarSCValType.SCV_I32:
+ if msg.i32 is None:
+ raise DataError("Stellar: missing i32 value")
+ write_int32(w, msg.i32)
+ elif msg.type == StellarSCValType.SCV_U64:
+ if msg.u64 is None:
+ raise DataError("Stellar: missing u64 value")
+ write_uint64(w, msg.u64)
+ elif msg.type == StellarSCValType.SCV_I64:
+ if msg.i64 is None:
+ raise DataError("Stellar: missing i64 value")
+ write_int64(w, msg.i64)
+ elif msg.type == StellarSCValType.SCV_TIMEPOINT:
+ if msg.timepoint is None:
+ raise DataError("Stellar: missing timepoint value")
+ write_uint64(w, msg.timepoint)
+ elif msg.type == StellarSCValType.SCV_DURATION:
+ if msg.duration is None:
+ raise DataError("Stellar: missing duration value")
+ write_uint64(w, msg.duration)
+ elif msg.type == StellarSCValType.SCV_U128:
+ if msg.u128 is None:
+ raise DataError("Stellar: missing u128 value")
+ _write_uint128_parts(w, msg.u128)
+ elif msg.type == StellarSCValType.SCV_I128:
+ if msg.i128 is None:
+ raise DataError("Stellar: missing i128 value")
+ _write_int128_parts(w, msg.i128)
+ elif msg.type == StellarSCValType.SCV_U256:
+ if msg.u256 is None:
+ raise DataError("Stellar: missing u256 value")
+ _write_uint256_parts(w, msg.u256)
+ elif msg.type == StellarSCValType.SCV_I256:
+ if msg.i256 is None:
+ raise DataError("Stellar: missing i256 value")
+ _write_int256_parts(w, msg.i256)
+ elif msg.type == StellarSCValType.SCV_BYTES:
+ if msg.bytes is None:
+ raise DataError("Stellar: missing bytes value")
+ write_string(w, msg.bytes)
+ elif msg.type == StellarSCValType.SCV_STRING:
+ if msg.string is None:
+ raise DataError("Stellar: missing string value")
+ write_string(w, msg.string)
+ elif msg.type == StellarSCValType.SCV_SYMBOL:
+ if msg.symbol is None:
+ raise DataError("Stellar: missing symbol value")
+ _write_sc_symbol(w, msg.symbol)
+ elif msg.type == StellarSCValType.SCV_VEC:
+ # In XDR the vector is a pointer (SCVec*), i.e. nullable, but a null vector
+ # is not a valid Soroban value (only Some([...]), possibly empty). Here it
+ # is a `repeated` field that is always a list, never None, so encoding it
+ # as present is correct.
+ write_bool(w, True) # present
+ write_vec(w, msg.vec, write_sc_val)
+ elif msg.type == StellarSCValType.SCV_MAP:
+ # map is a pointer (SCMap*) in XDR; same reasoning as SCV_VEC above.
+ write_bool(w, True) # present
+ write_vec(w, msg.map, _write_sc_map_entry)
+ elif msg.type == StellarSCValType.SCV_ADDRESS:
+ if msg.address is None:
+ raise DataError("Stellar: missing address value")
+ write_sc_address(w, msg.address)
+ else:
+ raise ProcessError("Stellar: unsupported SCVal type")
+
+
+def _write_sc_map_entry(w: Writer, entry: StellarSCValMapEntry) -> None:
+ write_sc_val(w, entry.key)
+ write_sc_val(w, entry.value)
+
+
+def _write_uint128_parts(w: Writer, msg: StellarUInt128Parts) -> None:
+ write_uint64(w, msg.hi)
+ write_uint64(w, msg.lo)
+
+
+def _write_int128_parts(w: Writer, msg: StellarInt128Parts) -> None:
+ write_int64(w, msg.hi)
+ write_uint64(w, msg.lo)
+
+
+def _write_uint256_parts(w: Writer, msg: StellarUInt256Parts) -> None:
+ write_uint64(w, msg.hi_hi)
+ write_uint64(w, msg.hi_lo)
+ write_uint64(w, msg.lo_hi)
+ write_uint64(w, msg.lo_lo)
+
+
+def _write_int256_parts(w: Writer, msg: StellarInt256Parts) -> None:
+ write_int64(w, msg.hi_hi)
+ write_uint64(w, msg.hi_lo)
+ write_uint64(w, msg.lo_hi)
+ write_uint64(w, msg.lo_lo)
+
+
+def write_soroban_authorized_invocation(
+ w: Writer, msg: StellarSorobanAuthorizedInvocation
+) -> None:
+ _write_soroban_authorized_function(w, msg.function)
+ write_vec(w, msg.sub_invocations, write_soroban_authorized_invocation)
+
+
+def _write_soroban_authorized_function(
+ w: Writer, msg: StellarSorobanAuthorizedFunction
+) -> None:
+ from trezor.enums import StellarSorobanAuthorizedFunctionType
+
+ write_uint32(w, msg.type)
+ if (
+ msg.type
+ == StellarSorobanAuthorizedFunctionType.SOROBAN_AUTHORIZED_FUNCTION_TYPE_CONTRACT_FN
+ ):
+ if msg.contract_fn is None:
+ raise DataError("Stellar: missing contract_fn")
+ write_invoke_contract_args(w, msg.contract_fn)
+ else:
+ raise ProcessError("Stellar: unsupported authorized function type")
diff --git a/core/tests/test_apps.stellar.writers.py b/core/tests/test_apps.stellar.writers.py
index 195e93ce..82deca4a 100644
--- a/core/tests/test_apps.stellar.writers.py
+++ b/core/tests/test_apps.stellar.writers.py
@@ -4,8 +4,7 @@ from common import * # isort:skip
from trezor.wire import DataError
if not utils.BITCOIN_ONLY:
- from apps.stellar.operations.serialize import _write_sc_symbol
- from apps.stellar.writers import write_int32, write_int64
+ from apps.stellar.writers import _write_sc_symbol, write_int32, write_int64
@unittest.skipUnless(not utils.BITCOIN_ONLY, "altcoin")
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.