feat(common,core,python,tests): add support for StellarInvokeHostFunctionOp.
What changed, and why it matters
This commit adds support for signing Stellar Soroban smart-contract transactions on Trezor hardware wallets. It introduces new message types, on-device confirmation screens, and serialization logic for complex contract arguments and authorization trees. The change is a feature addition rather than a bug fix; it does not by itself fix a known vulnerability, but it does expand the attack surface of the Stellar signing flow and includes several security-relevant design choices (e.g., showing externally signed authorizations only on request).
Treat this as a high-touch feature addition requiring focused review of the Stellar operations layout and serialization code, especially the consistency between what is displayed and what is signed, handling of nested/recursive authorization trees, and canonical strkey validation. No immediate emergency response is indicated by the diff alone, but regression and fuzz testing of the new message types is warranted before release.
Security signals we found
New signing path for smart-contract operations with large, nested, user-supplied data structures
UI formatting logic explicitly tries to prevent delimiter forgery in displayed SCV_STRING/SYMBOL values
Authorization entries with ADDRESS credentials are not shown by default; user must opt in
Strkey decoder now enforces canonical base32 encoding, checksum, payload length, and version
Soroban operation constrained to be single operation, no memo, ext.v=1
soroban_data is passed through as opaque bytes and committed only via signed digest
Evidence from the diff
The patch extends the Trezor firmware and Python/Rust client stacks to handle StellarInvokeHostFunctionOp and related Soroban types (SCVal, host functions, authorization entries, transaction extensions). Core firmware now parses, displays for user confirmation, and serializes these structures into Stellar XDR. Notable implementation details: strkey decoding was generalized and canonicality is enforced by re-encoding; SCV_STRING/SYMBOL values are escaped/quoted before display to prevent UI forgery of vector/map separators; SOURCE_ACCOUNT auth entries are always shown, while ADDRESS-credential entries are hidden behind an opt-in ‘External Authorizations’ prompt; Soroban ops are restricted to being the sole operation and require ext.v=1 with soroban_data. Legacy (t1b1) devices skip these tests.
Changed components
core/src/apps/stellarcore/src/trezor/messages.pycore/src/trezor/enumscore/src/trezor/strings.pycommon/protob/messages-stellar.protopython/src/trezorlib/stellar.pyrust/trezor-client/src/protos/generated/messages_stellar.rsInspect captured patch +8918 / −1233
diff --git a/common/protob/messages-stellar.proto b/common/protob/messages-stellar.proto
index 71593829..461bd089 100644
--- a/common/protob/messages-stellar.proto
+++ b/common/protob/messages-stellar.proto
@@ -89,6 +89,8 @@ message StellarSignTx {
* @next StellarAccountMergeOp
* @next StellarManageDataOp
* @next StellarBumpSequenceOp
+ * @next StellarClaimClaimableBalanceOp
+ * @next StellarInvokeHostFunctionOp
*/
message StellarTxOpRequest {}
@@ -289,3 +291,209 @@ message StellarSignedTx {
required bytes public_key = 1; // public key for the private key used to sign data
required bytes signature = 2; // signature suitable for sending to the Stellar network
}
+
+/**
+ * Describes a Stellar SCValue
+ * See https://github.com/stellar/stellar-xdr/blob/v26.0/Stellar-contract.x#L229
+ * @embed
+ */
+message StellarSCVal {
+ required StellarSCValType type = 1;
+ optional bool b = 2; // SCV_BOOL
+ reserved 3; // SCV_ERROR, not supported yet
+ optional uint32 u32 = 4; // SCV_U32
+ optional sint32 i32 = 5; // SCV_I32
+ optional uint64 u64 = 6; // SCV_U64
+ optional sint64 i64 = 7; // SCV_I64
+ optional uint64 timepoint = 8; // SCV_TIMEPOINT
+ optional uint64 duration = 9; // SCV_DURATION
+ optional StellarUInt128Parts u128 = 10; // SCV_U128
+ optional StellarInt128Parts i128 = 11; // SCV_I128
+ optional StellarUInt256Parts u256 = 12; // SCV_U256
+ optional StellarInt256Parts i256 = 13; // SCV_I256
+ optional bytes bytes = 14; // SCV_BYTES
+ // SCV_STRING - The bytes contained in Strings do not necessarily conform to any
+ // standard text encoding such as ASCII or Unicode UTF-8. They are plain uninterpreted bytes.
+ // See: https://developers.stellar.org/docs/learn/fundamentals/contract-development/types/built-in-types
+ optional bytes string = 15;
+ optional string symbol = 16; // SCV_SYMBOL
+ repeated StellarSCVal vec = 17; // SCV_VEC
+ repeated StellarSCValMapEntry map = 18; // SCV_MAP
+ optional string address = 19; // SCV_ADDRESS
+ reserved 20; // SCV_CONTRACT_INSTANCE, not supported yet
+ reserved 21; // SCV_LEDGER_KEY_NONCE, not supported yet
+
+ message StellarUInt128Parts {
+ required uint64 hi = 1;
+ required uint64 lo = 2;
+ }
+
+ message StellarInt128Parts {
+ required sint64 hi = 1;
+ required uint64 lo = 2;
+ }
+
+ message StellarUInt256Parts {
+ required uint64 hi_hi = 1;
+ required uint64 hi_lo = 2;
+ required uint64 lo_hi = 3;
+ required uint64 lo_lo = 4;
+ }
+
+ message StellarInt256Parts {
+ required sint64 hi_hi = 1;
+ required uint64 hi_lo = 2;
+ required uint64 lo_hi = 3;
+ required uint64 lo_lo = 4;
+ }
+
+ message StellarSCValMapEntry {
+ required StellarSCVal key = 1;
+ required StellarSCVal value = 2;
+ }
+
+ enum StellarSCValType {
+ SCV_BOOL = 0;
+ SCV_VOID = 1;
+ reserved 2; // SCV_ERROR, not supported yet (This type is unlikely to occur, although it is a valid value)
+ SCV_U32 = 3;
+ SCV_I32 = 4;
+ SCV_U64 = 5;
+ SCV_I64 = 6;
+ SCV_TIMEPOINT = 7;
+ SCV_DURATION = 8;
+ SCV_U128 = 9;
+ SCV_I128 = 10;
+ SCV_U256 = 11;
+ SCV_I256 = 12;
+ SCV_BYTES = 13;
+ SCV_STRING = 14;
+ SCV_SYMBOL = 15;
+ SCV_VEC = 16;
+ SCV_MAP = 17;
+ SCV_ADDRESS = 18;
+ // these 3 scval types are special and are only used in the internal ledger layout.
+ // they are not convertible to the env values
+ reserved 19; // SCV_CONTRACT_INSTANCE, not supported yet
+ reserved 20; // SCV_LEDGER_KEY_CONTRACT_INSTANCE, not supported yet
+ reserved 21; // SCV_LEDGER_KEY_NONCE, not supported yet
+ }
+}
+
+/**
+ * https://github.com/stellar/stellar-xdr/blob/v26.0/Stellar-transaction.x#L515
+ * @embed
+ */
+message StellarInvokeContractArgs {
+ required string contract_address = 1;
+ required string function_name = 2;
+ repeated StellarSCVal args = 3;
+}
+
+/**
+ * https://github.com/stellar/stellar-xdr/blob/v26.0/Stellar-transaction.x#L540
+ * @embed
+ */
+message StellarSorobanAuthorizedFunction {
+ required StellarSorobanAuthorizedFunctionType type = 1;
+ optional StellarInvokeContractArgs contract_fn = 2;
+
+ enum StellarSorobanAuthorizedFunctionType {
+ SOROBAN_AUTHORIZED_FUNCTION_TYPE_CONTRACT_FN = 0;
+ reserved 1; // SOROBAN_AUTHORIZED_FUNCTION_TYPE_CREATE_CONTRACT_HOST_FN, not supported yet
+ reserved 2; // SOROBAN_AUTHORIZED_FUNCTION_TYPE_CREATE_CONTRACT_V2_HOST_FN, not supported yet
+ }
+}
+
+/**
+ * https://github.com/stellar/stellar-xdr/blob/v26.0/Stellar-transaction.x#L558
+ * @embed
+ */
+message StellarSorobanAuthorizedInvocation {
+ required StellarSorobanAuthorizedFunction function = 1;
+ repeated StellarSorobanAuthorizedInvocation sub_invocations = 2;
+}
+
+/**
+ * https://github.com/stellar/stellar-xdr/blob/v26.0/Stellar-transaction.x#L521
+ * @embed
+ */
+message StellarHostFunction {
+ required StellarHostFunctionType type = 1;
+ optional StellarInvokeContractArgs invoke_contract = 2;
+
+ enum StellarHostFunctionType {
+ HOST_FUNCTION_TYPE_INVOKE_CONTRACT = 0; // We only support this type of host function at this time.
+ reserved 1; // HOST_FUNCTION_TYPE_CREATE_CONTRACT, not supported yet
+ reserved 2; // HOST_FUNCTION_TYPE_UPLOAD_CONTRACT_WASM, not supported yet
+ reserved 3; // HOST_FUNCTION_TYPE_CREATE_CONTRACT_V2, not supported yet
+ }
+}
+
+/**
+ * https://github.com/stellar/stellar-xdr/blob/v26.0/Stellar-transaction.x#L564
+ * @embed
+ */
+message StellarSorobanAddressCredentials {
+ required string address = 1;
+ required sint64 nonce = 2;
+ required uint32 signature_expiration_ledger = 3;
+ required StellarSCVal signature = 4;
+}
+
+/**
+ * https://github.com/stellar/stellar-xdr/blob/v26.0/Stellar-transaction.x#L578
+ * @embed
+ */
+message StellarSorobanCredentials {
+ required StellarSorobanCredentialsType type = 1;
+ optional StellarSorobanAddressCredentials address = 2;
+
+ enum StellarSorobanCredentialsType {
+ SOROBAN_CREDENTIALS_SOURCE_ACCOUNT = 0;
+ SOROBAN_CREDENTIALS_ADDRESS = 1;
+ };
+}
+
+/**
+ * https://github.com/stellar/stellar-xdr/blob/v26.0/Stellar-transaction.x#L591
+ * @embed
+ */
+message StellarSorobanAuthorizationEntry {
+ required StellarSorobanCredentials credentials = 1;
+ required StellarSorobanAuthorizedInvocation root_invocation = 2;
+}
+
+/**
+ * Request: ask device to confirm this operation type
+ * @next StellarTxOpRequest
+ * @next StellarTxExtRequest
+ */
+message StellarInvokeHostFunctionOp {
+ optional string source_account = 1; // (optional) source account address
+ required StellarHostFunction function = 2;
+ repeated StellarSorobanAuthorizationEntry auth = 3;
+}
+
+/**
+ * Response: device is ready for client to send StellarTxExt
+ * @next StellarTxExt
+ */
+message StellarTxExtRequest {}
+
+/**
+ * Request: ask device to add extra data to the transaction
+ * @next StellarSignedTx
+ */
+message StellarTxExt {
+ // For soroban transactions, v = 1, otherwise v = 0
+ required sint32 v = 1;
+ // SorobanTransactionData XDR, required when v == 1. Passed through as raw bytes
+ // and committed via the signed digest. Contains the footprint (ledger entries the
+ // tx may access), resource limits, and the resourceFee -- none of which grant
+ // authority: the footprint only *declares* accessed entries (it cannot authorize
+ // state changes; those are gated by StellarInvokeHostFunctionOp.auth, shown
+ // on-device), and the resourceFee is a component of the already-confirmed total
+ // fee. A tampered value can only fail the tx or waste fees up to that total.
+ optional bytes soroban_data = 2;
+}
diff --git a/common/protob/messages.proto b/common/protob/messages.proto
index e5eaecb3..fa813748 100644
--- a/common/protob/messages.proto
+++ b/common/protob/messages.proto
@@ -231,7 +231,20 @@ enum MessageType {
MessageType_StellarPathPaymentStrictSendOp = 223 [(wire_in) = true];
reserved 224; // omitted: StellarCreateClaimableBalanceOp
MessageType_StellarClaimClaimableBalanceOp = 225 [(wire_in) = true];
+ reserved 226; // omitted: StellarBeginSponsoringFutureReservesOp
+ reserved 227; // omitted: StellarEndSponsoringFutureReservesOp
+ reserved 228; // omitted: StellarRevokeSponsorshipOp
+ reserved 229; // omitted: StellarClawbackOp
MessageType_StellarSignedTx = 230 [(wire_out) = true];
+ reserved 231; // omitted: StellarClawbackClaimableBalanceOp
+ reserved 232; // omitted: StellarSetTrustLineFlagsOp
+ reserved 233; // omitted: StellarLiquidityPoolDepositOp
+ reserved 234; // omitted: StellarLiquidityPoolWithdrawOp
+ MessageType_StellarInvokeHostFunctionOp = 235 [(wire_in) = true];
+ reserved 236; // omitted: StellarExtendFootprintTtl
+ reserved 237; // omitted: StellarRestoreFootprint
+ MessageType_StellarTxExtRequest = 238 [(wire_out) = true];
+ MessageType_StellarTxExt = 239 [(wire_in) = true];
// Cardano
// dropped Sign/VerifyMessage ids 300-302
diff --git a/common/tests/fixtures/stellar/sign_tx.json b/common/tests/fixtures/stellar/sign_tx.json
index ec1511bf..b45c6cae 100644
--- a/common/tests/fixtures/stellar/sign_tx.json
+++ b/common/tests/fixtures/stellar/sign_tx.json
@@ -1228,6 +1228,673 @@
"public_key": "2f22b9c62f08b774f3ebe6dd6e7db93c3ec2cbde0279561a3d9c5225b8c32292",
"signature": "ooAYonekHwUqyAahFkVoHL811S0z22sJ8Wqe277RqBnTillDRtIk3uqi0KsLZYJyeC8Ln7J0ZaXAtGPwIYoKCg=="
}
+ },
+ {
+ "name": "StellarInvokeHostFunction-all-types",
+ "parameters": {
+ "xdr": "AAAAAgAAAAAvIrnGLwi3dPPr5t1ufbk8PsLL3gJ5Vho9nFIluMMikgAAw7QAAAAAAAAD6QAAAAEAAAAAAAAAAAAAAABw29iAAAAAAAAAAAEAAAAAAAAAGAAAAAAAAAABAwoRGB8mLTQ7QklQV15lbHN6gYiPlp2kq7K5wMfO1dwAAAAJYWxsX3R5cGVzAAAAAAAAGgAAAAAAAAABAAAAAAAAAAAAAAABAAAAA/////8AAAAEgAAAAAAAAAX//////////wAAAAaAAAAAAAAAAAAAAAcAAAAAZ3SFgAAAAAgAAAAAAAFRgAAAAAn/////////////////////AAAACoAAAAAAAAAAAAAAAAAAAAAAAAAL//////////////////////////////////////////8AAAAMgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANAAAABN6tvu8AAAAOAAAAE2hlbGxvICJ3b3JsZCIKXCBlbmQAAAAADgAAAAT//gABAAAADwAAAAh0cmFuc2ZlcgAAABAAAAABAAAAAwAAAAMAAAABAAAADwAAAAN0d28AAAAAAAAAAAEAAAAQAAAAAQAAAAAAAAARAAAAAQAAAAIAAAAPAAAABmFtb3VudAAAAAAACgAAAAAAAAAAAAAAAAAPQkAAAAAPAAAAAnRvAAAAAAASAAAAAAAAAAAvIrnGLwi3dPPr5t1ufbk8PsLL3gJ5Vho9nFIluMMikgAAABEAAAABAAAAAAAAABIAAAAAAAAAAC8iucYvCLd08+vm3W59uTw+wsveAnlWGj2cUiW4wyKSAAAAEgAAAAEDChEYHyYtNDtCSVBXXmVsc3qBiI+WnaSrsrnAx87V3AAAABIAAAACoaarsLW6v8QBBgsQFRofJCkuMzg9QkdMUVZbYGVqb3R5foOIjZKXnAAAABIAAAADAAAAAAkMDxIVGBseISQnKi0wMzY5PD9CRUhLTlFUV1pdYGNmAAAAEgAAAAQCDRgjLjlET1plcHuGkZynsr3I097p9P8KFSArNkFMVwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAw1AAAAAA",
+ "address_n": "m/44'/148'/0'",
+ "network_passphrase": "Test SDF Network ; September 2015",
+ "tx": {
+ "source_account": "GAXSFOOGF4ELO5HT5PTN23T5XE6D5QWL3YBHSVQ2HWOFEJNYYMRJENBV",
+ "fee": 50100,
+ "sequence_number": 1001,
+ "timebounds_start": 0,
+ "timebounds_end": 1893456000,
+ "memo_type": "NONE"
+ },
+ "operations": [
+ {
+ "_message_type": "StellarInvokeHostFunctionOp",
+ "function": {
+ "type": "HOST_FUNCTION_TYPE_INVOKE_CONTRACT",
+ "invoke_contract": {
+ "contract_address": "CABQUEIYD4TC2NB3IJEVAV26MVWHG6UBRCHZNHNEVOZLTQGHZ3K5ZIRI",
+ "function_name": "all_types",
+ "args": [
+ {
+ "type": "SCV_BOOL",
+ "b": true
+ },
+ {
+ "type": "SCV_BOOL",
+ "b": false
+ },
+ {
+ "type": "SCV_VOID"
+ },
+ {
+ "type": "SCV_U32",
+ "u32": 4294967295
+ },
+ {
+ "type": "SCV_I32",
+ "i32": -2147483648
+ },
+ {
+ "type": "SCV_U64",
+ "u64": 18446744073709551615
+ },
+ {
+ "type": "SCV_I64",
+ "i64": -9223372036854775808
+ },
+ {
+ "type": "SCV_TIMEPOINT",
+ "timepoint": 1735689600
+ },
+ {
+ "type": "SCV_DURATION",
+ "duration": 86400
+ },
+ {
+ "type": "SCV_U128",
+ "u128": {
+ "hi": 18446744073709551615,
+ "lo": 18446744073709551615
+ }
+ },
+ {
+ "type": "SCV_I128",
+ "i128": {
+ "hi": -9223372036854775808,
+ "lo": 0
+ }
+ },
+ {
+ "type": "SCV_U256",
+ "u256": {
+ "hi_hi": 18446744073709551615,
+ "hi_lo": 18446744073709551615,
+ "lo_hi": 18446744073709551615,
+ "lo_lo": 18446744073709551615
+ }
+ },
+ {
+ "type": "SCV_I256",
+ "i256": {
+ "hi_hi": -9223372036854775808,
+ "hi_lo": 0,
+ "lo_hi": 0,
+ "lo_lo": 0
+ }
+ },
+ {
+ "type": "SCV_BYTES",
+ "bytes": "deadbeef"
+ },
+ {
+ "type": "SCV_STRING",
+ "string": "68656c6c6f2022776f726c64220a5c20656e64"
+ },
+ {
+ "type": "SCV_STRING",
+ "string": "fffe0001"
+ },
+ {
+ "type": "SCV_SYMBOL",
+ "symbol": "transfer"
+ },
+ {
+ "type": "SCV_VEC",
+ "vec": [
+ {
+ "type": "SCV_U32",
+ "u32": 1
+ },
+ {
+ "type": "SCV_SYMBOL",
+ "symbol": "two"
+ },
+ {
+ "type": "SCV_BOOL",
+ "b": true
+ }
+ ]
+ },
+ {
+ "type": "SCV_VEC"
+ },
+ {
+ "type": "SCV_MAP",
+ "map": [
+ {
+ "key": {
+ "type": "SCV_SYMBOL",
+ "symbol": "amount"
+ },
+ "value": {
+ "type": "SCV_I128",
+ "i128": {
+ "hi": 0,
+ "lo": 1000000
+ }
+ }
+ },
+ {
+ "key": {
+ "type": "SCV_SYMBOL",
+ "symbol": "to"
+ },
+ "value": {
+ "type": "SCV_ADDRESS",
+ "address": "GAXSFOOGF4ELO5HT5PTN23T5XE6D5QWL3YBHSVQ2HWOFEJNYYMRJENBV"
+ }
+ }
+ ]
+ },
+ {
+ "type": "SCV_MAP"
+ },
+ {
+ "type": "SCV_ADDRESS",
+ "address": "GAXSFOOGF4ELO5HT5PTN23T5XE6D5QWL3YBHSVQ2HWOFEJNYYMRJENBV"
+ },
+ {
+ "type": "SCV_ADDRESS",
+ "address": "CABQUEIYD4TC2NB3IJEVAV26MVWHG6UBRCHZNHNEVOZLTQGHZ3K5ZIRI"
+ },
+ {
+ "type": "SCV_ADDRESS",
+ "address": "MAAQMCYQCUNB6JBJFYZTQPKCI5GFCVS3MBSWU33UPF7IHCENSKLZZINGVOYLLOV7YTWO4"
+ },
+ {
+ "type": "SCV_ADDRESS",
+ "address": "BAAASDAPCIKRQGY6EESCOKRNGAZTMOJ4H5BEKSCLJZIVIV22LVQGGZRNN4"
+ },
+ {
+ "type": "SCV_ADDRESS",
+ "address": "LABA2GBDFY4UIT22MVYHXBURTST3FPOI2PPOT5H7BIKSAKZWIFGFOEBC"
+ }
+ ]
+ }
+ }
+ }
+ ],
+ "ext": {
+ "v": 1,
+ "soroban_data": "000000000000000000000000000000000000000000000000000000000000c350"
+ }
+ },
+ "result": {
+ "public_key": "2f22b9c62f08b774f3ebe6dd6e7db93c3ec2cbde0279561a3d9c5225b8c32292",
+ "signature": "D3dQu/hju9RPgHNb8Dwjl+Idx1HToY8MsVIUIk80hlEj2P473PSDf7PIYjBYZrb4FKDK6pE2yKapSwpGsRiUAA=="
+ },
+ "skip_models": ["t1b1"]
+ },
+ {
+ "name": "StellarInvokeHostFunction-auth-tree",
+ "parameters": {
+ "xdr": "AAAAAgAAAAAvIrnGLwi3dPPr5t1ufbk8PsLL3gJ5Vho9nFIluMMikgAAw7QAAAAAAAAH0QAAAAEAAAAAAAAAAAAAAABw29iAAAAAAAAAAAEAAAAAAAAAGAAAAAAAAAABAwoRGB8mLTQ7QklQV15lbHN6gYiPlp2kq7K5wMfO1dwAAAAGc3VibWl0AAAAAAACAAAAEgAAAAAAAAAALyK5xi8It3Tz6+bdbn25PD7Cy94CeVYaPZxSJbjDIpIAAAAKAAAAAAAAAAAAAAAAAExLQAAAAAMAAAAAAAAAAAAAAAEDChEYHyYtNDtCSVBXXmVsc3qBiI+WnaSrsrnAx87V3AAAAAZzdWJtaXQAAAAAAAIAAAASAAAAAAAAAAAvIrnGLwi3dPPr5t1ufbk8PsLL3gJ5Vho9nFIluMMikgAAAAoAAAAAAAAAAAAAAAAATEtAAAAAAgAAAAAAAAAEAg0YIy45RE9aZXB7hpGcp7K9yNPe6fT/ChUgKzZBTFcAAAAIdHJhbnNmZXIAAAADAAAAEgAAAAAAAAAALyK5xi8It3Tz6+bdbn25PD7Cy94CeVYaPZxSJbjDIpIAAAASAAAAAQMKERgfJi00O0JJUFdeZWxzeoGIj5adpKuyucDHztXcAAAACgAAAAAAAAAAAAAAAABMS0AAAAABAAAAAAAAAAMAAAAACQwPEhUYGx4hJCcqLTAzNjk8P0JFSEtOUVRXWl1gY2YAAAAEYnVybgAAAAIAAAASAAAAAAAAAAAvIrnGLwi3dPPr5t1ufbk8PsLL3gJ5Vho9nFIluMMikgAAAAoAAAAAAAAAAAAAAAAAAABkAAAAAAAAAAAAAAACoaarsLW6v8QBBgsQFRofJCkuMzg9QkdMUVZbYGVqb3R5foOIjZKXnAAAAAdhcHByb3ZlAAAAAAIAAAASAAAAAAAAAAAvIrnGLwi3dPPr5t1ufbk8PsLL3gJ5Vho9nFIluMMikgAAAAMAAAAHAAAAAAAAAAAAAAAAAAAAAQMKERgfJi00O0JJUFdeZWxzeoGIj5adpKuyucDHztXcAAAABHN3YXAAAAACAAAADwAAAAhleGFjdF9pbgAAAAoAAAAAAAAAAAAAAAAAAADIAAAAAQAAAAAAAAAEAg0YIy45RE9aZXB7hpGcp7K9yNPe6fT/ChUgKzZBTFcAAAAFcm91dGUAAAAAAAABAAAAEgAAAAAAAAAALyK5xi8It3Tz6+bdbn25PD7Cy94CeVYaPZxSJbjDIpIAAAAAAAAAAQAAAAAAAAAALyK5xi8It3Tz6+bdbn25PD7Cy94CeVYaPZxSJbjDIpJ//////////wAB4kAAAAANAAAAQAABAgMEBQYHCAkKCwwNDg8QERITFBUWFxgZGhscHR4fICEiIyQlJicoKSorLC0uLzAxMjM0NTY3ODk6Ozw9Pj8AAAAAAAAAAQMKERgfJi00O0JJUFdeZWxzeoGIj5adpKuyucDHztXcAAAAB2RlcG9zaXQAAAAAAgAAABIAAAAAAAAAAC8iucYvCLd08+vm3W59uTw+wsveAnlWGj2cUiW4wyKSAAAACgAAAAAAAAAAAAAAAAAAA+cAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMNQAAAAAA==",
+ "address_n": "m/44'/148'/0'",
+ "network_passphrase": "Test SDF Network ; September 2015",
+ "tx": {
+ "source_account": "GAXSFOOGF4ELO5HT5PTN23T5XE6D5QWL3YBHSVQ2HWOFEJNYYMRJENBV",
+ "fee": 50100,
+ "sequence_number": 2001,
+ "timebounds_start": 0,
+ "timebounds_end": 1893456000,
+ "memo_type": "NONE"
+ },
+ "operations": [
+ {
+ "_message_type": "StellarInvokeHostFunctionOp",
+ "function": {
+ "type": "HOST_FUNCTION_TYPE_INVOKE_CONTRACT",
+ "invoke_contract": {
+ "contract_address": "CABQUEIYD4TC2NB3IJEVAV26MVWHG6UBRCHZNHNEVOZLTQGHZ3K5ZIRI",
+ "function_name": "submit",
+ "args": [
+ {
+ "type": "SCV_ADDRESS",
+ "address": "GAXSFOOGF4ELO5HT5PTN23T5XE6D5QWL3YBHSVQ2HWOFEJNYYMRJENBV"
+ },
+ {
+ "type": "SCV_I128",
+ "i128": {
+ "hi": 0,
+ "lo": 5000000
+ }
+ }
+ ]
+ }
+ },
+ "auth": [
+ {
+ "credentials": {
+ "type": "SOROBAN_CREDENTIALS_SOURCE_ACCOUNT"
+ },
+ "root_invocation": {
+ "function": {
+ "type": "SOROBAN_AUTHORIZED_FUNCTION_TYPE_CONTRACT_FN",
+ "contract_fn": {
+ "contract_address": "CABQUEIYD4TC2NB3IJEVAV26MVWHG6UBRCHZNHNEVOZLTQGHZ3K5ZIRI",
+ "function_name": "submit",
+ "args": [
+ {
+ "type": "SCV_ADDRESS",
+ "address": "GAXSFOOGF4ELO5HT5PTN23T5XE6D5QWL3YBHSVQ2HWOFEJNYYMRJENBV"
+ },
+ {
+ "type": "SCV_I128",
+ "i128": {
+ "hi": 0,
+ "lo": 5000000
+ }
+ }
+ ]
+ }
+ },
+ "sub_invocations": [
+ {
+ "function": {
+ "type": "SOROBAN_AUTHORIZED_FUNCTION_TYPE_CONTRACT_FN",
+ "contract_fn": {
+ "contract_address": "LABA2GBDFY4UIT22MVYHXBURTST3FPOI2PPOT5H7BIKSAKZWIFGFOEBC",
+ "function_name": "transfer",
+ "args": [
+ {
+ "type": "SCV_ADDRESS",
+ "address": "GAXSFOOGF4ELO5HT5PTN23T5XE6D5QWL3YBHSVQ2HWOFEJNYYMRJENBV"
+ },
+ {
+ "type": "SCV_ADDRESS",
+ "address": "CABQUEIYD4TC2NB3IJEVAV26MVWHG6UBRCHZNHNEVOZLTQGHZ3K5ZIRI"
+ },
+ {
+ "type": "SCV_I128",
+ "i128": {
+ "hi": 0,
+ "lo": 5000000
+ }
+ }
+ ]
+ }
+ },
+ "sub_invocations": [
+ {
+ "function": {
+ "type": "SOROBAN_AUTHORIZED_FUNCTION_TYPE_CONTRACT_FN",
+ "contract_fn": {
+ "contract_address": "BAAASDAPCIKRQGY6EESCOKRNGAZTMOJ4H5BEKSCLJZIVIV22LVQGGZRNN4",
+ "function_name": "burn",
+ "args": [
+ {
+ "type": "SCV_ADDRESS",
+ "address": "GAXSFOOGF4ELO5HT5PTN23T5XE6D5QWL3YBHSVQ2HWOFEJNYYMRJENBV"
+ },
+ {
+ "type": "SCV_I128",
+ "i128": {
+ "hi": 0,
+ "lo": 100
+ }
+ }
+ ]
+ }
+ }
+ }
+ ]
+ },
+ {
+ "function": {
+ "type": "SOROBAN_AUTHORIZED_FUNCTION_TYPE_CONTRACT_FN",
+ "contract_fn": {
+ "contract_address": "MAAQMCYQCUNB6JBJFYZTQPKCI5GFCVS3MBSWU33UPF7IHCENSKLZZINGVOYLLOV7YTWO4",
+ "function_name": "approve",
+ "args": [
+ {
+ "type": "SCV_ADDRESS",
+ "address": "GAXSFOOGF4ELO5HT5PTN23T5XE6D5QWL3YBHSVQ2HWOFEJNYYMRJENBV"
+ },
+ {
+ "type": "SCV_U32",
+ "u32": 7
+ }
+ ]
+ }
+ }
+ }
+ ]
+ }
+ },
+ {
+ "credentials": {
+ "type": "SOROBAN_CREDENTIALS_SOURCE_ACCOUNT"
+ },
+ "root_invocation": {
+ "function": {
+ "type": "SOROBAN_AUTHORIZED_FUNCTION_TYPE_CONTRACT_FN",
+ "contract_fn": {
+ "contract_address": "CABQUEIYD4TC2NB3IJEVAV26MVWHG6UBRCHZNHNEVOZLTQGHZ3K5ZIRI",
+ "function_name": "swap",
+ "args": [
+ {
+ "type": "SCV_SYMBOL",
+ "symbol": "exact_in"
+ },
+ {
+ "type": "SCV_I128",
+ "i128": {
+ "hi": 0,
+ "lo": 200
+ }
+ }
+ ]
+ }
+ },
+ "sub_invocations": [
+ {
+ "function": {
+ "type": "SOROBAN_AUTHORIZED_FUNCTION_TYPE_CONTRACT_FN",
+ "contract_fn": {
+ "contract_address": "LABA2GBDFY4UIT22MVYHXBURTST3FPOI2PPOT5H7BIKSAKZWIFGFOEBC",
+ "function_name": "route",
+ "args": [
+ {
+ "type": "SCV_ADDRESS",
+ "address": "GAXSFOOGF4ELO5HT5PTN23T5XE6D5QWL3YBHSVQ2HWOFEJNYYMRJENBV"
+ }
+ ]
+ }
+ }
+ }
+ ]
+ }
+ },
+ {
+ "credentials": {
+ "type": "SOROBAN_CREDENTIALS_ADDRESS",
+ "address": {
+ "address": "GAXSFOOGF4ELO5HT5PTN23T5XE6D5QWL3YBHSVQ2HWOFEJNYYMRJENBV",
+ "nonce": 9223372036854775807,
+ "signature_expiration_ledger": 123456,
+ "signature": {
+ "type": "SCV_BYTES",
+ "bytes": "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f"
+ }
+ }
+ },
+ "root_invocation": {
+ "function": {
+ "type": "SOROBAN_AUTHORIZED_FUNCTION_TYPE_CONTRACT_FN",
+ "contract_fn": {
+ "contract_address": "CABQUEIYD4TC2NB3IJEVAV26MVWHG6UBRCHZNHNEVOZLTQGHZ3K5ZIRI",
+ "function_name": "deposit",
+ "args": [
+ {
+ "type": "SCV_ADDRESS",
+ "address": "GAXSFOOGF4ELO5HT5PTN23T5XE6D5QWL3YBHSVQ2HWOFEJNYYMRJENBV"
+ },
+ {
+ "type": "SCV_I128",
+ "i128": {
+ "hi": 0,
+ "lo": 999
+ }
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+ ],
+ "ext": {
+ "v": 1,
+ "soroban_data": "000000000000000000000000000000000000000000000000000000000000c350"
+ }
+ },
+ "result": {
+ "public_key": "2f22b9c62f08b774f3ebe6dd6e7db93c3ec2cbde0279561a3d9c5225b8c32292",
+ "signature": "/di5nlkauUooBg83iKXOkEWG1X1cQ4/IraU/dIx9a0vviKXMCRORXZm0vV2F9FG+1hKWcY3qCFDzdGKahzVWAA=="
+ },
+ "skip_models": ["t1b1"]
+ },
+ {
+ "name": "StellarInvokeHostFunction-memo-not-allowed",
+ "parameters": {
+ "xdr": "AAAAAgAAAAAvIrnGLwi3dPPr5t1ufbk8PsLL3gJ5Vho9nFIluMMikgAAw7QAAAAAAAALuQAAAAEAAAAAAAAAAAAAAABw29iAAAAAAQAAAAVoZWxsbwAAAAAAAAEAAAAAAAAAGAAAAAAAAAABAwoRGB8mLTQ7QklQV15lbHN6gYiPlp2kq7K5wMfO1dwAAAAGc3VibWl0AAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADDUAAAAAA=",
+ "address_n": "m/44'/148'/0'",
+ "network_passphrase": "Test SDF Network ; September 2015",
+ "tx": {
+ "source_account": "GAXSFOOGF4ELO5HT5PTN23T5XE6D5QWL3YBHSVQ2HWOFEJNYYMRJENBV",
+ "fee": 50100,
+ "sequence_number": 3001,
+ "timebounds_start": 0,
+ "timebounds_end": 1893456000,
+ "memo_type": "TEXT",
+ "memo_text": "hello"
+ },
+ "operations": [
+ {
+ "_message_type": "StellarInvokeHostFunctionOp",
+ "function": {
+ "type": "HOST_FUNCTION_TYPE_INVOKE_CONTRACT",
+ "invoke_contract": {
+ "contract_address": "CABQUEIYD4TC2NB3IJEVAV26MVWHG6UBRCHZNHNEVOZLTQGHZ3K5ZIRI",
+ "function_name": "submit"
+ }
+ }
+ }
+ ],
+ "ext": {
+ "v": 1,
+ "soroban_data": "000000000000000000000000000000000000000000000000000000000000c350"
+ }
+ },
+ "result": {
+ "error_message": "cannot be used with a memo"
+ },
+ "skip_models": ["t1b1"]
+ },
+ {
+ "name": "StellarInvokeHostFunction-not-only-operation",
+ "parameters": {
+ "xdr": "AAAAAgAAAAAvIrnGLwi3dPPr5t1ufbk8PsLL3gJ5Vho9nFIluMMikgAAAMgAAAAAAAAPoQAAAAEAAAAAAAAAAAAAAABw29iAAAAAAAAAAAIAAAAAAAAAGAAAAAAAAAABAwoRGB8mLTQ7QklQV15lbHN6gYiPlp2kq7K5wMfO1dwAAAAGc3VibWl0AAAAAAAAAAAAAAAAAAAAAAABAAAAAC8iucYvCLd08+vm3W59uTw+wsveAnlWGj2cUiW4wyKSAAAAAAAAAAAA5OHAAAAAAAAAAAA=",
+ "address_n": "m/44'/148'/0'",
+ "network_passphrase": "Test SDF Network ; September 2015",
+ "tx": {
+ "source_account": "GAXSFOOGF4ELO5HT5PTN23T5XE6D5QWL3YBHSVQ2HWOFEJNYYMRJENBV",
+ "fee": 200,
+ "sequence_number": 4001,
+ "timebounds_start": 0,
+ "timebounds_end": 1893456000,
+ "memo_type": "NONE"
+ },
+ "operations": [
+ {
+ "_message_type": "StellarInvokeHostFunctionOp",
+ "function": {
+ "type": "HOST_FUNCTION_TYPE_INVOKE_CONTRACT",
+ "invoke_contract": {
+ "contract_address": "CABQUEIYD4TC2NB3IJEVAV26MVWHG6UBRCHZNHNEVOZLTQGHZ3K5ZIRI",
+ "function_name": "submit"
+ }
+ }
+ },
+ {
+ "_message_type": "StellarPaymentOp",
+ "destination_account": "GAXSFOOGF4ELO5HT5PTN23T5XE6D5QWL3YBHSVQ2HWOFEJNYYMRJENBV",
+ "asset": {
+ "type": "NATIVE"
+ },
+ "amount": 15000000
+ }
+ ],
+ "ext": {
+ "v": 0
+ }
+ },
+ "result": {
+ "error_message": "must be the only operation"
+ },
+ "skip_models": ["t1b1"]
+ },
+ {
+ "name": "StellarInvokeHostFunction-requires-ext-v1",
+ "parameters": {
+ "xdr": "AAAAAgAAAAAvIrnGLwi3dPPr5t1ufbk8PsLL3gJ5Vho9nFIluMMikgAAAGQAAAAAAAATiQAAAAEAAAAAAAAAAAAAAABw29iAAAAAAAAAAAEAAAAAAAAAGAAAAAAAAAABAwoRGB8mLTQ7QklQV15lbHN6gYiPlp2kq7K5wMfO1dwAAAAGc3VibWl0AAAAAAAAAAAAAAAAAAAAAAAA",
+ "address_n": "m/44'/148'/0'",
+ "network_passphrase": "Test SDF Network ; September 2015",
+ "tx": {
+ "source_account": "GAXSFOOGF4ELO5HT5PTN23T5XE6D5QWL3YBHSVQ2HWOFEJNYYMRJENBV",
+ "fee": 100,
+ "sequence_number": 5001,
+ "timebounds_start": 0,
+ "timebounds_end": 1893456000,
+ "memo_type": "NONE"
+ },
+ "operations": [
+ {
+ "_message_type": "StellarInvokeHostFunctionOp",
+ "function": {
+ "type": "HOST_FUNCTION_TYPE_INVOKE_CONTRACT",
+ "invoke_contract": {
+ "contract_address": "CABQUEIYD4TC2NB3IJEVAV26MVWHG6UBRCHZNHNEVOZLTQGHZ3K5ZIRI",
+ "function_name": "submit"
+ }
+ }
+ }
+ ],
+ "ext": {
+ "v": 0
+ }
+ },
+ "result": {
+ "error_message": "requires ext.v = 1"
+ },
+ "skip_models": ["t1b1"]
+ },
+ {
+ "name": "StellarInvokeHostFunction-no-args",
+ "parameters": {
+ "xdr": "AAAAAgAAAAAvIrnGLwi3dPPr5t1ufbk8PsLL3gJ5Vho9nFIluMMikgAAw7QAAAAAAAAXcQAAAAEAAAAAAAAAAAAAAABw29iAAAAAAAAAAAEAAAAAAAAAGAAAAAAAAAABAwoRGB8mLTQ7QklQV15lbHN6gYiPlp2kq7K5wMfO1dwAAAAJZ2V0X2NvdW50AAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAw1AAAAAA",
+ "address_n": "m/44'/148'/0'",
+ "network_passphrase": "Test SDF Network ; September 2015",
+ "tx": {
+ "source_account": "GAXSFOOGF4ELO5HT5PTN23T5XE6D5QWL3YBHSVQ2HWOFEJNYYMRJENBV",
+ "fee": 50100,
+ "sequence_number": 6001,
+ "timebounds_start": 0,
+ "timebounds_end": 1893456000,
+ "memo_type": "NONE"
+ },
+ "operations": [
+ {
+ "_message_type": "StellarInvokeHostFunctionOp",
+ "function": {
+ "type": "HOST_FUNCTION_TYPE_INVOKE_CONTRACT",
+ "invoke_contract": {
+ "contract_address": "CABQUEIYD4TC2NB3IJEVAV26MVWHG6UBRCHZNHNEVOZLTQGHZ3K5ZIRI",
+ "function_name": "get_count"
+ }
+ }
+ }
+ ],
+ "ext": {
+ "v": 1,
+ "soroban_data": "000000000000000000000000000000000000000000000000000000000000c350"
+ }
+ },
+ "result": {
+ "public_key": "2f22b9c62f08b774f3ebe6dd6e7db93c3ec2cbde0279561a3d9c5225b8c32292",
+ "signature": "38320pFImULhxVKboTvem0wKuI6D8d5SjCstMSmJihRFnjJhhAXkHMIDxLXot6ImDdbdjnNHFIa9jxM84zG4BQ=="
+ },
+ "skip_models": ["t1b1"]
+ },
+ {
+ "name": "StellarInvokeHostFunction-op-source-account",
+ "parameters": {
+ "xdr": "AAAAAgAAAAAvIrnGLwi3dPPr5t1ufbk8PsLL3gJ5Vho9nFIluMMikgAAw7QAAAAAAAAXcQAAAAEAAAAAAAAAAAAAAABw29iAAAAAAAAAAAEAAAABAAAAAF1VZCRmsYW4QxUuniGRUdvFiSAn7EAQGlF77VygMMLgAAAAGAAAAAAAAAABAwoRGB8mLTQ7QklQV15lbHN6gYiPlp2kq7K5wMfO1dwAAAAGc3VibWl0AAAAAAABAAAACgAAAAAAAAAAAAAAAABMS0AAAAABAAAAAAAAAAAAAAABAwoRGB8mLTQ7QklQV15lbHN6gYiPlp2kq7K5wMfO1dwAAAAGc3VibWl0AAAAAAABAAAACgAAAAAAAAAAAAAAAABMS0AAAAABAAAAAAAAAAEHCAkKCwwNDg8QERITFBUWFxgZGhscHR4fICEiIyQlJgAAAARidXJuAAAAAQAAAAoAAAAAAAAAAAAAAAAAAABkAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADDUAAAAAA=",
+ "address_n": "m/44'/148'/0'",
+ "network_passphrase": "Test SDF Network ; September 2015",
+ "tx": {
+ "source_account": "GAXSFOOGF4ELO5HT5PTN23T5XE6D5QWL3YBHSVQ2HWOFEJNYYMRJENBV",
+ "fee": 50100,
+ "sequence_number": 6001,
+ "timebounds_start": 0,
+ "timebounds_end": 1893456000,
+ "memo_type": "NONE"
+ },
+ "operations": [
+ {
+ "_message_type": "StellarInvokeHostFunctionOp",
+ "source_account": "GBOVKZBEM2YYLOCDCUXJ4IMRKHN4LCJAE7WEAEA2KF562XFAGDBOB64V",
+ "function": {
+ "type": "HOST_FUNCTION_TYPE_INVOKE_CONTRACT",
+ "invoke_contract": {
+ "contract_address": "CABQUEIYD4TC2NB3IJEVAV26MVWHG6UBRCHZNHNEVOZLTQGHZ3K5ZIRI",
+ "function_name": "submit",
+ "args": [
+ {
+ "type": "SCV_I128",
+ "i128": {
+ "hi": 0,
+ "lo": 5000000
+ }
+ }
+ ]
+ }
+ },
+ "auth": [
+ {
+ "credentials": {
+ "type": "SOROBAN_CREDENTIALS_SOURCE_ACCOUNT"
+ },
+ "root_invocation": {
+ "function": {
+ "type": "SOROBAN_AUTHORIZED_FUNCTION_TYPE_CONTRACT_FN",
+ "contract_fn": {
+ "contract_address": "CABQUEIYD4TC2NB3IJEVAV26MVWHG6UBRCHZNHNEVOZLTQGHZ3K5ZIRI",
+ "function_name": "submit",
+ "args": [
+ {
+ "type": "SCV_I128",
+ "i128": {
+ "hi": 0,
+ "lo": 5000000
+ }
+ }
+ ]
+ }
+ },
+ "sub_invocations": [
+ {
+ "function": {
+ "type": "SOROBAN_AUTHORIZED_FUNCTION_TYPE_CONTRACT_FN",
+ "contract_fn": {
+ "contract_address": "CADQQCIKBMGA2DQPCAIREEYUCULBOGAZDINRYHI6D4QCCIRDEQSSN4QL",
+ "function_name": "burn",
+ "args": [
+ {
+ "type": "SCV_I128",
+ "i128": {
+ "hi": 0,
+ "lo": 100
+ }
+ }
+ ]
+ }
+ }
+ }
+ ]
+ }
+ }
+ ]
+ }
+ ],
+ "ext": {
+ "v": 1,
+ "soroban_data": "000000000000000000000000000000000000000000000000000000000000c350"
+ }
+ },
+ "result": {
+ "public_key": "2f22b9c62f08b774f3ebe6dd6e7db93c3ec2cbde0279561a3d9c5225b8c32292",
+ "signature": "5uv43tMSDRDgs788M4uIkEkA6QvwN4eu0EP7yq2niwVSsVRHTqrnc6S3JrH2vu11ZwbUfaHL0BhGhPDm90vaAw=="
+ },
+ "skip_models": ["t1b1"]
}
]
}
diff --git a/core/.changelog.d/3471.added b/core/.changelog.d/3471.added
new file mode 100644
index 00000000..8b64ec35
--- /dev/null
+++ b/core/.changelog.d/3471.added
@@ -0,0 +1 @@
+Stellar: Enable signing Soroban smart contract transactions (containing StellarInvokeHostFunctionOp).
diff --git a/core/embed/rust/librust_qstr.h b/core/embed/rust/librust_qstr.h
index 170d0502..429c7637 100644
--- a/core/embed/rust/librust_qstr.h
+++ b/core/embed/rust/librust_qstr.h
@@ -1010,10 +1010,12 @@ static void _librust_qstrs(void) {
MP_QSTR_words__address;
MP_QSTR_words__amount;
MP_QSTR_words__are_you_sure;
+ MP_QSTR_words__arguments;
MP_QSTR_words__array_of;
MP_QSTR_words__asset;
MP_QSTR_words__assets;
MP_QSTR_words__authenticate;
+ MP_QSTR_words__authorization;
MP_QSTR_words__blockhash;
MP_QSTR_words__bluetooth;
MP_QSTR_words__buying;
@@ -1042,6 +1044,7 @@ static void _librust_qstrs(void) {
MP_QSTR_words__fee_limit;
MP_QSTR_words__forget;
MP_QSTR_words__from;
+ MP_QSTR_words__function;
MP_QSTR_words__important;
MP_QSTR_words__instructions;
MP_QSTR_words__intent;
@@ -1503,11 +1506,14 @@ static void _librust_qstrs(void) {
MP_QSTR_stellar__delete_trust;
MP_QSTR_stellar__destination;
MP_QSTR_stellar__exchanges_require_memo;
+ MP_QSTR_stellar__ext_auth;
+ MP_QSTR_stellar__ext_auth_message;
MP_QSTR_stellar__final_confirm;
MP_QSTR_stellar__hash;
MP_QSTR_stellar__high;
MP_QSTR_stellar__home_domain;
MP_QSTR_stellar__inflation;
+ MP_QSTR_stellar__invoke_contract;
MP_QSTR_stellar__issuer_template;
MP_QSTR_stellar__key;
MP_QSTR_stellar__limit;
diff --git a/core/embed/rust/src/translations/generated/translated_string.rs b/core/embed/rust/src/translations/generated/translated_string.rs
index b2c3e87d..a73da3d9 100644
--- a/core/embed/rust/src/translations/generated/translated_string.rs
+++ b/core/embed/rust/src/translations/generated/translated_string.rs
@@ -1653,6 +1653,15 @@ pub enum TranslatedString {
#[cfg(feature = "universal_fw")]
ethereum__calldata_digest = 1253, // "ERC-8213 digest"
pin__reenter_new_description = 1254, // {"Bolt": "", "Caesar": "", "Delizia": "Re-enter new PIN.", "Eckhart": ""}
+ #[cfg(feature = "universal_fw")]
+ stellar__ext_auth = 1255, // "External Authorizations"
+ #[cfg(feature = "universal_fw")]
+ stellar__ext_auth_message = 1256, // "Transaction contains additional invocations authorized by external means."
+ #[cfg(feature = "universal_fw")]
+ stellar__invoke_contract = 1257, // "Invoke Contract"
+ words__arguments = 1258, // "Arguments"
+ words__authorization = 1259, // "Authorization"
+ words__function = 1260, // "Function"
}
impl TranslatedString {
@@ -2915,6 +2924,12 @@ impl TranslatedString {
"To",
"ERC-8213 digest",
"",
+ "External Authorizations",
+ "Transaction contains additional invocations authorized by external means.",
+ "Invoke Contract",
+ "Arguments",
+ "Authorization",
+ "Function",
);
#[cfg(all(feature = "debug", feature = "universal_fw"))]
@@ -4175,6 +4190,12 @@ impl TranslatedString {
18888,
18903,
18903,
+ 18926,
+ 18999,
+ 19014,
+ 19023,
+ 19036,
+ 19044,
];
#[cfg(all(feature = "debug", not(feature = "universal_fw")))]
@@ -5434,6 +5455,12 @@ impl TranslatedString {
"To",
"ERC-8213 digest",
"",
+ "External Authorizations",
+ "Transaction contains additional invocations authorized by external means.",
+ "Invoke Contract",
+ "Arguments",
+ "Authorization",
+ "Function",
);
#[cfg(all(feature = "debug", not(feature = "universal_fw")))]
@@ -6694,6 +6721,12 @@ impl TranslatedString {
18888,
18903,
18903,
+ 18926,
+ 18999,
+ 19014,
+ 19023,
+ 19036,
+ 19044,
];
#[cfg(all(not(feature = "debug"), feature = "universal_fw"))]
@@ -7953,6 +7986,12 @@ impl TranslatedString {
"To",
"ERC-8213 digest",
"",
+ "External Authorizations",
+ "Transaction contains additional invocations authorized by external means.",
+ "Invoke Contract",
+ "Arguments",
+ "Authorization",
+ "Function",
);
#[cfg(all(not(feature = "debug"), feature = "universal_fw"))]
@@ -9213,6 +9252,12 @@ impl TranslatedString {
18888,
18903,
18903,
+ 18926,
+ 18999,
+ 19014,
+ 19023,
+ 19036,
+ 19044,
];
#[cfg(all(not(feature = "debug"), not(feature = "universal_fw")))]
@@ -10472,6 +10517,12 @@ impl TranslatedString {
"To",
"ERC-8213 digest",
"",
+ "External Authorizations",
+ "Transaction contains additional invocations authorized by external means.",
+ "Invoke Contract",
+ "Arguments",
+ "Authorization",
+ "Function",
);
#[cfg(all(not(feature = "debug"), not(feature = "universal_fw")))]
@@ -11732,6 +11783,12 @@ impl TranslatedString {
18888,
18903,
18903,
+ 18926,
+ 18999,
+ 19014,
+ 19023,
+ 19036,
+ 19044,
];
} else if #[cfg(feature = "layout_caesar")] {
@@ -12992,6 +13049,12 @@ impl TranslatedString {
"To",
"ERC-8213 digest",
"",
+ "External Authorizations",
+ "Transaction contains additional invocations authorized by external means.",
+ "Invoke Contract",
+ "Arguments",
+ "Authorization",
+ "Function",
);
#[cfg(all(feature = "debug", feature = "universal_fw"))]
@@ -14252,6 +14315,12 @@ impl TranslatedString {
16679,
16694,
16694,
+ 16717,
+ 16790,
+ 16805,
+ 16814,
+ 16827,
+ 16835,
];
#[cfg(all(feature = "debug", not(feature = "universal_fw")))]
@@ -15511,6 +15580,12 @@ impl TranslatedString {
"To",
"ERC-8213 digest",
"",
+ "External Authorizations",
+ "Transaction contains additional invocations authorized by external means.",
+ "Invoke Contract",
+ "Arguments",
+ "Authorization",
+ "Function",
);
#[cfg(all(feature = "debug", not(feature = "universal_fw")))]
@@ -16771,6 +16846,12 @@ impl TranslatedString {
16679,
16694,
16694,
+ 16717,
+ 16790,
+ 16805,
+ 16814,
+ 16827,
+ 16835,
];
#[cfg(all(not(feature = "debug"), feature = "universal_fw"))]
@@ -18030,6 +18111,12 @@ impl TranslatedString {
"To",
"ERC-8213 digest",
"",
+ "External Authorizations",
+ "Transaction contains additional invocations authorized by external means.",
+ "Invoke Contract",
+ "Arguments",
+ "Authorization",
+ "Function",
);
#[cfg(all(not(feature = "debug"), feature = "universal_fw"))]
@@ -19290,6 +19377,12 @@ impl TranslatedString {
16679,
16694,
16694,
+ 16717,
+ 16790,
+ 16805,
+ 16814,
+ 16827,
+ 16835,
];
#[cfg(all(not(feature = "debug"), not(feature = "universal_fw")))]
@@ -20549,6 +20642,12 @@ impl TranslatedString {
"To",
"ERC-8213 digest",
"",
+ "External Authorizations",
+ "Transaction contains additional invocations authorized by external means.",
+ "Invoke Contract",
+ "Arguments",
+ "Authorization",
+ "Function",
);
#[cfg(all(not(feature = "debug"), not(feature = "universal_fw")))]
@@ -21809,6 +21908,12 @@ impl TranslatedString {
16679,
16694,
16694,
+ 16717,
+ 16790,
+ 16805,
+ 16814,
+ 16827,
+ 16835,
];
} else if #[cfg(feature = "layout_delizia")] {
@@ -23069,6 +23174,12 @@ impl TranslatedString {
"To",
"ERC-8213 digest",
"Re-enter new PIN.",
+ "External Authorizations",
+ "Transaction contains additional invocations authorized by external means.",
+ "Invoke Contract",
+ "Arguments",
+ "Authorization",
+ "Function",
);
#[cfg(all(feature = "debug", feature = "universal_fw"))]
@@ -24329,6 +24440,12 @@ impl TranslatedString {
18760,
18775,
18792,
+ 18815,
+ 18888,
+ 18903,
+ 18912,
+ 18925,
+ 18933,
];
#[cfg(all(feature = "debug", not(feature = "universal_fw")))]
@@ -25588,6 +25705,12 @@ impl TranslatedString {
"To",
"ERC-8213 digest",
"Re-enter new PIN.",
+ "External Authorizations",
+ "Transaction contains additional invocations authorized by external means.",
+ "Invoke Contract",
+ "Arguments",
+ "Authorization",
+ "Function",
);
#[cfg(all(feature = "debug", not(feature = "universal_fw")))]
@@ -26848,6 +26971,12 @@ impl TranslatedString {
18760,
18775,
18792,
+ 18815,
+ 18888,
+ 18903,
+ 18912,
+ 18925,
+ 18933,
];
#[cfg(all(not(feature = "debug"), feature = "universal_fw"))]
@@ -28107,6 +28236,12 @@ impl TranslatedString {
"To",
"ERC-8213 digest",
"Re-enter new PIN.",
+ "External Authorizations",
+ "Transaction contains additional invocations authorized by external means.",
+ "Invoke Contract",
+ "Arguments",
+ "Authorization",
+ "Function",
);
#[cfg(all(not(feature = "debug"), feature = "universal_fw"))]
@@ -29367,6 +29502,12 @@ impl TranslatedString {
18760,
18775,
18792,
+ 18815,
+ 18888,
+ 18903,
+ 18912,
+ 18925,
+ 18933,
];
#[cfg(all(not(feature = "debug"), not(feature = "universal_fw")))]
@@ -30626,6 +30767,12 @@ impl TranslatedString {
"To",
"ERC-8213 digest",
"Re-enter new PIN.",
+ "External Authorizations",
+ "Transaction contains additional invocations authorized by external means.",
+ "Invoke Contract",
+ "Arguments",
+ "Authorization",
+ "Function",
);
#[cfg(all(not(feature = "debug"), not(feature = "universal_fw")))]
@@ -31886,6 +32033,12 @@ impl TranslatedString {
18760,
18775,
18792,
+ 18815,
+ 18888,
+ 18903,
+ 18912,
+ 18925,
+ 18933,
];
} else if #[cfg(feature = "layout_eckhart")] {
@@ -33146,6 +33299,12 @@ impl TranslatedString {
"To",
"ERC-8213 digest",
"",
+ "External Authorizations",
+ "Transaction contains additional invocations authorized by external means.",
+ "Invoke Contract",
+ "Arguments",
+ "Authorization",
+ "Function",
);
#[cfg(all(feature = "debug", feature = "universal_fw"))]
@@ -34406,6 +34565,12 @@ impl TranslatedString {
20138,
20153,
20153,
+ 20176,
+ 20249,
+ 20264,
+ 20273,
+ 20286,
+ 20294,
];
#[cfg(all(feature = "debug", not(feature = "universal_fw")))]
@@ -35665,6 +35830,12 @@ impl TranslatedString {
"To",
"ERC-8213 digest",
"",
+ "External Authorizations",
+ "Transaction contains additional invocations authorized by external means.",
+ "Invoke Contract",
+ "Arguments",
+ "Authorization",
+ "Function",
);
#[cfg(all(feature = "debug", not(feature = "universal_fw")))]
@@ -36925,6 +37096,12 @@ impl TranslatedString {
20138,
20153,
20153,
+ 20176,
+ 20249,
+ 20264,
+ 20273,
+ 20286,
+ 20294,
];
#[cfg(all(not(feature = "debug"), feature = "universal_fw"))]
@@ -38184,6 +38361,12 @@ impl TranslatedString {
"To",
"ERC-8213 digest",
"",
+ "External Authorizations",
+ "Transaction contains additional invocations authorized by external means.",
+ "Invoke Contract",
+ "Arguments",
+ "Authorization",
+ "Function",
);
#[cfg(all(not(feature = "debug"), feature = "universal_fw"))]
@@ -39444,6 +39627,12 @@ impl TranslatedString {
20138,
20153,
20153,
+ 20176,
+ 20249,
+ 20264,
+ 20273,
+ 20286,
+ 20294,
];
#[cfg(all(not(feature = "debug"), not(feature = "universal_fw")))]
@@ -40703,6 +40892,12 @@ impl TranslatedString {
"To",
"ERC-8213 digest",
"",
+ "External Authorizations",
+ "Transaction contains additional invocations authorized by external means.",
+ "Invoke Contract",
+ "Arguments",
+ "Authorization",
+ "Function",
);
#[cfg(all(not(feature = "debug"), not(feature = "universal_fw")))]
@@ -41963,6 +42158,12 @@ impl TranslatedString {
20138,
20153,
20153,
+ 20176,
+ 20249,
+ 20264,
+ 20273,
+ 20286,
+ 20294,
];
}
@@ -43309,6 +43510,10 @@ impl TranslatedString {
#[cfg(feature = "universal_fw")]
(Qstr::MP_QSTR_stellar__exchanges_require_memo, Self::stellar__exchanges_require_memo),
#[cfg(feature = "universal_fw")]
+ (Qstr::MP_QSTR_stellar__ext_auth, Self::stellar__ext_auth),
+ #[cfg(feature = "universal_fw")]
+ (Qstr::MP_QSTR_stellar__ext_auth_message, Self::stellar__ext_auth_message),
+ #[cfg(feature = "universal_fw")]
(Qstr::MP_QSTR_stellar__final_confirm, Self::stellar__final_confirm),
#[cfg(feature = "universal_fw")]
(Qstr::MP_QSTR_stellar__hash, Self::stellar__hash),
@@ -43319,6 +43524,8 @@ impl TranslatedString {
#[cfg(feature = "universal_fw")]
(Qstr::MP_QSTR_stellar__inflation, Self::stellar__inflation),
#[cfg(feature = "universal_fw")]
+ (Qstr::MP_QSTR_stellar__invoke_contract, Self::stellar__invoke_contract),
+ #[cfg(feature = "universal_fw")]
(Qstr::MP_QSTR_stellar__issuer_template, Self::stellar__issuer_template),
#[cfg(feature = "universal_fw")]
(Qstr::MP_QSTR_stellar__key, Self::stellar__key),
@@ -43521,10 +43728,12 @@ impl TranslatedString {
(Qstr::MP_QSTR_words__address, Self::words__address),
(Qstr::MP_QSTR_words__amount, Self::words__amount),
(Qstr::MP_QSTR_words__are_you_sure, Self::words__are_you_sure),
+ (Qstr::MP_QSTR_words__arguments, Self::words__arguments),
(Qstr::MP_QSTR_words__array_of, Self::words__array_of),
(Qstr::MP_QSTR_words__asset, Self::words__asset),
(Qstr::MP_QSTR_words__assets, Self::words__assets),
(Qstr::MP_QSTR_words__authenticate, Self::words__authenticate),
+ (Qstr::MP_QSTR_words__authorization, Self::words__authorization),
(Qstr::MP_QSTR_words__blockhash, Self::words__blockhash),
(Qstr::MP_QSTR_words__bluetooth, Self::words__bluetooth),
(Qstr::MP_QSTR_words__buying, Self::words__buying),
@@ -43553,6 +43762,7 @@ impl TranslatedString {
(Qstr::MP_QSTR_words__fee_limit, Self::words__fee_limit),
(Qstr::MP_QSTR_words__forget, Self::words__forget),
(Qstr::MP_QSTR_words__from, Self::words__from),
+ (Qstr::MP_QSTR_words__function, Self::words__function),
(Qstr::MP_QSTR_words__important, Self::words__important),
(Qstr::MP_QSTR_words__instructions, Self::words__instructions),
(Qstr::MP_QSTR_words__intent, Self::words__intent),
diff --git a/core/embed/upymod/qstrdefsport.h b/core/embed/upymod/qstrdefsport.h
index 5bd6af27..49277342 100644
--- a/core/embed/upymod/qstrdefsport.h
+++ b/core/embed/upymod/qstrdefsport.h
@@ -490,8 +490,12 @@ Q(NEMModificationType)
Q(NEMMosaicLevy)
Q(NEMSupplyChangeType)
Q(StellarAssetType)
+Q(StellarHostFunctionType)
Q(StellarMemoType)
+Q(StellarSCValType)
Q(StellarSignerType)
+Q(StellarSorobanAuthorizedFunctionType)
+Q(StellarSorobanCredentialsType)
Q(TezosBallotType)
Q(TezosContractType)
Q(TronRawContractType)
@@ -810,8 +814,12 @@ Q(trezor.enums.NEMModificationType)
Q(trezor.enums.NEMMosaicLevy)
Q(trezor.enums.NEMSupplyChangeType)
Q(trezor.enums.StellarAssetType)
+Q(trezor.enums.StellarHostFunctionType)
Q(trezor.enums.StellarMemoType)
+Q(trezor.enums.StellarSCValType)
Q(trezor.enums.StellarSignerType)
+Q(trezor.enums.StellarSorobanAuthorizedFunctionType)
+Q(trezor.enums.StellarSorobanCredentialsType)
Q(trezor.enums.TezosBallotType)
Q(trezor.enums.TezosContractType)
Q(trezor.enums.TronRawContractType)
diff --git a/core/mocks/trezortranslate_keys.pyi b/core/mocks/trezortranslate_keys.pyi
index 7f4cb06d..7e966bdd 100644
--- a/core/mocks/trezortranslate_keys.pyi
+++ b/core/mocks/trezortranslate_keys.pyi
@@ -939,11 +939,14 @@ class TR:
stellar__delete_trust: str = "Delete trust"
stellar__destination: str = "Destination"
stellar__exchanges_require_memo: str = "Memo is not set.\nTypically needed when sending to exchanges."
+ stellar__ext_auth: str = "External Authorizations"
+ stellar__ext_auth_message: str = "Transaction contains additional invocations authorized by external means."
stellar__final_confirm: str = "Final confirm"
stellar__hash: str = "Hash"
stellar__high: str = "High"
stellar__home_domain: str = "Home Domain"
stellar__inflation: str = "Inflation"
+ stellar__invoke_contract: str = "Invoke Contract"
stellar__issuer_template: str = "{0} issuer"
stellar__key: str = "Key"
stellar__limit: str = "Limit"
@@ -1092,10 +1095,12 @@ class TR:
words__address: str = "Address"
words__amount: str = "Amount"
words__are_you_sure: str = "Are you sure?"
+ words__arguments: str = "Arguments"
words__array_of: str = "Array of"
words__asset: str = "Asset"
words__assets: str = "Assets"
words__authenticate: str = "Authenticate"
+ words__authorization: str = "Authorization"
words__blockhash: str = "Blockhash"
words__bluetooth: str = "Bluetooth"
words__buying: str = "Buying"
@@ -1124,6 +1129,7 @@ class TR:
words__fee_limit: str = "Fee limit"
words__forget: str = "Forget"
words__from: str = "from"
+ words__function: str = "Function"
words__important: str = "Important"
words__instructions: str = "Instructions"
words__intent: str = "Intent"
diff --git a/core/src/apps/stellar/consts.py b/core/src/apps/stellar/consts.py
index ef6741a3..7cf52f18 100644
--- a/core/src/apps/stellar/consts.py
+++ b/core/src/apps/stellar/consts.py
@@ -13,6 +13,7 @@ if TYPE_CHECKING:
StellarClaimClaimableBalanceOp,
StellarCreateAccountOp,
StellarCreatePassiveSellOfferOp,
+ StellarInvokeHostFunctionOp,
StellarManageBuyOfferOp,
StellarManageDataOp,
StellarManageSellOfferOp,
@@ -37,6 +38,7 @@ if TYPE_CHECKING:
| StellarPaymentOp
| StellarSetOptionsOp
| StellarClaimClaimableBalanceOp
+ | StellarInvokeHostFunctionOp
)
@@ -59,6 +61,7 @@ op_codes: dict[int, int] = {
MessageType.StellarPaymentOp: 1,
MessageType.StellarSetOptionsOp: 5,
MessageType.StellarClaimClaimableBalanceOp: 15,
+ MessageType.StellarInvokeHostFunctionOp: 24,
}
@@ -76,6 +79,10 @@ FLAG_AUTH_REVOCABLE = const(2)
FLAG_AUTH_IMMUTABLE = const(4)
FLAGS_MAX_SIZE = const(7)
+# SCSymbol is a string with a maximum length of 32
+# https://github.com/stellar/stellar-xdr/blob/v26.0/Stellar-contract.x#L211
+SCSYMBOL_MAX_SIZE = const(32)
+
def get_op_code(msg: protobuf.MessageType) -> int:
wire = msg.MESSAGE_WIRE_TYPE
diff --git a/core/src/apps/stellar/helpers.py b/core/src/apps/stellar/helpers.py
index 349f6699..1d877aaf 100644
--- a/core/src/apps/stellar/helpers.py
+++ b/core/src/apps/stellar/helpers.py
@@ -1,3 +1,4 @@
+from micropython import const
from typing import TYPE_CHECKING
from trezor.crypto import base32
@@ -5,29 +6,79 @@ from trezor.crypto import base32
if TYPE_CHECKING:
from buffer_types import AnyBytes
+# Stellar strkey version bytes
+# See: https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0023.md
+STRKEY_ED25519_PUBLIC_KEY = const(6) # G...
+STRKEY_CONTRACT = const(2) # C...
+STRKEY_MUXED_ACCOUNT = const(12) # M...
+STRKEY_CLAIMABLE_BALANCE = const(1) # B...
+STRKEY_LIQUIDITY_POOL = const(11) # L...
+
+_PAYLOAD_SIZE = {
+ STRKEY_ED25519_PUBLIC_KEY: 32,
+ STRKEY_CONTRACT: 32,
+ STRKEY_MUXED_ACCOUNT: 40,
+ STRKEY_CLAIMABLE_BALANCE: 33,
+ STRKEY_LIQUIDITY_POOL: 32,
+}
+
def public_key_from_address(address: str) -> bytes:
"""Extracts public key from an address
Stellar address is in format:
<1-byte version> <32-bytes ed25519 public key> <2-bytes CRC-16 checksum>
"""
- from trezor.wire import ProcessError
+ from trezor.wire import DataError
- b = base32.decode(address)
- # verify checksum - function deleted as it saved 50 bytes from the binary
- if _crc16_checksum(b[:-2]) != b[-2:]:
- raise ProcessError("Invalid address checksum")
- return b[1:-2]
+ version, data = decode_strkey(address)
+ if version != STRKEY_ED25519_PUBLIC_KEY:
+ raise DataError("Expected a public key address")
+ return data
def address_from_public_key(pubkey: AnyBytes) -> str:
"""Returns the base32-encoded version of public key bytes (G...)"""
- address = bytearray()
- address.append(6 << 3) # version -> 'G'
- address.extend(pubkey)
- address.extend(_crc16_checksum(bytes(address))) # checksum
+ return encode_strkey(STRKEY_ED25519_PUBLIC_KEY, pubkey)
+
+
+def encode_strkey(version: int, data: AnyBytes) -> str:
+ """Encode data to Stellar strkey format."""
+ payload = bytearray()
+ payload.append(version << 3)
+ payload.extend(data)
+ payload.extend(_crc16_checksum(bytes(payload)))
+ return base32.encode(payload).rstrip("=")
- return base32.encode(address)
+
+def decode_strkey(strkey: str) -> tuple[int, bytes]:
+ """Decode and validate a Stellar strkey into (version, data).
+
+ Follows SEP-0023: strkeys are unpadded base32, so the `=` padding stripped
+ by `encode_strkey` is restored before decoding. Besides the CRC-16 checksum,
+ canonicality is enforced by re-encoding: any input with an invalid length,
+ non-zero unused bits or an unsupported algorithm re-encodes differently and
+ is rejected.
+ """
+ from trezor.wire import DataError
+
+ try:
+ b = base32.decode(strkey + "=" * (-len(strkey) % 8))
+ except ValueError:
+ raise DataError("Strkey not base32-encoded")
+ if _crc16_checksum(b[:-2]) != b[-2:]:
+ raise DataError("Invalid strkey checksum")
+ version = b[0] >> 3
+ data = b[1:-2]
+ if encode_strkey(version, data) != strkey:
+ raise DataError("Invalid strkey encoding")
+ if version not in _PAYLOAD_SIZE:
+ raise DataError("Unsupported strkey version")
+ if len(data) != _PAYLOAD_SIZE[version]:
+ raise DataError("Invalid strkey payload length")
+ if version == STRKEY_CLAIMABLE_BALANCE and data[0] != 0:
+ # only CLAIMABLE_BALANCE_ID_TYPE_V0 exists
+ raise DataError("Invalid claimable balance type")
+ return version, data
def _crc16_checksum(data: AnyBytes) -> bytes:
diff --git a/core/src/apps/stellar/operations/__init__.py b/core/src/apps/stellar/operations/__init__.py
index 2eaa6021..39046312 100644
--- a/core/src/apps/stellar/operations/__init__.py
+++ b/core/src/apps/stellar/operations/__init__.py
@@ -79,5 +79,8 @@ async def process_operation(
elif messages.StellarClaimClaimableBalanceOp.is_type_of(op):
await layout.confirm_claim_claimable_balance_op(op)
serialize.write_claim_claimable_balance_op(w, op)
+ elif messages.StellarInvokeHostFunctionOp.is_type_of(op):
+ await layout.confirm_invoke_host_function_op(op)
+ serialize.write_invoke_host_function_op(w, op)
else:
raise ValueError("Unknown operation")
diff --git a/core/src/apps/stellar/operations/layout.py b/core/src/apps/stellar/operations/layout.py
index 61c0d07d..f9b87a5e 100644
--- a/core/src/apps/stellar/operations/layout.py
+++ b/core/src/apps/stellar/operations/layout.py
@@ -2,11 +2,13 @@ from typing import TYPE_CHECKING
from ubinascii import hexlify
from trezor import TR
+from trezor.enums import StellarSCValType
from trezor.ui.layouts import (
confirm_address,
confirm_properties,
confirm_stellar_output,
confirm_stellar_output_amount,
+ confirm_text,
confirm_value,
)
from trezor.wire import DataError, ProcessError
@@ -25,13 +27,24 @@ if TYPE_CHECKING:
StellarClaimClaimableBalanceOp,
StellarCreateAccountOp,
StellarCreatePassiveSellOfferOp,
+ StellarHostFunction,
+ StellarInt128Parts,
+ StellarInt256Parts,
+ StellarInvokeContractArgs,
+ StellarInvokeHostFunctionOp,
StellarManageBuyOfferOp,
StellarManageDataOp,
StellarManageSellOfferOp,
StellarPathPaymentStrictReceiveOp,
StellarPathPaymentStrictSendOp,
StellarPaymentOp,
+ StellarSCVal,
+ StellarSCValMapEntry,
StellarSetOptionsOp,
+ StellarSorobanAuthorizationEntry,
+ StellarSorobanAuthorizedInvocation,
+ StellarUInt128Parts,
+ StellarUInt256Parts,
)
from trezor.ui.layouts import PropertyType
@@ -416,3 +429,328 @@ async def confirm_asset_issuer(asset: StellarAsset) -> None:
br_name="confirm_asset_issuer",
verb=TR.buttons__continue,
)
+
+
+async def _confirm_invoke_contract_args(
+ args: StellarInvokeContractArgs,
+ br_name_prefix: str,
+ title: str | None = None,
+) -> None:
+ # If title is not empty, it is shared across screens;
+ # the per-screen label moves into the description / subtitle.
+ await confirm_address(
+ title or TR.stellar__invoke_contract,
+ args.contract_address,
+ description=TR.stellar__invoke_contract if title else None,
+ br_name=f"{br_name_prefix}_contract_address",
+ )
+ await confirm_text(
+ f"{br_name_prefix}_function",
+ title or TR.words__function,
+ args.function_name,
+ description=TR.words__function if title else None,
+ )
+ if not args.args:
+ return
+ props = [
+ (f"{i + 1} / {len(args.args)}", _format_sc_val(arg), True)
+ for i, arg in enumerate(args.args)
+ ]
+ await confirm_properties(
+ f"{br_name_prefix}_args",
+ title or TR.words__arguments,
+ props,
+ TR.words__arguments if title else None,
+ )
+
+
+def _is_root_auth_entry(
+ auth_entry: StellarSorobanAuthorizationEntry, invoked_fn: StellarHostFunction
+) -> bool:
+ from trezor.enums import (
+ StellarHostFunctionType,
+ StellarSorobanAuthorizedFunctionType,
+ )
+
+ from .serialize import write_invoke_contract_args
+
+ auth_fn = auth_entry.root_invocation.function
+
+ if (
+ auth_fn.type
+ == StellarSorobanAuthorizedFunctionType.SOROBAN_AUTHORIZED_FUNCTION_TYPE_CONTRACT_FN
+ and invoked_fn.type
+ == StellarHostFunctionType.HOST_FUNCTION_TYPE_INVOKE_CONTRACT
+ ):
+ if auth_fn.contract_fn is None or invoked_fn.invoke_contract is None:
+ return False
+
+ b1 = bytearray()
+ write_invoke_contract_args(b1, auth_fn.contract_fn)
+ b2 = bytearray()
+ write_invoke_contract_args(b2, invoked_fn.invoke_contract)
+
+ return b1 == b2
+
+ return False
+
+
+async def confirm_invoke_host_function_op(op: StellarInvokeHostFunctionOp) -> None:
+ from trezor.enums import StellarHostFunctionType, StellarSorobanCredentialsType
+ from trezor.ui.layouts import should_show_more
+
+ function = op.function
+
+ if function.type == StellarHostFunctionType.HOST_FUNCTION_TYPE_INVOKE_CONTRACT:
+ if function.invoke_contract is None:
+ raise DataError("Stellar: missing invoke_contract")
+
+ await _confirm_invoke_contract_args(
+ function.invoke_contract,
+ br_name_prefix="op_invoke",
+ )
+ else:
+ raise ProcessError("Stellar: unsupported host function type")
+
+ # Auth entries fall into two kinds by credential type:
+ #
+ # - SOURCE_ACCOUNT credentials are authorized by the signature the device
+ # produces over the transaction envelope. Approving that signature approves
+ # these entries, so we must always show them for confirmation.
+ #
+ # - ADDRESS credentials are authorized by a separate signature over the
+ # ENVELOPE_TYPE_SOROBAN_AUTHORIZATION preimage, which this device does not
+ # produce. They are hidden behind an opt-in and only shown for information;
+ # the user does not need to review them to sign safely.
+
+ # NOTE: signing ADDRESS credentials for our own account may be added later.
+
+ shown = 0
+ non_src_entries = []
+
+ for auth_entry in op.auth:
+ if (
+ auth_entry.credentials.type
+ == StellarSorobanCredentialsType.SOROBAN_CREDENTIALS_SOURCE_ACCOUNT
+ ):
+ shown += 1
+ await _confirm_auth_entry(
+ auth_entry, shown, _is_root_auth_entry(auth_entry, function)
+ )
+ else:
+ non_src_entries.append(auth_entry)
+
+ show_non_src = non_src_entries and await should_show_more(
+ TR.stellar__ext_auth,
+ ((TR.stellar__ext_auth_message, False),),
+ button_text=TR.buttons__show_all,
+ )
+ if show_non_src:
+ for auth_entry in non_src_entries:
+ shown += 1
+ await _confirm_auth_entry(
+ auth_entry, shown, _is_root_auth_entry(auth_entry, function)
+ )
+
+
+async def _confirm_auth_entry(
+ auth: StellarSorobanAuthorizationEntry, position: int, is_root: bool = False
+) -> None:
+ from trezor.enums import StellarSorobanCredentialsType
+
+ creds = auth.credentials
+
+ if creds.type == StellarSorobanCredentialsType.SOROBAN_CREDENTIALS_ADDRESS:
+ if creds.address is None:
+ raise DataError("Stellar: missing address credentials")
+
+ await confirm_address(
+ f"{TR.words__authorization} {position}",
+ creds.address.address,
+ description=TR.words__address,
+ br_name="op_auth_entry_address",
+ )
+
+ # Show the whole authorized invocation tree starting from its root (not just the
+ # nested sub-invocations), so the user sees exactly what this signature authorizes.
+ await _confirm_invocation(auth.root_invocation, str(position), is_root=is_root)
+
+
+async def _confirm_invocation(
+ invocation: StellarSorobanAuthorizedInvocation, position: str, is_root: bool = False
+) -> None:
+ """Confirm an authorized invocation and its sub-invocations recursively.
+
+ The whole authorization tree is shown by default (it is security-critical and
+ can differ from the host function being invoked). `position` is the path in
+ the auth tree (e.g. "1", "1-2", "1-2-1").
+ """
+ from trezor.enums import StellarSorobanAuthorizedFunctionType
+
+ func = invocation.function
+ if (
+ func.type
+ != StellarSorobanAuthorizedFunctionType.SOROBAN_AUTHORIZED_FUNCTION_TYPE_CONTRACT_FN
+ ):
+ raise ProcessError("Stellar: unsupported authorized function type")
+ if func.contract_fn is None:
+ raise DataError("Stellar: missing contract_fn")
+
+ title = f"{TR.words__authorization} {position}"
+
+ if not is_root:
+ await _confirm_invoke_contract_args(
+ func.contract_fn,
+ br_name_prefix="op_auth",
+ title=title,
+ )
+
+ for i, sub in enumerate(invocation.sub_invocations):
+ await _confirm_invocation(sub, f"{position}-{i + 1}")
+
+
+def _escape_str(s: str) -> str:
+ # Escape `\` first, then `"`, so an embedded quote cannot close the surrounding
+ # string delimiters -- otherwise a string could forge extra vec/map items.
+ return s.replace("\\", "\\\\").replace('"', '\\"')
+
+
+def _format_sc_val(val: StellarSCVal) -> str:
+ """Format SCVal as a human-readable string, using JSON for complex types."""
+ from trezor.strings import format_duration, format_timestamp
+
+ t = val.type
+
+ if t == StellarSCValType.SCV_BOOL:
+ if val.b is None:
+ raise DataError("Stellar: missing bool value")
+ return "true" if val.b else "false"
+ elif t == StellarSCValType.SCV_VOID:
+ return "void"
+ elif t == StellarSCValType.SCV_U32:
+ if val.u32 is None:
+ raise DataError("Stellar: missing u32 value")
+ return str(val.u32)
+ elif t == StellarSCValType.SCV_I32:
+ if val.i32 is None:
+ raise DataError("Stellar: missing i32 value")
+ return str(val.i32)
+ elif t == StellarSCValType.SCV_U64:
+ if val.u64 is None:
+ raise DataError("Stellar: missing u64 value")
+ return str(val.u64)
+ elif t == StellarSCValType.SCV_I64:
+ if val.i64 is None:
+ raise DataError("Stellar: missing i64 value")
+ return str(val.i64)
+ elif t == StellarSCValType.SCV_TIMEPOINT:
+ if val.timepoint is None:
+ raise DataError("Stellar: missing timepoint value")
+ try:
+ return format_timestamp(val.timepoint)
+ except Exception:
+ return str(val.timepoint)
+ elif t == StellarSCValType.SCV_DURATION:
+ if val.duration is None:
+ raise DataError("Stellar: missing duration value")
+ return format_duration(val.duration)
+ elif t == StellarSCValType.SCV_U128:
+ if val.u128 is None:
+ raise DataError("Stellar: missing u128 value")
+ return _format_u128(val.u128)
+ elif t == StellarSCValType.SCV_I128:
+ if val.i128 is None:
+ raise DataError("Stellar: missing i128 value")
+ return _format_i128(val.i128)
+ elif t == StellarSCValType.SCV_U256:
+ if val.u256 is None:
+ raise DataError("Stellar: missing u256 value")
+ return _format_u256(val.u256)
+ elif t == StellarSCValType.SCV_I256:
+ if val.i256 is None:
+ raise DataError("Stellar: missing i256 value")
+ return _format_i256(val.i256)
+ elif t == StellarSCValType.SCV_BYTES:
+ if val.bytes is None:
+ raise DataError("Stellar: missing bytes value")
+ return "0x" + hexlify(val.bytes).decode()
+ elif t == StellarSCValType.SCV_STRING:
+ if val.string is None:
+ raise DataError("Stellar: missing string value")
+ # Render decoded text as a quoted, escaped string so its content can never
+ # forge the surrounding quotes (and thus the vec/map separators). Non-UTF-8
+ # bytes can't be shown as text, so render them as hex like SCV_BYTES.
+ try:
+ return f'"{_escape_str(bytes(val.string).decode())}"'
+ except UnicodeError:
+ return "0x" + hexlify(val.string).decode()
+ elif t == StellarSCValType.SCV_SYMBOL:
+ if val.symbol is None:
+ raise DataError("Stellar: missing symbol value")
+ # Quote and escape like SCV_STRING so the symbol's content can never forge the
+ # surrounding vec/map delimiters. A symbol is already a valid UTF-8 str, so no
+ # hex fallback is needed (unlike SCV_STRING, which holds raw bytes).
+ return f'"{_escape_str(val.symbol)}"'
+ elif t == StellarSCValType.SCV_VEC:
+ return _format_vec_as_json(val.vec)
+ elif t == StellarSCValType.SCV_MAP:
+ return _format_map_as_json(val.map)
+ elif t == StellarSCValType.SCV_ADDRESS:
+ if val.address is None:
+ raise DataError("Stellar: missing address value")
+ return val.address
+ else:
+ raise DataError(f"Stellar: unsupported SCVal type {t}")
+
+
+def _format_vec_as_json(vec: list[StellarSCVal]) -> str:
+ """Format a vector as JSON array."""
+ items = [_format_sc_val(item) for item in vec]
+ return "[" + ", ".join(items) + "]"
+
+
+def _format_map_as_json(map_entries: list[StellarSCValMapEntry]) -> str:
+ """Format a map as JSON object."""
+ pairs = []
+ for entry in map_entries:
+ key = _format_sc_val(entry.key)
+ value = _format_sc_val(entry.value)
+ pairs.append(f"{key}: {value}")
+ return "{" + ", ".join(pairs) + "}"
+
+
+_MASK64 = 0xFFFF_FFFF_FFFF_FFFF
+
+
+def _format_u128(parts: StellarUInt128Parts) -> str:
+ value = ((parts.hi & _MASK64) << 64) | (parts.lo & _MASK64)
+ return str(value)
+
+
+def _format_i128(parts: StellarInt128Parts) -> str:
+ value = ((parts.hi & _MASK64) << 64) | (parts.lo & _MASK64)
+ if parts.hi < 0:
+ value -= 1 << 128
+ return str(value)
+
+
+def _format_u256(parts: StellarUInt256Parts) -> str:
+ value = (
+ ((parts.hi_hi & _MASK64) << 192)
+ | ((parts.hi_lo & _MASK64) << 128)
+ | ((parts.lo_hi & _MASK64) << 64)
+ | (parts.lo_lo & _MASK64)
+ )
+ return str(value)
+
+
+def _format_i256(parts: StellarInt256Parts) -> str:
+ value = (
+ ((parts.hi_hi & _MASK64) << 192)
+ | ((parts.hi_lo & _MASK64) << 128)
+ | ((parts.lo_hi & _MASK64) << 64)
+ | (parts.lo_lo & _MASK64)
+ )
+ if parts.hi_hi < 0:
+ value -= 1 << 256
+ return str(value)
diff --git a/core/src/apps/stellar/operations/serialize.py b/core/src/apps/stellar/operations/serialize.py
index d50c4ea3..da4002e5 100644
--- a/core/src/apps/stellar/operations/serialize.py
+++ b/core/src/apps/stellar/operations/serialize.py
@@ -6,6 +6,8 @@ from trezor.wire import DataError, ProcessError
from ..writers import (
write_bool,
write_bytes_fixed,
+ write_int32,
+ write_int64,
write_pubkey,
write_string,
write_uint32,
@@ -14,6 +16,7 @@ from ..writers import (
if TYPE_CHECKING:
from buffer_types import AnyBytes
+ from typing import Callable, TypeVar
from trezor.messages import (
StellarAccountMergeOp,
@@ -24,16 +27,40 @@ if TYPE_CHECKING:
StellarClaimClaimableBalanceOp,
StellarCreateAccountOp,
StellarCreatePassiveSellOfferOp,
+ StellarHostFunction,
+ StellarInt128Parts,
+ StellarInt256Parts,
+ StellarInvokeContractArgs,
+ StellarInvokeHostFunctionOp,
StellarManageBuyOfferOp,
StellarManageDataOp,
StellarManageSellOfferOp,
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)
@@ -109,9 +136,7 @@ def write_path_payment_strict_receive_op(
_write_asset(w, msg.destination_asset)
write_uint64(w, msg.destination_amount)
- write_uint32(w, len(msg.paths))
- for p in msg.paths:
- _write_asset(w, p)
+ _write_vec(w, msg.paths, _write_asset)
def write_path_payment_strict_send_op(
@@ -123,9 +148,7 @@ def write_path_payment_strict_send_op(
_write_asset(w, msg.destination_asset)
write_uint64(w, msg.destination_min)
- write_uint32(w, len(msg.paths))
- for p in msg.paths:
- _write_asset(w, p)
+ _write_vec(w, msg.paths, _write_asset)
def write_payment_op(w: Writer, msg: StellarPaymentOp) -> None:
@@ -238,3 +261,234 @@ def _write_claimable_balance_id(w: Writer, claimable_balance_id: AnyBytes) -> No
if claimable_balance_id[:4] != b"\x00\x00\x00\x00": # CLAIMABLE_BALANCE_ID_TYPE_V0
raise DataError("Stellar: invalid claimable balance id, unknown type")
write_bytes_fixed(w, claimable_balance_id, 36)
+
+
+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)
+
+
+def _write_host_function(w: Writer, msg: StellarHostFunction) -> None:
+ from trezor.enums import StellarHostFunctionType
+
+ write_uint32(w, msg.type)
+ if msg.type == StellarHostFunctionType.HOST_FUNCTION_TYPE_INVOKE_CONTRACT:
+ if msg.invoke_contract is None:
+ raise DataError("Stellar: missing invoke_contract")
+ write_invoke_contract_args(w, msg.invoke_contract)
+ else:
+ 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:
+ _write_soroban_credentials(w, msg.credentials)
+ _write_soroban_authorized_invocation(w, msg.root_invocation)
+
+
+def _write_soroban_credentials(w: Writer, msg: StellarSorobanCredentials) -> None:
+ from trezor.enums import StellarSorobanCredentialsType
+
+ write_uint32(w, msg.type)
+ if msg.type == StellarSorobanCredentialsType.SOROBAN_CREDENTIALS_SOURCE_ACCOUNT:
+ pass # void
+ elif msg.type == StellarSorobanCredentialsType.SOROBAN_CREDENTIALS_ADDRESS:
+ if msg.address is None:
+ raise DataError("Stellar: missing address credentials")
+ _write_soroban_address_credentials(w, msg.address)
+ else:
+ raise ProcessError("Stellar: unsupported credentials type")
+
+
+def _write_soroban_address_credentials(
+ w: Writer, msg: StellarSorobanAddressCredentials
+) -> None:
+ _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")
diff --git a/core/src/apps/stellar/sign_tx.py b/core/src/apps/stellar/sign_tx.py
index 836484ca..f694d7f0 100644
--- a/core/src/apps/stellar/sign_tx.py
+++ b/core/src/apps/stellar/sign_tx.py
@@ -23,10 +23,13 @@ async def sign_tx(msg: StellarSignTx, keychain: Slip21Keychain) -> StellarSigned
from trezor.messages import (
StellarAccountMergeOp,
StellarCreateAccountOp,
+ StellarInvokeHostFunctionOp,
StellarPathPaymentStrictReceiveOp,
StellarPathPaymentStrictSendOp,
StellarPaymentOp,
StellarSignedTx,
+ StellarTxExt,
+ StellarTxExtRequest,
StellarTxOpRequest,
)
from trezor.ui.layouts import show_continue_in_app
@@ -105,7 +108,6 @@ async def sign_tx(msg: StellarSignTx, keychain: Slip21Keychain) -> StellarSigned
memo_confirm_text = hexlify(msg.memo_hash).decode()
else:
raise ProcessError("Stellar invalid memo type")
- await layout.require_confirm_memo(memo_type, memo_confirm_text)
if msg.payment_req:
from apps.common.payment_request import PaymentRequestVerifier
@@ -121,6 +123,7 @@ async def sign_tx(msg: StellarSignTx, keychain: Slip21Keychain) -> StellarSigned
# these two are used in case of payment requests, where we allow only one output, hence we have a single output address and asset
output_address = None
output_asset = None
+ has_soroban_op = False
progress_obj = progress(indeterminate=True)
writers.write_uint32(w, num_operations)
@@ -128,7 +131,22 @@ async def sign_tx(msg: StellarSignTx, keychain: Slip21Keychain) -> StellarSigned
progress_obj.report(int(i / num_operations * 900))
op = await call_any(StellarTxOpRequest(), *consts.op_codes.keys())
- await process_operation(w, op, current_output_index, verifier) # type: ignore [Argument of type "MessageType" cannot be assigned to parameter "op" of type "StellarMessageType" in function "process_operation"]
+ if StellarInvokeHostFunctionOp.is_type_of(op):
+ # A Soroban operation must be the only operation in the transaction.
+ if num_operations != 1:
+ raise ProcessError(
+ "Stellar: a Soroban operation must be the only operation"
+ )
+ if memo_type != StellarMemoType.NONE:
+ raise ProcessError(
+ "Stellar: a Soroban operation cannot be used with a memo"
+ )
+ has_soroban_op = True
+ elif i == 0:
+ # Soroban transactions do not support memos
+ await layout.require_confirm_memo(memo_type, memo_confirm_text)
+
+ await process_operation(w, op, current_output_index, verifier) # type: ignore [Argument of type "StellarInvokeHostFunctionOp | MessageType" cannot be assigned to parameter "op" of type "StellarMessageType" in function "process_operation"]
if msg.payment_req:
assert verifier is not None
@@ -160,8 +178,24 @@ async def sign_tx(msg: StellarSignTx, keychain: Slip21Keychain) -> StellarSigned
# ---------------------------------
# FINAL
# ---------------------------------
- # 4 null bytes representing a (currently unused) empty union
- writers.write_uint32(w, 0)
+ # Transaction extension (ext union)
+ if has_soroban_op:
+ # For Soroban transactions, request StellarTxExt with soroban_data
+ from trezor.wire.context import call
+
+ tx_ext = await call(StellarTxExtRequest(), StellarTxExt)
+ if tx_ext.v != 1:
+ raise DataError("Stellar: Soroban transaction requires ext.v = 1")
+ if tx_ext.soroban_data is None:
+ raise DataError("Stellar: missing soroban_data")
+ writers.write_uint32(w, 1) # ext.v = 1
+ # Write soroban_data as raw XDR bytes (SorobanTransactionData struct)
+ writers.write_bytes_unchecked(w, tx_ext.soroban_data)
+ else:
+ # For non-Soroban transactions, ext.v = 0 (empty union).
+ # We intentionally do NOT request StellarTxExtRequest here to maintain
+ # backward compatibility with existing SDK implementations.
+ writers.write_uint32(w, 0)
if msg.payment_req:
assert verifier is not None
diff --git a/core/src/apps/stellar/writers.py b/core/src/apps/stellar/writers.py
index 826923ed..06b7bfe5 100644
--- a/core/src/apps/stellar/writers.py
+++ b/core/src/apps/stellar/writers.py
@@ -1,9 +1,11 @@
+from micropython import const
from typing import TYPE_CHECKING
import apps.common.writers as writers
# Reexporting to other modules
write_bytes_fixed = writers.write_bytes_fixed
+write_bytes_unchecked = writers.write_bytes_unchecked
write_uint32 = writers.write_uint32_be
write_uint64 = writers.write_uint64_be
@@ -39,3 +41,26 @@ def write_pubkey(w: Writer, address: str) -> None:
# first 4 bytes of an address are the type, there's only one type (0)
write_uint32(w, 0)
writers.write_bytes_fixed(w, public_key_from_address(address), 32)
+
+
+_INT32_MIN = const(-0x8000_0000)
+_INT32_MAX = const(0x7FFF_FFFF)
+_UINT32_MASK = const(0xFFFF_FFFF)
+
+_INT64_MIN = const(-0x8000_0000_0000_0000)
+_INT64_MAX = const(0x7FFF_FFFF_FFFF_FFFF)
+_UINT64_MASK = const(0xFFFF_FFFF_FFFF_FFFF)
+
+
+def write_int32(w: Writer, value: int) -> None:
+ """Write signed 32-bit integer in big-endian."""
+ if value < _INT32_MIN or value > _INT32_MAX:
+ raise ValueError("int32 out of range")
+ write_uint32(w, value & _UINT32_MASK)
+
+
+def write_int64(w: Writer, value: int) -> None:
+ """Write signed 64-bit integer in big-endian."""
+ if value < _INT64_MIN or value > _INT64_MAX:
+ raise ValueError("int64 out of range")
+ write_uint64(w, value & _UINT64_MASK)
diff --git a/core/src/trezor/enums/MessageType.py b/core/src/trezor/enums/MessageType.py
index 13b574c0..aad2a8f9 100644
--- a/core/src/trezor/enums/MessageType.py
+++ b/core/src/trezor/enums/MessageType.py
@@ -191,6 +191,9 @@ if not utils.BITCOIN_ONLY:
StellarPathPaymentStrictSendOp = 223
StellarClaimClaimableBalanceOp = 225
StellarSignedTx = 230
+ StellarInvokeHostFunctionOp = 235
+ StellarTxExtRequest = 238
+ StellarTxExt = 239
CardanoGetPublicKey = 305
CardanoPublicKey = 306
CardanoGetAddress = 307
diff --git a/core/src/trezor/enums/StellarHostFunctionType.py b/core/src/trezor/enums/StellarHostFunctionType.py
new file mode 100644
index 00000000..96957d7b
--- /dev/null
+++ b/core/src/trezor/enums/StellarHostFunctionType.py
@@ -0,0 +1,5 @@
+# Automatically generated by pb2py
+# fmt: off
+# isort:skip_file
+
+HOST_FUNCTION_TYPE_INVOKE_CONTRACT = 0
diff --git a/core/src/trezor/enums/StellarSCValType.py b/core/src/trezor/enums/StellarSCValType.py
new file mode 100644
index 00000000..ea3a34ec
--- /dev/null
+++ b/core/src/trezor/enums/StellarSCValType.py
@@ -0,0 +1,22 @@
+# Automatically generated by pb2py
+# fmt: off
+# isort:skip_file
+
+SCV_BOOL = 0
+SCV_VOID = 1
+SCV_U32 = 3
+SCV_I32 = 4
+SCV_U64 = 5
+SCV_I64 = 6
+SCV_TIMEPOINT = 7
+SCV_DURATION = 8
+SCV_U128 = 9
+SCV_I128 = 10
+SCV_U256 = 11
+SCV_I256 = 12
+SCV_BYTES = 13
+SCV_STRING = 14
+SCV_SYMBOL = 15
+SCV_VEC = 16
+SCV_MAP = 17
+SCV_ADDRESS = 18
diff --git a/core/src/trezor/enums/StellarSorobanAuthorizedFunctionType.py b/core/src/trezor/enums/StellarSorobanAuthorizedFunctionType.py
new file mode 100644
index 00000000..57390994
--- /dev/null
+++ b/core/src/trezor/enums/StellarSorobanAuthorizedFunctionType.py
@@ -0,0 +1,5 @@
+# Automatically generated by pb2py
+# fmt: off
+# isort:skip_file
+
+SOROBAN_AUTHORIZED_FUNCTION_TYPE_CONTRACT_FN = 0
diff --git a/core/src/trezor/enums/StellarSorobanCredentialsType.py b/core/src/trezor/enums/StellarSorobanCredentialsType.py
new file mode 100644
index 00000000..fa2a1ac9
--- /dev/null
+++ b/core/src/trezor/enums/StellarSorobanCredentialsType.py
@@ -0,0 +1,6 @@
+# Automatically generated by pb2py
+# fmt: off
+# isort:skip_file
+
+SOROBAN_CREDENTIALS_SOURCE_ACCOUNT = 0
+SOROBAN_CREDENTIALS_ADDRESS = 1
diff --git a/core/src/trezor/enums/__init__.py b/core/src/trezor/enums/__init__.py
index b373e1ee..32206bd9 100644
--- a/core/src/trezor/enums/__init__.py
+++ b/core/src/trezor/enums/__init__.py
@@ -384,6 +384,36 @@ if TYPE_CHECKING:
PRE_AUTH = 1
HASH = 2
+ class StellarSCValType(IntEnum):
+ SCV_BOOL = 0
+ SCV_VOID = 1
+ SCV_U32 = 3
+ SCV_I32 = 4
+ SCV_U64 = 5
+ SCV_I64 = 6
+ SCV_TIMEPOINT = 7
+ SCV_DURATION = 8
+ SCV_U128 = 9
+ SCV_I128 = 10
+ SCV_U256 = 11
+ SCV_I256 = 12
+ SCV_BYTES = 13
+ SCV_STRING = 14
+ SCV_SYMBOL = 15
+ SCV_VEC = 16
+ SCV_MAP = 17
+ SCV_ADDRESS = 18
+
+ class StellarSorobanAuthorizedFunctionType(IntEnum):
+ SOROBAN_AUTHORIZED_FUNCTION_TYPE_CONTRACT_FN = 0
+
+ class StellarHostFunctionType(IntEnum):
+ HOST_FUNCTION_TYPE_INVOKE_CONTRACT = 0
+
+ class StellarSorobanCredentialsType(IntEnum):
+ SOROBAN_CREDENTIALS_SOURCE_ACCOUNT = 0
+ SOROBAN_CREDENTIALS_ADDRESS = 1
+
class TezosContractType(IntEnum):
Implicit = 0
Originated = 1
@@ -602,6 +632,9 @@ if TYPE_CHECKING:
StellarPathPaymentStrictSendOp = 223
StellarClaimClaimableBalanceOp = 225
StellarSignedTx = 230
+ StellarInvokeHostFunctionOp = 235
+ StellarTxExtRequest = 238
+ StellarTxExt = 239
CardanoGetPublicKey = 305
CardanoPublicKey = 306
CardanoGetAddress = 307
diff --git a/core/src/trezor/messages.py b/core/src/trezor/messages.py
index 602f3439..a037f886 100644
--- a/core/src/trezor/messages.py
+++ b/core/src/trezor/messages.py
@@ -68,8 +68,12 @@ if TYPE_CHECKING:
from trezor.enums import SafetyCheckLevel # noqa: F401
from trezor.enums import SdProtectOperationType # noqa: F401
from trezor.enums import StellarAssetType # noqa: F401
+ from trezor.enums import StellarHostFunctionType # noqa: F401
from trezor.enums import StellarMemoType # noqa: F401
+ from trezor.enums import StellarSCValType # noqa: F401
from trezor.enums import StellarSignerType # noqa: F401
+ from trezor.enums import StellarSorobanAuthorizedFunctionType # noqa: F401
+ from trezor.enums import StellarSorobanCredentialsType # noqa: F401
from trezor.enums import TezosBallotType # noqa: F401
from trezor.enums import TezosContractType # noqa: F401
from trezor.enums import ThpMessageType # noqa: F401
@@ -6570,6 +6574,300 @@ if TYPE_CHECKING:
def is_type_of(cls, msg: Any) -> TypeGuard["StellarSignedTx"]:
return isinstance(msg, cls)
+ class StellarSCVal(protobuf.MessageType):
+ type: "StellarSCValType"
+ b: "bool | None"
+ u32: "int | None"
+ i32: "int | None"
+ u64: "int | None"
+ i64: "int | None"
+ timepoint: "int | None"
+ duration: "int | None"
+ u128: "StellarUInt128Parts | None"
+ i128: "StellarInt128Parts | None"
+ u256: "StellarUInt256Parts | None"
+ i256: "StellarInt256Parts | None"
+ bytes: "AnyBytes | None"
+ string: "AnyBytes | None"
+ symbol: "str | None"
+ vec: "list[StellarSCVal]"
+ map: "list[StellarSCValMapEntry]"
+ address: "str | None"
+
+ def __init__(
+ self,
+ *,
+ type: "StellarSCValType",
+ vec: "list[StellarSCVal] | None" = None,
+ map: "list[StellarSCValMapEntry] | None" = None,
+ b: "bool | None" = None,
+ u32: "int | None" = None,
+ i32: "int | None" = None,
+ u64: "int | None" = None,
+ i64: "int | None" = None,
+ timepoint: "int | None" = None,
+ duration: "int | None" = None,
+ u128: "StellarUInt128Parts | None" = None,
+ i128: "StellarInt128Parts | None" = None,
+ u256: "StellarUInt256Parts | None" = None,
+ i256: "StellarInt256Parts | None" = None,
+ bytes: "AnyBytes | None" = None,
+ string: "AnyBytes | None" = None,
+ symbol: "str | None" = None,
+ address: "str | None" = None,
+ ) -> None:
+ pass
+
+ @classmethod
+ def is_type_of(cls, msg: Any) -> TypeGuard["StellarSCVal"]:
+ return isinstance(msg, cls)
+
+ class StellarInvokeContractArgs(protobuf.MessageType):
+ contract_address: "str"
+ function_name: "str"
+ args: "list[StellarSCVal]"
+
+ def __init__(
+ self,
+ *,
+ contract_address: "str",
+ function_name: "str",
+ args: "list[StellarSCVal] | None" = None,
+ ) -> None:
+ pass
+
+ @classmethod
+ def is_type_of(cls, msg: Any) -> TypeGuard["StellarInvokeContractArgs"]:
+ return isinstance(msg, cls)
+
+ class StellarSorobanAuthorizedFunction(protobuf.MessageType):
+ type: "StellarSorobanAuthorizedFunctionType"
+ contract_fn: "StellarInvokeContractArgs | None"
+
+ def __init__(
+ self,
+ *,
+ type: "StellarSorobanAuthorizedFunctionType",
+ contract_fn: "StellarInvokeContractArgs | None" = None,
+ ) -> None:
+ pass
+
+ @classmethod
+ def is_type_of(cls, msg: Any) -> TypeGuard["StellarSorobanAuthorizedFunction"]:
+ return isinstance(msg, cls)
+
+ class StellarSorobanAuthorizedInvocation(protobuf.MessageType):
+ function: "StellarSorobanAuthorizedFunction"
+ sub_invocations: "list[StellarSorobanAuthorizedInvocation]"
+
+ def __init__(
+ self,
+ *,
+ function: "StellarSorobanAuthorizedFunction",
+ sub_invocations: "list[StellarSorobanAuthorizedInvocation] | None" = None,
+ ) -> None:
+ pass
+
+ @classmethod
+ def is_type_of(cls, msg: Any) -> TypeGuard["StellarSorobanAuthorizedInvocation"]:
+ return isinstance(msg, cls)
+
+ class StellarHostFunction(protobuf.MessageType):
+ type: "StellarHostFunctionType"
+ invoke_contract: "StellarInvokeContractArgs | None"
+
+ def __init__(
+ self,
+ *,
+ type: "StellarHostFunctionType",
+ invoke_contract: "StellarInvokeContractArgs | None" = None,
+ ) -> None:
+ pass
+
+ @classmethod
+ def is_type_of(cls, msg: Any) -> TypeGuard["StellarHostFunction"]:
+ return isinstance(msg, cls)
+
+ class StellarSorobanAddressCredentials(protobuf.MessageType):
+ address: "str"
+ nonce: "int"
+ signature_expiration_ledger: "int"
+ signature: "StellarSCVal"
+
+ def __init__(
+ self,
+ *,
+ address: "str",
+ nonce: "int",
+ signature_expiration_ledger: "int",
+ signature: "StellarSCVal",
+ ) -> None:
+ pass
+
+ @classmethod
+ def is_type_of(cls, msg: Any) -> TypeGuard["StellarSorobanAddressCredentials"]:
+ return isinstance(msg, cls)
+
+ class StellarSorobanCredentials(protobuf.MessageType):
+ type: "StellarSorobanCredentialsType"
+ address: "StellarSorobanAddressCredentials | None"
+
+ def __init__(
+ self,
+ *,
+ type: "StellarSorobanCredentialsType",
+ address: "StellarSorobanAddressCredentials | None" = None,
+ ) -> None:
+ pass
+
+ @classmethod
+ def is_type_of(cls, msg: Any) -> TypeGuard["StellarSorobanCredentials"]:
+ return isinstance(msg, cls)
+
+ class StellarSorobanAuthorizationEntry(protobuf.MessageType):
+ credentials: "StellarSorobanCredentials"
+ root_invocation: "StellarSorobanAuthorizedInvocation"
+
+ def __init__(
+ self,
+ *,
+ credentials: "StellarSorobanCredentials",
+ root_invocation: "StellarSorobanAuthorizedInvocation",
+ ) -> None:
+ pass
+
+ @classmethod
+ def is_type_of(cls, msg: Any) -> TypeGuard["StellarSorobanAuthorizationEntry"]:
+ return isinstance(msg, cls)
+
+ class StellarInvokeHostFunctionOp(protobuf.MessageType):
+ source_account: "str | None"
+ function: "StellarHostFunction"
+ auth: "list[StellarSorobanAuthorizationEntry]"
+
+ def __init__(
+ self,
+ *,
+ function: "StellarHostFunction",
+ auth: "list[StellarSorobanAuthorizationEntry] | None" = None,
+ source_account: "str | None" = None,
+ ) -> None:
+ pass
+
+ @classmethod
+ def is_type_of(cls, msg: Any) -> TypeGuard["StellarInvokeHostFunctionOp"]:
+ return isinstance(msg, cls)
+
+ class StellarTxExtRequest(protobuf.MessageType):
+
+ @classmethod
+ def is_type_of(cls, msg: Any) -> TypeGuard["StellarTxExtRequest"]:
+ return isinstance(msg, cls)
+
+ class StellarTxExt(protobuf.MessageType):
+ v: "int"
+ soroban_data: "AnyBytes | None"
+
+ def __init__(
+ self,
+ *,
+ v: "int",
+ soroban_data: "AnyBytes | None" = None,
+ ) -> None:
+ pass
+
+ @classmethod
+ def is_type_of(cls, msg: Any) -> TypeGuard["StellarTxExt"]:
+ return isinstance(msg, cls)
+
+ class StellarUInt128Parts(protobuf.MessageType):
+ hi: "int"
+ lo: "int"
+
+ def __init__(
+ self,
+ *,
+ hi: "int",
+ lo: "int",
+ ) -> None:
+ pass
+
+ @classmethod
+ def is_type_of(cls, msg: Any) -> TypeGuard["StellarUInt128Parts"]:
+ return isinstance(msg, cls)
+
+ class StellarInt128Parts(protobuf.MessageType):
+ hi: "int"
+ lo: "int"
+
+ def __init__(
+ self,
+ *,
+ hi: "int",
+ lo: "int",
+ ) -> None:
+ pass
+
+ @classmethod
+ def is_type_of(cls, msg: Any) -> TypeGuard["StellarInt128Parts"]:
+ return isinstance(msg, cls)
+
+ class StellarUInt256Parts(protobuf.MessageType):
+ hi_hi: "int"
+ hi_lo: "int"
+ lo_hi: "int"
+ lo_lo: "int"
+
+ def __init__(
+ self,
+ *,
+ hi_hi: "int",
+ hi_lo: "int",
+ lo_hi: "int",
+ lo_lo: "int",
+ ) -> None:
+ pass
+
+ @classmethod
+ def is_type_of(cls, msg: Any) -> TypeGuard["StellarUInt256Parts"]:
+ return isinstance(msg, cls)
+
+ class StellarInt256Parts(protobuf.MessageType):
+ hi_hi: "int"
+ hi_lo: "int"
+ lo_hi: "int"
+ lo_lo: "int"
+
+ def __init__(
+ self,
+ *,
+ hi_hi: "int",
+ hi_lo: "int",
+ lo_hi: "int",
+ lo_lo: "int",
+ ) -> None:
+ pass
+
+ @classmethod
+ def is_type_of(cls, msg: Any) -> TypeGuard["StellarInt256Parts"]:
+ return isinstance(msg, cls)
+
+ class StellarSCValMapEntry(protobuf.MessageType):
+ key: "StellarSCVal"
+ value: "StellarSCVal"
+
+ def __init__(
+ self,
+ *,
+ key: "StellarSCVal",
+ value: "StellarSCVal",
+ ) -> None:
+ pass
+
+ @classmethod
+ def is_type_of(cls, msg: Any) -> TypeGuard["StellarSCValMapEntry"]:
+ return isinstance(msg, cls)
+
class TelemetryGet(protobuf.MessageType):
@classmethod
diff --git a/core/src/trezor/strings.py b/core/src/trezor/strings.py
index 761d225a..53a43f16 100644
--- a/core/src/trezor/strings.py
+++ b/core/src/trezor/strings.py
@@ -1,6 +1,8 @@
import utime
from micropython import const
+from . import TR
+
_SECONDS_1970_TO_2000 = const(946684800)
@@ -86,26 +88,54 @@ def format_plural(string: str, count: int, plurals: str) -> str:
return string.format(count=count, plural=plural)
+_TIME_UNITS = (
+ (TR.plurals__days, 24 * 60 * 60 * 1000),
+ (TR.plurals__hours, 60 * 60 * 1000),
+ (TR.plurals__minutes, 60 * 1000),
+ (TR.plurals__seconds, 1000),
+ (TR.plurals__milliseconds, 1),
+)
+
+
+def _format_duration(
+ milliseconds: int,
+ units: tuple[tuple[str, int], ...] = _TIME_UNITS,
+ truncate: bool = False,
+) -> str:
+ """
+ Returns human-friendly representation of a duration given in milliseconds.
+
+ With `truncate=True` only the largest matching unit is shown, dropping all
+ decimals (e.g. 119 seconds -> "1 minute").
+
+ With `truncate=False` the duration is formatted exactly, joining all
+ non-zero components (e.g. 61 seconds -> "1 minute 1 second").
+ """
+ components: list[str] = []
+ remainder = milliseconds
+ for unit, divisor in units:
+ count, remainder = divmod(remainder, divisor)
+ if count:
+ components.append(format_plural("{count} {plural}", count, unit))
+ if truncate:
+ break
+
+ # empty components means zero duration; use the smallest unit
+ return " ".join(components) or format_plural("{count} {plural}", 0, units[-1][0])
+
+
def format_duration_ms(milliseconds: int) -> str:
"""
Returns human-friendly representation of a duration. Truncates all decimals.
"""
- from . import TR
-
- units: tuple[tuple[str, int], ...] = (
- (TR.plurals__days, 24 * 60 * 60 * 1000),
- (TR.plurals__hours, 60 * 60 * 1000),
- (TR.plurals__minutes, 60 * 1000),
- (TR.plurals__seconds, 1000),
- )
- for unit, divisor in units:
- if milliseconds >= divisor:
- break
- else:
- unit = TR.plurals__milliseconds
- divisor = 1
+ return _format_duration(milliseconds, truncate=True)
+
- return format_plural("{count} {plural}", milliseconds // divisor, unit)
+def format_duration(seconds: int) -> str:
+ """
+ Returns human-friendly representation of a duration given in seconds.
+ """
+ return _format_duration(seconds * 1000, units=_TIME_UNITS[:-1])
def format_timestamp(timestamp: int) -> str:
diff --git a/core/tests/test_apps.stellar.address.py b/core/tests/test_apps.stellar.address.py
index b808e926..7717e65d 100644
--- a/core/tests/test_apps.stellar.address.py
+++ b/core/tests/test_apps.stellar.address.py
@@ -1,10 +1,20 @@
# flake8: noqa: F403,F405
from common import * # isort:skip
-from trezor.wire import ProcessError
+from trezor.wire import DataError
if not utils.BITCOIN_ONLY:
- from apps.stellar.helpers import address_from_public_key, public_key_from_address
+ from apps.stellar.helpers import (
+ STRKEY_CLAIMABLE_BALANCE,
+ STRKEY_CONTRACT,
+ STRKEY_ED25519_PUBLIC_KEY,
+ STRKEY_LIQUIDITY_POOL,
+ STRKEY_MUXED_ACCOUNT,
+ address_from_public_key,
+ decode_strkey,
+ encode_strkey,
+ public_key_from_address,
+ )
@unittest.skipUnless(not utils.BITCOIN_ONLY, "altcoin")
@@ -70,11 +80,114 @@ class TestStellarAddress(unittest.TestCase):
)
def test_invalid_address(self):
- with self.assertRaises(ProcessError):
+ with self.assertRaises(DataError):
public_key_from_address(
"GCN2K2HG53AWX2SP5UHRPMJUUHLJF2XBTGSXROTPWRGAYJCDDP63J2AA"
) # invalid checksum
+ # Strkey round-trip test vectors from SEP-0023 "Valid test cases":
+ # https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0023.md#tests
+ # Each case asserts both directions: encode_strkey(version, data) -> strkey
+ # and decode_strkey(strkey) -> (version, data).
+ def test_strkey_account(self):
+ # ED25519 public key (G... address)
+ pubkey = unhexlify(
+ "3f0c34bf93ad0d9971d04ccc90f705511c838aad9734a4a2fb0d7a03fc7fe89a"
+ )
+ strkey = "GA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVSGZ"
+ self.assertEqual(encode_strkey(STRKEY_ED25519_PUBLIC_KEY, pubkey), strkey)
+ self.assertEqual(decode_strkey(strkey), (STRKEY_ED25519_PUBLIC_KEY, pubkey))
+
+ def test_strkey_contract(self):
+ # contract address (C... address)
+ contract_hash = unhexlify(
+ "3f0c34bf93ad0d9971d04ccc90f705511c838aad9734a4a2fb0d7a03fc7fe89a"
+ )
+ strkey = "CA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJUWDA"
+ self.assertEqual(encode_strkey(STRKEY_CONTRACT, contract_hash), strkey)
+ self.assertEqual(decode_strkey(strkey), (STRKEY_CONTRACT, contract_hash))
+
+ def test_strkey_muxed_account(self):
+ # muxed account (M... address): 32 bytes public key + 8 bytes ID
+ # ed25519: GA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVSGZ
+ pubkey = "3f0c34bf93ad0d9971d04ccc90f705511c838aad9734a4a2fb0d7a03fc7fe89a"
+ for muxed_id, strkey in (
+ # id: 9223372036854775808 (0x8000000000000000)
+ (
+ "8000000000000000",
+ "MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVAAAAAAAAAAAAAJLK",
+ ),
+ # id: 0
+ (
+ "0000000000000000",
+ "MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJUAAAAAAAAAAAACJUQ",
+ ),
+ # id: 1024 (extra variant, not part of SEP-0023; muxed addresses can
+ # be generated at https://lab.stellar.org/account/muxed-create)
+ (
+ "0000000000000400",
+ "MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJUAAAAAAAAAAEABLYI",
+ ),
+ ):
+ muxed_data = unhexlify(pubkey + muxed_id)
+ self.assertEqual(encode_strkey(STRKEY_MUXED_ACCOUNT, muxed_data), strkey)
+ self.assertEqual(decode_strkey(strkey), (STRKEY_MUXED_ACCOUNT, muxed_data))
+
+ def test_strkey_claimable_balance(self):
+ # claimable balance (B... address): 1 byte type (v0 = 0x00) + 32 bytes hash
+ balance_id = unhexlify(
+ "00" # type v0
+ "3f0c34bf93ad0d9971d04ccc90f705511c838aad9734a4a2fb0d7a03fc7fe89a" # hash
+ )
+ strkey = "BAAD6DBUX6J22DMZOHIEZTEQ64CVCHEDRKWZONFEUL5Q26QD7R76RGR4TU"
+ self.assertEqual(encode_strkey(STRKEY_CLAIMABLE_BALANCE, balance_id), strkey)
+ self.assertEqual(decode_strkey(strkey), (STRKEY_CLAIMABLE_BALANCE, balance_id))
+
+ def test_strkey_liquidity_pool(self):
+ # liquidity pool (L... address): 32 bytes hash
+ pool_id = unhexlify(
+ "3f0c34bf93ad0d9971d04ccc90f705511c838aad9734a4a2fb0d7a03fc7fe89a"
+ )
+ strkey = "LA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJUPJN"
+ self.assertEqual(encode_strkey(STRKEY_LIQUIDITY_POOL, pool_id), strkey)
+ self.assertEqual(decode_strkey(strkey), (STRKEY_LIQUIDITY_POOL, pool_id))
+
+ # Invalid test cases from SEP-0023, minus the P... (signed payload) vectors,
+ # which are rejected as an unsupported strkey version.
+ def test_decode_strkey_invalid(self):
+ for strkey in (
+ "GAAAAAAAACGC6", # payload length 5, G expects 32
+ "MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJUAAAAAAAAAAAACJUR", # unused trailing bit not zero
+ "GA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVSGZA", # length 1 mod 8
+ "GA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJUACUSI", # payload length 33, G expects 32
+ "G47QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVP2I", # non-zero algorithm bits
+ "MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVAAAAAAAAAAAAAJLKA", # length 6 mod 8
+ "MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVAAAAAAAAAAAAAAV75I", # payload length 41, M expects 40
+ "M47QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJUAAAAAAAAAAAACJUQ", # non-zero algorithm bits
+ "MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJUAAAAAAAAAAAACJUK===", # explicit padding not allowed
+ "MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJUAAAAAAAAAAAACJUO", # invalid checksum
+ "BAAD6DBUX6J22DMZOHIEZTEQ64CVCHEDRKWZONFEUL5Q26QD7R76RGR4TV", # unused trailing 2-bits not zero
+ "BAAT6DBUX6J22DMZOHIEZTEQ64CVCHEDRKWZONFEUL5Q26QD7R76RGXACA", # claimable balance type byte not v0
+ ):
+ with self.assertRaises((DataError, ValueError)):
+ decode_strkey(strkey)
+
+ def test_decode_strkey_invalid_payload_size(self):
+ # a canonical strkey (valid checksum) whose payload length does not
+ # match its version is rejected
+ TESTS = [
+ (STRKEY_ED25519_PUBLIC_KEY, 31), # G expects 32
+ (STRKEY_ED25519_PUBLIC_KEY, 33),
+ (STRKEY_CONTRACT, 33), # C expects 32
+ (STRKEY_MUXED_ACCOUNT, 32), # M expects 40
+ (STRKEY_CLAIMABLE_BALANCE, 32), # B expects 33
+ (STRKEY_LIQUIDITY_POOL, 31), # L expects 32
+ ]
+ for version, size in TESTS:
+ strkey = encode_strkey(version, bytes(size))
+ with self.assertRaises(DataError):
+ decode_strkey(strkey)
+
if __name__ == "__main__":
unittest.main()
diff --git a/core/tests/test_apps.stellar.layout.py b/core/tests/test_apps.stellar.layout.py
new file mode 100644
index 00000000..39c69b8b
--- /dev/null
+++ b/core/tests/test_apps.stellar.layout.py
@@ -0,0 +1,252 @@
+# flake8: noqa: F403,F405
+from common import * # isort:skip
+
+if not utils.BITCOIN_ONLY:
+ from trezor.enums import (
+ StellarHostFunctionType,
+ StellarSCValType,
+ StellarSorobanAuthorizedFunctionType,
+ StellarSorobanCredentialsType,
+ )
+ from trezor.messages import (
+ StellarHostFunction,
+ StellarInt128Parts,
+ StellarInt256Parts,
+ StellarInvokeContractArgs,
+ StellarSCVal,
+ StellarSCValMapEntry,
+ StellarSorobanAuthorizationEntry,
+ StellarSorobanAuthorizedFunction,
+ StellarSorobanAuthorizedInvocation,
+ StellarSorobanCredentials,
+ StellarUInt128Parts,
+ StellarUInt256Parts,
+ )
+
+ from apps.stellar.operations.layout import (
+ _format_i128,
+ _format_i256,
+ _format_sc_val,
+ _format_u128,
+ _format_u256,
+ _is_root_auth_entry,
+ )
+
+ def _u32(value):
+ return StellarSCVal(type=StellarSCValType.SCV_U32, u32=value)
+
+ def _u64(value):
+ return StellarSCVal(type=StellarSCValType.SCV_U64, u64=value)
+
+ def _bytes(value):
+ return StellarSCVal(type=StellarSCValType.SCV_BYTES, bytes=value)
+
+ def _string(value):
+ return StellarSCVal(type=StellarSCValType.SCV_STRING, string=value)
+
+ def _symbol(value):
+ return StellarSCVal(type=StellarSCValType.SCV_SYMBOL, symbol=value)
+
+ def _vec(items):
+ return StellarSCVal(type=StellarSCValType.SCV_VEC, vec=items)
+
+ def _map(entries):
+ return StellarSCVal(type=StellarSCValType.SCV_MAP, map=entries)
+
+ def _entry(key, value):
+ return StellarSCValMapEntry(key=key, value=value)
+
+
+@unittest.skipUnless(not utils.BITCOIN_ONLY, "altcoin")
+class TestStellarFormatIntegers(unittest.TestCase):
+ def test_format_u128(self):
+ TESTS = [
+ ((0, 0), "0"),
+ ((0, 1), "1"),
+ ((1, 0), str(2**64)),
+ ((0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF), str(2**128 - 1)),
+ ]
+ for (hi, lo), expected in TESTS:
+ self.assertEqual(_format_u128(StellarUInt128Parts(hi=hi, lo=lo)), expected)
+
+ def test_format_i128(self):
+ TESTS = [
+ ((0, 0), "0"),
+ ((0, 1), "1"),
+ ((-1, 0xFFFFFFFFFFFFFFFF), "-1"),
+ ((1, 0), str(2**64)),
+ ((-1, 0), str(-(2**64))),
+ ((0x7FFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF), str(2**127 - 1)),
+ ((-0x8000000000000000, 0), str(-(2**127))),
+ ]
+ for (hi, lo), expected in TESTS:
+ self.assertEqual(_format_i128(StellarInt128Parts(hi=hi, lo=lo)), expected)
+
+ def test_format_u256(self):
+ TESTS = [
+ ((0, 0, 0, 0), "0"),
+ ((0, 0, 0, 1), "1"),
+ ((0, 0, 1, 0), str(2**64)),
+ ((0, 1, 0, 0), str(2**128)),
+ ((1, 0, 0, 0), str(2**192)),
+ (
+ (
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ ),
+ str(2**256 - 1),
+ ),
+ ]
+ for (hi_hi, hi_lo, lo_hi, lo_lo), expected in TESTS:
+ parts = StellarUInt256Parts(
+ hi_hi=hi_hi, hi_lo=hi_lo, lo_hi=lo_hi, lo_lo=lo_lo
+ )
+ self.assertEqual(_format_u256(parts), expected)
+
+ def test_format_i256(self):
+ TESTS = [
+ ((0, 0, 0, 0), "0"),
+ ((0, 0, 0, 1), "1"),
+ (
+ (
+ -1,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ ),
+ "-1",
+ ),
+ ((0, 0, 1, 0), str(2**64)),
+ ((-1, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0), str(-(2**64))),
+ ((0, 1, 0, 0), str(2**128)),
+ ((-1, 0xFFFFFFFFFFFFFFFF, 0, 0), str(-(2**128))),
+ ((1, 0, 0, 0), str(2**192)),
+ ((-1, 0, 0, 0), str(-(2**192))),
+ (
+ (
+ 0x7FFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF,
+ ),
+ str(2**255 - 1),
+ ),
+ ((-0x8000000000000000, 0, 0, 0), str(-(2**255))),
+ ]
+ for (hi_hi, hi_lo, lo_hi, lo_lo), expected in TESTS:
+ parts = StellarInt256Parts(
+ hi_hi=hi_hi, hi_lo=hi_lo, lo_hi=lo_hi, lo_lo=lo_lo
+ )
+ self.assertEqual(_format_i256(parts), expected)
+
+
+@unittest.skipUnless(not utils.BITCOIN_ONLY, "altcoin")
+class TestStellarFormatScVal(unittest.TestCase):
+ def test_format_bytes(self):
+ TESTS = [
+ (b"", "0x"),
+ (b"\xde\xad\xbe\xef", "0xdeadbeef"),
+ ]
+ for value, expected in TESTS:
+ self.assertEqual(_format_sc_val(_bytes(value)), expected)
+
+ def test_format_string(self):
+ TESTS = [
+ (b"hello", '"hello"'),
+ # embedded quotes and backslashes are escaped so a string cannot forge
+ # the surrounding quotes (and thus the vec/map separators)
+ (b'a"b', r'"a\"b"'),
+ (b"a\\b", r'"a\\b"'),
+ (b'a\\"b', r'"a\\\"b"'),
+ # control characters are passed through unescaped
+ (b"a\nb", '"a\nb"'),
+ # non-UTF-8 bytes fall back to hex, like SCV_BYTES
+ (b"\xff\xfe", "0xfffe"),
+ ]
+ for value, expected in TESTS:
+ self.assertEqual(_format_sc_val(_string(value)), expected)
+
+ def test_format_symbol(self):
+ TESTS = [
+ ("transfer", '"transfer"'),
+ ('a"b', r'"a\"b"'),
+ ]
+ for value, expected in TESTS:
+ self.assertEqual(_format_sc_val(_symbol(value)), expected)
+
+ def test_format_vec(self):
+ TESTS = [
+ ([], "[]"),
+ ([_u32(1), _symbol("a")], '[1, "a"]'),
+ ([_vec([_u32(1)])], "[[1]]"),
+ # a string element cannot forge additional vec items
+ ([_string(b'", "x')], r'["\", \"x"]'),
+ ]
+ for items, expected in TESTS:
+ self.assertEqual(_format_sc_val(_vec(items)), expected)
+
+ def test_format_map(self):
+ TESTS = [
+ ([], "{}"),
+ ([_entry(_symbol("amount"), _u32(5))], '{"amount": 5}'),
+ (
+ [
+ _entry(_symbol("amount"), _u32(5)),
+ # a string value cannot forge map structure
+ _entry(_symbol("k"), _string(b'", "x')),
+ ],
+ r'{"amount": 5, "k": "\", \"x"}',
+ ),
+ ]
+ for entries, expected in TESTS:
+ self.assertEqual(_format_sc_val(_map(entries)), expected)
+
+
+# valid contract (C...) strkeys, see test_apps.stellar.address.py for the format
+_CONTRACT_A = "CAAACAQDAQCQMBYIBEFAWDANBYHRAEISCMKBKFQXDAMRUGY4DUPB6N4O"
+_CONTRACT_B = "CBSGKZTHNBUWU23MNVXG64DROJZXI5LWO54HS6T3PR6X474AQGBIHDKP"
+
+
+@unittest.skipUnless(not utils.BITCOIN_ONLY, "altcoin")
+class TestStellarIsRootAuthEntry(unittest.TestCase):
+ def test_is_root_auth_entry(self):
+ invoked = StellarHostFunction(
+ type=StellarHostFunctionType.HOST_FUNCTION_TYPE_INVOKE_CONTRACT,
+ invoke_contract=StellarInvokeContractArgs(
+ contract_address=_CONTRACT_A, function_name="submit", args=[_u32(1)]
+ ),
+ )
+
+ TESTS = [
+ ((_CONTRACT_A, "submit", [_u32(1)]), True), # identical
+ ((_CONTRACT_A, "submit", [_u32(2)]), False), # different arg value
+ ((_CONTRACT_A, "submit", [_u64(1)]), False), # different arg type
+ ((_CONTRACT_A, "submit", [_u32(1), _u32(1)]), False), # extra arg
+ ((_CONTRACT_A, "submit", []), False), # missing arg
+ ((_CONTRACT_A, "swap", [_u32(1)]), False), # different function
+ ((_CONTRACT_B, "submit", [_u32(1)]), False), # different contract
+ ]
+ for (contract, function, args), is_root in TESTS:
+ auth_entry = StellarSorobanAuthorizationEntry(
+ credentials=StellarSorobanCredentials(
+ type=StellarSorobanCredentialsType.SOROBAN_CREDENTIALS_SOURCE_ACCOUNT
+ ),
+ root_invocation=StellarSorobanAuthorizedInvocation(
+ function=StellarSorobanAuthorizedFunction(
+ type=StellarSorobanAuthorizedFunctionType.SOROBAN_AUTHORIZED_FUNCTION_TYPE_CONTRACT_FN,
+ contract_fn=StellarInvokeContractArgs(
+ contract_address=contract,
+ function_name=function,
+ args=args,
+ ),
+ ),
+ sub_invocations=[],
+ ),
+ )
+ self.assertEqual(_is_root_auth_entry(auth_entry, invoked), is_root)
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/core/tests/test_apps.stellar.writers.py b/core/tests/test_apps.stellar.writers.py
new file mode 100644
index 00000000..195e93ce
--- /dev/null
+++ b/core/tests/test_apps.stellar.writers.py
@@ -0,0 +1,85 @@
+# flake8: noqa: F403,F405
+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
+
+
+@unittest.skipUnless(not utils.BITCOIN_ONLY, "altcoin")
+class TestStellarWriters(unittest.TestCase):
+ def test_write_int32(self):
+ TESTS = [
+ (0, "00000000"),
+ (1, "00000001"),
+ (127, "0000007f"),
+ (256, "00000100"),
+ (-1, "ffffffff"),
+ (-128, "ffffff80"),
+ (-256, "ffffff00"),
+ (0x7FFFFFFF, "7fffffff"), # INT32_MAX
+ (-0x80000000, "80000000"), # INT32_MIN
+ ]
+ for value, expected in TESTS:
+ w = bytearray()
+ write_int32(w, value)
+ self.assertEqual(w, unhexlify(expected), msg=f"write_int32({value})")
+
+ def test_write_int32_out_of_range(self):
+ TESTS = [
+ 0x80000000, # INT32_MAX + 1
+ -0x80000001, # INT32_MIN - 1
+ 0x100000000, # way out of range
+ ]
+ for value in TESTS:
+ w = bytearray()
+ with self.assertRaises(ValueError):
+ write_int32(w, value)
+
+ def test_write_int64(self):
+ TESTS = [
+ (0, "0000000000000000"),
+ (1, "0000000000000001"),
+ (127, "000000000000007f"),
+ (0x100000000, "0000000100000000"), # larger than int32
+ (-1, "ffffffffffffffff"),
+ (-128, "ffffffffffffff80"),
+ (-0x100000000, "ffffffff00000000"),
+ (0x7FFFFFFFFFFFFFFF, "7fffffffffffffff"), # INT64_MAX
+ (-0x8000000000000000, "8000000000000000"), # INT64_MIN
+ ]
+ for value, expected in TESTS:
+ w = bytearray()
+ write_int64(w, value)
+ self.assertEqual(w, unhexlify(expected), msg=f"write_int64({value})")
+
+ def test_write_int64_out_of_range(self):
+ TESTS = [
+ 0x8000000000000000, # INT64_MAX + 1
+ -0x8000000000000001, # INT64_MIN - 1
+ 0x10000000000000000, # way out of range
+ ]
+ for value in TESTS:
+ w = bytearray()
+ with self.assertRaises(ValueError):
+ write_int64(w, value)
+
+ def test_write_sc_symbol(self):
+ # the XDR limit is 32 bytes
+ for symbol in ("", "a", "a" * 32):
+ _write_sc_symbol(bytearray(), symbol)
+
+ def test_write_sc_symbol_too_long(self):
+ # the limit is on UTF-8 bytes, not code points
+ for symbol in (
+ "a" * 33, # 33 ASCII bytes
+ "ž" * 32, # 32 two-byte code points -> 64 UTF-8 bytes
+ ):
+ with self.assertRaises(DataError):
+ _write_sc_symbol(bytearray(), symbol)
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/core/tests/test_trezor.strings.py b/core/tests/test_trezor.strings.py
index 64ec46b5..ff1f50c5 100644
--- a/core/tests/test_trezor.strings.py
+++ b/core/tests/test_trezor.strings.py
@@ -123,6 +123,27 @@ class TestStrings(unittest.TestCase):
for v in VECTORS:
self.assertEqual(strings.format_duration_ms(v[0]), v[1])
+ def test_format_duration(self):
+ VECTORS = [
+ (0, "0 seconds"),
+ (1, "1 second"),
+ (59, "59 seconds"),
+ (60, "1 minute"),
+ (61, "1 minute 1 second"),
+ (60 * 60, "1 hour"),
+ # zero components are omitted, even in the middle
+ (60 * 60 + 1, "1 hour 1 second"),
+ (60 * 60 + 61, "1 hour 1 minute 1 second"),
+ (24 * 60 * 60, "1 day"),
+ (
+ 2 * 24 * 60 * 60 + 3 * 60 * 60 + 4 * 60 + 5,
+ "2 days 3 hours 4 minutes 5 seconds",
+ ),
+ (365 * 24 * 60 * 60, "365 days"),
+ ]
+ for value, expected in VECTORS:
+ self.assertEqual(strings.format_duration(value), expected)
+
def test_format_timestamp(self):
VECTORS = [
(0, "1970-01-01 00:00:00"),
diff --git a/core/translations/en.json b/core/translations/en.json
index 9b5d6074..8c36a116 100644
--- a/core/translations/en.json
+++ b/core/translations/en.json
@@ -2826,11 +2826,14 @@
"stellar__delete_trust": "Delete trust",
"stellar__destination": "Destination",
"stellar__exchanges_require_memo": "Memo is not set.\nTypically needed when sending to exchanges.",
+ "stellar__ext_auth": "External Authorizations",
+ "stellar__ext_auth_message": "Transaction contains additional invocations authorized by external means.",
"stellar__final_confirm": "Final confirm",
"stellar__hash": "Hash",
"stellar__high": "High",
"stellar__home_domain": "Home Domain",
"stellar__inflation": "Inflation",
+ "stellar__invoke_contract": "Invoke Contract",
"stellar__issuer_template": "{0} issuer",
"stellar__key": "Key",
"stellar__limit": "Limit",
@@ -3299,10 +3302,12 @@
"words__address": "Address",
"words__amount": "Amount",
"words__are_you_sure": "Are you sure?",
+ "words__arguments": "Arguments",
"words__array_of": "Array of",
"words__asset": "Asset",
"words__assets": "Assets",
"words__authenticate": "Authenticate",
+ "words__authorization": "Authorization",
"words__blockhash": "Blockhash",
"words__bluetooth": "Bluetooth",
"words__buying": "Buying",
@@ -3356,6 +3361,7 @@
"Delizia": "from",
"Eckhart": ""
},
+ "words__function": "Function",
"words__important": "Important",
"words__instructions": "Instructions",
"words__intent": "Intent",
diff --git a/core/translations/order.json b/core/translations/order.json
index 1bb08252..853b8d4e 100644
--- a/core/translations/order.json
+++ b/core/translations/order.json
@@ -1253,5 +1253,11 @@
"1251": "ethereum__smart_info",
"1252": "ethereum__to",
"1253": "ethereum__calldata_digest",
- "1254": "pin__reenter_new_description"
+ "1254": "pin__reenter_new_description",
+ "1255": "stellar__ext_auth",
+ "1256": "stellar__ext_auth_message",
+ "1257": "stellar__invoke_contract",
+ "1258": "words__arguments",
+ "1259": "words__authorization",
+ "1260": "words__function"
}
diff --git a/core/translations/signatures.json b/core/translations/signatures.json
index 924f8903..dcbdcb0f 100644
--- a/core/translations/signatures.json
+++ b/core/translations/signatures.json
@@ -1,8 +1,8 @@
{
"current": {
- "merkle_root": "0dae1f6e3ef29a6213b5774aa455ac994ff1e5484469788f2ac91ed1b3a04184",
- "datetime": "2026-07-21T12:41:30.341510+00:00",
- "commit": "6aee0a36b2629552a5766c176b765c00feed9602"
+ "merkle_root": "1ed3d1cf544c1172247be18991f0eb764241609e27089decbf375f12bf8befe9",
+ "datetime": "2026-07-22T12:42:32.025263+00:00",
+ "commit": "4bdca3d276ea349e448315634f177e1e9c56f8a0"
},
"history": [
{
diff --git a/legacy/firmware/protob/Makefile b/legacy/firmware/protob/Makefile
index 776c40ac..8eb7f8cc 100644
--- a/legacy/firmware/protob/Makefile
+++ b/legacy/firmware/protob/Makefile
@@ -17,7 +17,7 @@ SKIPPED_MESSAGES := Cardano DebugMonero Eos Monero Ontology Ripple SdProtect Tez
EthereumSignTypedData EthereumTypedDataStructRequest EthereumTypedDataStructAck \
EthereumTypedDataValueRequest EthereumTypedDataValueAck ShowDeviceTutorial \
UnlockBootloader AuthenticateDevice AuthenticityProof GetAuthenticityProofChunk \
- Solana StellarClaimClaimableBalanceOp SetBrightness \
+ Solana StellarClaimClaimableBalanceOp StellarInvokeHostFunctionOp StellarTxExt SetBrightness \
ChangeLanguage DataChunkRequest DataChunkAck Thp \
BenchmarkListNames BenchmarkRun BenchmarkNames BenchmarkResult \
NostrGetPubkey NostrPubkey NostrSignEvent NostrEventSignature \
diff --git a/python/.changelog.d/3471.incompatible b/python/.changelog.d/3471.incompatible
new file mode 100644
index 00000000..90782820
--- /dev/null
+++ b/python/.changelog.d/3471.incompatible
@@ -0,0 +1 @@
+Stellar: Enable signing Soroban smart contract transactions; `sign_tx` / `from_envelope` now accepts / returns a transaction extension.
diff --git a/python/pyproject.toml b/python/pyproject.toml
index ea370f03..fd722100 100644
--- a/python/pyproject.toml
+++ b/python/pyproject.toml
@@ -40,14 +40,14 @@ hidapi = ["hidapi>=0.7.99.post20"]
ethereum = ["web3>=5"]
qt-widgets = ["PyQt5"]
extra = ["Pillow>=10"]
-stellar = ["stellar-sdk>=6"]
+stellar = ["stellar-sdk>=13"]
ble = ["bleak>=1.1.0"]
full = [
"hidapi>=0.7.99.post20",
"web3>=5",
"PyQt5",
"Pillow>=10",
- "stellar-sdk>=6",
+ "stellar-sdk>=13",
"bleak>=1.1.0",
]
diff --git a/python/src/trezorlib/cli/stellar.py b/python/src/trezorlib/cli/stellar.py
index 08452009..87ccffd5 100644
--- a/python/src/trezorlib/cli/stellar.py
+++ b/python/src/trezorlib/cli/stellar.py
@@ -108,7 +108,9 @@ def sign_transaction(
sys.exit(1)
address_n = tools.parse_path(address)
- tx, operations = stellar.from_envelope(envelope)
- resp = stellar.sign_tx(session, tx, operations, address_n, network_passphrase)
+ tx, operations, tx_ext = stellar.from_envelope(envelope)
+ resp = stellar.sign_tx(
+ session, tx, operations, tx_ext, address_n, network_passphrase
+ )
return base64.b64encode(resp.signature)
diff --git a/python/src/trezorlib/messages.py b/python/src/trezorlib/messages.py
index 28787d91..9b10da80 100644
--- a/python/src/trezorlib/messages.py
+++ b/python/src/trezorlib/messages.py
@@ -438,6 +438,40 @@ class StellarSignerType(IntEnum):
HASH = 2
+class StellarSCValType(IntEnum):
+ SCV_BOOL = 0
+ SCV_VOID = 1
+ SCV_U32 = 3
+ SCV_I32 = 4
+ SCV_U64 = 5
+ SCV_I64 = 6
+ SCV_TIMEPOINT = 7
+ SCV_DURATION = 8
+ SCV_U128 = 9
+ SCV_I128 = 10
+ SCV_U256 = 11
+ SCV_I256 = 12
+ SCV_BYTES = 13
+ SCV_STRING = 14
+ SCV_SYMBOL = 15
+ SCV_VEC = 16
+ SCV_MAP = 17
+ SCV_ADDRESS = 18
+
+
+class StellarSorobanAuthorizedFunctionType(IntEnum):
+ SOROBAN_AUTHORIZED_FUNCTION_TYPE_CONTRACT_FN = 0
+
+
+class StellarHostFunctionType(IntEnum):
+ HOST_FUNCTION_TYPE_INVOKE_CONTRACT = 0
+
+
+class StellarSorobanCredentialsType(IntEnum):
+ SOROBAN_CREDENTIALS_SOURCE_ACCOUNT = 0
+ SOROBAN_CREDENTIALS_ADDRESS = 1
+
+
class TezosContractType(IntEnum):
Implicit = 0
Originated = 1
@@ -662,6 +696,9 @@ class MessageType(IntEnum):
StellarPathPaymentStrictSendOp = 223
StellarClaimClaimableBalanceOp = 225
StellarSignedTx = 230
+ StellarInvokeHostFunctionOp = 235
+ StellarTxExtRequest = 238
+ StellarTxExt = 239
CardanoGetPublicKey = 305
CardanoPublicKey = 306
CardanoGetAddress = 307
@@ -8412,6 +8449,337 @@ class StellarSignedTx(protobuf.MessageType):
self.signature = signature
+class StellarSCVal(protobuf.MessageType):
+ MESSAGE_WIRE_TYPE = None
+ FIELDS = {
+ 1: protobuf.Field("type", "StellarSCValType", repeated=False, required=True),
+ 2: protobuf.Field("b", "bool", repeated=False, required=False, default=None),
+ 4: protobuf.Field("u32", "uint32", repeated=False, required=False, default=None),
+ 5: protobuf.Field("i32", "sint32", repeated=False, required=False, default=None),
+ 6: protobuf.Field("u64", "uint64", repeated=False, required=False, default=None),
+ 7: protobuf.Field("i64", "sint64", repeated=False, required=False, default=None),
+ 8: protobuf.Field("timepoint", "uint64", repeated=False, required=False, default=None),
+ 9: protobuf.Field("duration", "uint64", repeated=False, required=False, default=None),
+ 10: protobuf.Field("u128", "StellarUInt128Parts", repeated=False, required=False, default=None),
+ 11: protobuf.Field("i128", "StellarInt128Parts", repeated=False, required=False, default=None),
+ 12: protobuf.Field("u256", "StellarUInt256Parts", repeated=False, required=False, default=None),
+ 13: protobuf.Field("i256", "StellarInt256Parts", repeated=False, required=False, default=None),
+ 14: protobuf.Field("bytes", "bytes", repeated=False, required=False, default=None),
+ 15: protobuf.Field("string", "bytes", repeated=False, required=False, default=None),
+ 16: protobuf.Field("symbol", "string", repeated=False, required=False, default=None),
+ 17: protobuf.Field("vec", "StellarSCVal", repeated=True, required=False, default=None),
+ 18: protobuf.Field("map", "StellarSCValMapEntry", repeated=True, required=False, default=None),
+ 19: protobuf.Field("address", "string", repeated=False, required=False, default=None),
+ }
+
+ def __init__(
+ self,
+ *,
+ type: "StellarSCValType",
+ vec: Optional[Sequence["StellarSCVal"]] = None,
+ map: Optional[Sequence["StellarSCValMapEntry"]] = None,
+ b: Optional["bool"] = None,
+ u32: Optional["int"] = None,
+ i32: Optional["int"] = None,
+ u64: Optional["int"] = None,
+ i64: Optional["int"] = None,
+ timepoint: Optional["int"] = None,
+ duration: Optional["int"] = None,
+ u128: Optional["StellarUInt128Parts"] = None,
+ i128: Optional["StellarInt128Parts"] = None,
+ u256: Optional["StellarUInt256Parts"] = None,
+ i256: Optional["StellarInt256Parts"] = None,
+ bytes: Optional["bytes"] = None,
+ string: Optional["bytes"] = None,
+ symbol: Optional["str"] = None,
+ address: Optional["str"] = None,
+ ) -> None:
+ self.vec: Sequence["StellarSCVal"] = vec if vec is not None else []
+ self.map: Sequence["StellarSCValMapEntry"] = map if map is not None else []
+ self.type = type
+ self.b = b
+ self.u32 = u32
+ self.i32 = i32
+ self.u64 = u64
+ self.i64 = i64
+ self.timepoint = timepoint
+ self.duration = duration
+ self.u128 = u128
+ self.i128 = i128
+ self.u256 = u256
+ self.i256 = i256
+ self.bytes = bytes
+ self.string = string
+ self.symbol = symbol
+ self.address = address
+
+
+class StellarInvokeContractArgs(protobuf.MessageType):
+ MESSAGE_WIRE_TYPE = None
+ FIELDS = {
+ 1: protobuf.Field("contract_address", "string", repeated=False, required=True),
+ 2: protobuf.Field("function_name", "string", repeated=False, required=True),
+ 3: protobuf.Field("args", "StellarSCVal", repeated=True, required=False, default=None),
+ }
+
+ def __init__(
+ self,
+ *,
+ contract_address: "str",
+ function_name: "str",
+ args: Optional[Sequence["StellarSCVal"]] = None,
+ ) -> None:
+ self.args: Sequence["StellarSCVal"] = args if args is not None else []
+ self.contract_address = contract_address
+ self.function_name = function_name
+
+
+class StellarSorobanAuthorizedFunction(protobuf.MessageType):
+ MESSAGE_WIRE_TYPE = None
+ FIELDS = {
+ 1: protobuf.Field("type", "StellarSorobanAuthorizedFunctionType", repeated=False, required=True),
+ 2: protobuf.Field("contract_fn", "StellarInvokeContractArgs", repeated=False, required=False, default=None),
+ }
+
+ def __init__(
+ self,
+ *,
+ type: "StellarSorobanAuthorizedFunctionType",
+ contract_fn: Optional["StellarInvokeContractArgs"] = None,
+ ) -> None:
+ self.type = type
+ self.contract_fn = contract_fn
+
+
+class StellarSorobanAuthorizedInvocation(protobuf.MessageType):
+ MESSAGE_WIRE_TYPE = None
+ FIELDS = {
+ 1: protobuf.Field("function", "StellarSorobanAuthorizedFunction", repeated=False, required=True),
+ 2: protobuf.Field("sub_invocations", "StellarSorobanAuthorizedInvocation", repeated=True, required=False, default=None),
+ }
+
+ def __init__(
+ self,
+ *,
+ function: "StellarSorobanAuthorizedFunction",
+ sub_invocations: Optional[Sequence["StellarSorobanAuthorizedInvocation"]] = None,
+ ) -> None:
+ self.sub_invocations: Sequence["StellarSorobanAuthorizedInvocation"] = sub_invocations if sub_invocations is not None else []
+ self.function = function
+
+
+class StellarHostFunction(protobuf.MessageType):
+ MESSAGE_WIRE_TYPE = None
+ FIELDS = {
+ 1: protobuf.Field("type", "StellarHostFunctionType", repeated=False, required=True),
+ 2: protobuf.Field("invoke_contract", "StellarInvokeContractArgs", repeated=False, required=False, default=None),
+ }
+
+ def __init__(
+ self,
+ *,
+ type: "StellarHostFunctionType",
+ invoke_contract: Optional["StellarInvokeContractArgs"] = None,
+ ) -> None:
+ self.type = type
+ self.invoke_contract = invoke_contract
+
+
+class StellarSorobanAddressCredentials(protobuf.MessageType):
+ MESSAGE_WIRE_TYPE = None
+ FIELDS = {
+ 1: protobuf.Field("address", "string", repeated=False, required=True),
+ 2: protobuf.Field("nonce", "sint64", repeated=False, required=True),
+ 3: protobuf.Field("signature_expiration_ledger", "uint32", repeated=False, required=True),
+ 4: protobuf.Field("signature", "StellarSCVal", repeated=False, required=True),
+ }
+
+ def __init__(
+ self,
+ *,
+ address: "str",
+ nonce: "int",
+ signature_expiration_ledger: "int",
+ signature: "StellarSCVal",
+ ) -> None:
+ self.address = address
+ self.nonce = nonce
+ self.signature_expiration_ledger = signature_expiration_ledger
+ self.signature = signature
+
+
+class StellarSorobanCredentials(protobuf.MessageType):
+ MESSAGE_WIRE_TYPE = None
+ FIELDS = {
+ 1: protobuf.Field("type", "StellarSorobanCredentialsType", repeated=False, required=True),
+ 2: protobuf.Field("address", "StellarSorobanAddressCredentials", repeated=False, required=False, default=None),
+ }
+
+ def __init__(
+ self,
+ *,
+ type: "StellarSorobanCredentialsType",
+ address: Optional["StellarSorobanAddressCredentials"] = None,
+ ) -> None:
+ self.type = type
+ self.address = address
+
+
+class StellarSorobanAuthorizationEntry(protobuf.MessageType):
+ MESSAGE_WIRE_TYPE = None
+ FIELDS = {
+ 1: protobuf.Field("credentials", "StellarSorobanCredentials", repeated=False, required=True),
+ 2: protobuf.Field("root_invocation", "StellarSorobanAuthorizedInvocation", repeated=False, required=True),
+ }
+
+ def __init__(
+ self,
+ *,
+ credentials: "StellarSorobanCredentials",
+ root_invocation: "StellarSorobanAuthorizedInvocation",
+ ) -> None:
+ self.credentials = credentials
+ self.root_invocation = root_invocation
+
+
+class StellarInvokeHostFunctionOp(protobuf.MessageType):
+ MESSAGE_WIRE_TYPE = 235
+ FIELDS = {
+ 1: protobuf.Field("source_account", "string", repeated=False, required=False, default=None),
+ 2: protobuf.Field("function", "StellarHostFunction", repeated=False, required=True),
+ 3: protobuf.Field("auth", "StellarSorobanAuthorizationEntry", repeated=True, required=False, default=None),
+ }
+
+ def __init__(
+ self,
+ *,
+ function: "StellarHostFunction",
+ auth: Optional[Sequence["StellarSorobanAuthorizationEntry"]] = None,
+ source_account: Optional["str"] = None,
+ ) -> None:
+ self.auth: Sequence["StellarSorobanAuthorizationEntry"] = auth if auth is not None else []
+ self.function = function
+ self.source_account = source_account
+
+
+class StellarTxExtRequest(protobuf.MessageType):
+ MESSAGE_WIRE_TYPE = 238
+
+
+class StellarTxExt(protobuf.MessageType):
+ MESSAGE_WIRE_TYPE = 239
+ FIELDS = {
+ 1: protobuf.Field("v", "sint32", repeated=False, required=True),
+ 2: protobuf.Field("soroban_data", "bytes", repeated=False, required=False, default=None),
+ }
+
+ def __init__(
+ self,
+ *,
+ v: "int",
+ soroban_data: Optional["bytes"] = None,
+ ) -> None:
+ self.v = v
+ self.soroban_data = soroban_data
+
+
+class StellarUInt128Parts(protobuf.MessageType):
+ MESSAGE_WIRE_TYPE = None
+ FIELDS = {
+ 1: protobuf.Field("hi", "uint64", repeated=False, required=True),
+ 2: protobuf.Field("lo", "uint64", repeated=False, required=True),
+ }
+
+ def __init__(
+ self,
+ *,
+ hi: "int",
+ lo: "int",
+ ) -> None:
+ self.hi = hi
+ self.lo = lo
+
+
+class StellarInt128Parts(protobuf.MessageType):
+ MESSAGE_WIRE_TYPE = None
+ FIELDS = {
+ 1: protobuf.Field("hi", "sint64", repeated=False, required=True),
+ 2: protobuf.Field("lo", "uint64", repeated=False, required=True),
+ }
+
+ def __init__(
+ self,
+ *,
+ hi: "int",
+ lo: "int",
+ ) -> None:
+ self.hi = hi
+ self.lo = lo
+
+
+class StellarUInt256Parts(protobuf.MessageType):
+ MESSAGE_WIRE_TYPE = None
+ FIELDS = {
+ 1: protobuf.Field("hi_hi", "uint64", repeated=False, required=True),
+ 2: protobuf.Field("hi_lo", "uint64", repeated=False, required=True),
+ 3: protobuf.Field("lo_hi", "uint64", repeated=False, required=True),
+ 4: protobuf.Field("lo_lo", "uint64", repeated=False, required=True),
+ }
+
+ def __init__(
+ self,
+ *,
+ hi_hi: "int",
+ hi_lo: "int",
+ lo_hi: "int",
+ lo_lo: "int",
+ ) -> None:
+ self.hi_hi = hi_hi
+ self.hi_lo = hi_lo
+ self.lo_hi = lo_hi
+ self.lo_lo = lo_lo
+
+
+class StellarInt256Parts(protobuf.MessageType):
+ MESSAGE_WIRE_TYPE = None
+ FIELDS = {
+ 1: protobuf.Field("hi_hi", "sint64", repeated=False, required=True),
+ 2: protobuf.Field("hi_lo", "uint64", repeated=False, required=True),
+ 3: protobuf.Field("lo_hi", "uint64", repeated=False, required=True),
+ 4: protobuf.Field("lo_lo", "uint64", repeated=False, required=True),
+ }
+
+ def __init__(
+ self,
+ *,
+ hi_hi: "int",
+ hi_lo: "int",
+ lo_hi: "int",
+ lo_lo: "int",
+ ) -> None:
+ self.hi_hi = hi_hi
+ self.hi_lo = hi_lo
+ self.lo_hi = lo_hi
+ self.lo_lo = lo_lo
+
+
+class StellarSCValMapEntry(protobuf.MessageType):
+ MESSAGE_WIRE_TYPE = None
+ FIELDS = {
+ 1: protobuf.Field("key", "StellarSCVal", repeated=False, required=True),
+ 2: protobuf.Field("value", "StellarSCVal", repeated=False, required=True),
+ }
+
+ def __init__(
+ self,
+ *,
+ key: "StellarSCVal",
+ value: "StellarSCVal",
+ ) -> None:
+ self.key = key
+ self.value = value
+
+
class TelemetryGet(protobuf.MessageType):
MESSAGE_WIRE_TYPE = 1100
diff --git a/python/src/trezorlib/stellar.py b/python/src/trezorlib/stellar.py
index 6f5bf1ae..64fb0119 100644
--- a/python/src/trezorlib/stellar.py
+++ b/python/src/trezorlib/stellar.py
@@ -39,11 +39,13 @@ if TYPE_CHECKING:
messages.StellarPaymentOp,
messages.StellarSetOptionsOp,
messages.StellarClaimClaimableBalanceOp,
+ messages.StellarInvokeHostFunctionOp,
]
try:
+ from stellar_sdk import AccountMerge
+ from stellar_sdk import Address as StellarAddress
from stellar_sdk import (
- AccountMerge,
AllowTrust,
Asset,
BumpSequence,
@@ -53,6 +55,7 @@ try:
CreatePassiveSellOffer,
HashMemo,
IdMemo,
+ InvokeHostFunction,
LiquidityPoolAsset,
ManageBuyOffer,
ManageData,
@@ -70,6 +73,7 @@ try:
TextMemo,
TransactionEnvelope,
TrustLineEntryFlag,
+ xdr,
)
HAVE_STELLAR_SDK = True
@@ -84,10 +88,13 @@ DEFAULT_BIP32_PATH = "m/44h/148h/0h"
def from_envelope(
envelope: "TransactionEnvelope",
-) -> Tuple[messages.StellarSignTx, List["StellarMessageType"]]:
- """Parses transaction envelope into a map with the following keys:
+) -> Tuple[messages.StellarSignTx, List["StellarMessageType"], messages.StellarTxExt]:
+ """Parse a transaction envelope into a tuple of:
+
tx - a StellarSignTx describing the transaction header
- operations - an array of protobuf message objects for each operation
+ operations - a list of protobuf messages, one per operation
+ tx_ext - a StellarTxExt describing the transaction extension: v=1 carrying
+ the Soroban data for Soroban transactions, otherwise v=0
"""
if not HAVE_STELLAR_SDK:
raise RuntimeError("Stellar SDK not available")
@@ -134,7 +141,16 @@ def from_envelope(
)
operations = [_read_operation(op) for op in parsed_tx.operations]
- return tx, operations
+
+ if parsed_tx.soroban_data:
+ tx_ext = messages.StellarTxExt(
+ v=1,
+ soroban_data=parsed_tx.soroban_data.to_xdr_bytes(),
+ )
+ else:
+ tx_ext = messages.StellarTxExt(v=0)
+
+ return tx, operations, tx_ext
def _read_operation(op: "Operation") -> "StellarMessageType":
@@ -278,6 +294,12 @@ def _read_operation(op: "Operation") -> "StellarMessageType":
source_account=source_account,
balance_id=bytes.fromhex(op.balance_id),
)
+ if isinstance(op, InvokeHostFunction):
+ return messages.StellarInvokeHostFunctionOp(
+ source_account=source_account,
+ function=_read_host_function(op.host_function),
+ auth=[_read_authorization_entry(entry) for entry in op.auth],
+ )
raise ValueError(f"Unknown operation type: {op.__class__.__name__}")
@@ -346,6 +368,7 @@ def sign_tx(
session: "Session",
tx: messages.StellarSignTx,
operations: List["StellarMessageType"],
+ tx_ext: messages.StellarTxExt,
address_n: "Address",
network_passphrase: str = DEFAULT_NETWORK_PASSPHRASE,
) -> messages.StellarSignedTx:
@@ -354,11 +377,12 @@ def sign_tx(
tx.num_operations = len(operations)
# Signing loop works as follows:
#
- # 1. Start with tx (header information for the transaction) and operations (an array of operation protobuf messagess)
+ # 1. Start with tx (header information for the transaction) and operations (an array of operation protobuf messages)
# 2. Send the tx header to the device
# 3. Receive a StellarTxOpRequest message
# 4. Send operations one by one until all operations have been sent. If there are more operations to sign, the device will send a StellarTxOpRequest message
- # 5. The final message received will be StellarSignedTx which is returned from this method
+ # 5. If the transaction contains Soroban operations, the device will send a StellarTxExtRequest message. Send tx_ext to the device.
+ # 6. The final message received will be StellarSignedTx which is returned from this method
resp = session.call(tx)
try:
while isinstance(resp, messages.StellarTxOpRequest):
@@ -369,6 +393,10 @@ def sign_tx(
"Reached end of operations without a signature."
) from None
+ # Handle StellarTxExtRequest for Soroban transactions
+ if isinstance(resp, messages.StellarTxExtRequest):
+ resp = session.call(tx_ext)
+
resp = messages.StellarSignedTx.ensure_isinstance(resp)
if operations:
@@ -377,3 +405,216 @@ def sign_tx(
)
return resp
+
+
+def _read_sc_address(address: "xdr.SCAddress") -> str:
+ """Read an SCAddress from XDR."""
+ addr = StellarAddress.from_xdr_sc_address(address)
+ return addr.address
+
+
+def _read_sc_val(val: "xdr.SCVal") -> messages.StellarSCVal:
+ """Read an SCVal from XDR."""
+ if val.type == xdr.SCValType.SCV_BOOL:
+ return messages.StellarSCVal(type=messages.StellarSCValType.SCV_BOOL, b=val.b)
+ elif val.type == xdr.SCValType.SCV_VOID:
+ return messages.StellarSCVal(type=messages.StellarSCValType.SCV_VOID)
+ elif val.type == xdr.SCValType.SCV_U32:
+ return messages.StellarSCVal(
+ type=messages.StellarSCValType.SCV_U32, u32=val.u32.uint32
+ )
+ elif val.type == xdr.SCValType.SCV_I32:
+ return messages.StellarSCVal(
+ type=messages.StellarSCValType.SCV_I32, i32=val.i32.int32
+ )
+ elif val.type == xdr.SCValType.SCV_U64:
+ return messages.StellarSCVal(
+ type=messages.StellarSCValType.SCV_U64, u64=val.u64.uint64
+ )
+ elif val.type == xdr.SCValType.SCV_I64:
+ return messages.StellarSCVal(
+ type=messages.StellarSCValType.SCV_I64, i64=val.i64.int64
+ )
+ elif val.type == xdr.SCValType.SCV_TIMEPOINT:
+ return messages.StellarSCVal(
+ type=messages.StellarSCValType.SCV_TIMEPOINT,
+ timepoint=val.timepoint.time_point.uint64,
+ )
+ elif val.type == xdr.SCValType.SCV_DURATION:
+ return messages.StellarSCVal(
+ type=messages.StellarSCValType.SCV_DURATION,
+ duration=val.duration.duration.uint64,
+ )
+ elif val.type == xdr.SCValType.SCV_U128:
+ return messages.StellarSCVal(
+ type=messages.StellarSCValType.SCV_U128,
+ u128=messages.StellarUInt128Parts(
+ hi=val.u128.hi.uint64, lo=val.u128.lo.uint64
+ ),
+ )
+ elif val.type == xdr.SCValType.SCV_I128:
+ return messages.StellarSCVal(
+ type=messages.StellarSCValType.SCV_I128,
+ i128=messages.StellarInt128Parts(
+ hi=val.i128.hi.int64, lo=val.i128.lo.uint64
+ ),
+ )
+ elif val.type == xdr.SCValType.SCV_U256:
+ return messages.StellarSCVal(
+ type=messages.StellarSCValType.SCV_U256,
+ u256=messages.StellarUInt256Parts(
+ hi_hi=val.u256.hi_hi.uint64,
+ hi_lo=val.u256.hi_lo.uint64,
+ lo_hi=val.u256.lo_hi.uint64,
+ lo_lo=val.u256.lo_lo.uint64,
+ ),
+ )
+ elif val.type == xdr.SCValType.SCV_I256:
+ return messages.StellarSCVal(
+ type=messages.StellarSCValType.SCV_I256,
+ i256=messages.StellarInt256Parts(
+ hi_hi=val.i256.hi_hi.int64,
+ hi_lo=val.i256.hi_lo.uint64,
+ lo_hi=val.i256.lo_hi.uint64,
+ lo_lo=val.i256.lo_lo.uint64,
+ ),
+ )
+ elif val.type == xdr.SCValType.SCV_BYTES:
+ return messages.StellarSCVal(
+ type=messages.StellarSCValType.SCV_BYTES, bytes=val.bytes.sc_bytes
+ )
+ elif val.type == xdr.SCValType.SCV_STRING:
+ return messages.StellarSCVal(
+ type=messages.StellarSCValType.SCV_STRING,
+ string=val.str.sc_string, # raw bytes, not necessarily UTF-8
+ )
+ elif val.type == xdr.SCValType.SCV_SYMBOL:
+ return messages.StellarSCVal(
+ type=messages.StellarSCValType.SCV_SYMBOL,
+ symbol=val.sym.sc_symbol.decode("utf-8"),
+ )
+ elif val.type == xdr.SCValType.SCV_VEC:
+ # SCV_VEC's vector is an XDR pointer (SCVec*), i.e. technically nullable,
+ # but a null vector is not a valid Soroban value. Reject it so we never
+ # coerce Vec(None) into Vec([]) and end up signing different bytes than
+ # the input XDR (the firmware always encodes the vector as present).
+ if val.vec is None:
+ raise ValueError("SCV_VEC with a null vector is not supported")
+ return messages.StellarSCVal(
+ type=messages.StellarSCValType.SCV_VEC,
+ vec=[_read_sc_val(v) for v in val.vec.sc_vec],
+ )
+ elif val.type == xdr.SCValType.SCV_MAP:
+ # SCV_MAP's map is an XDR pointer (SCMap*); same reasoning as SCV_VEC.
+ if val.map is None:
+ raise ValueError("SCV_MAP with a null map is not supported")
+ return messages.StellarSCVal(
+ type=messages.StellarSCValType.SCV_MAP,
+ map=[
+ messages.StellarSCValMapEntry(
+ key=_read_sc_val(item.key), value=_read_sc_val(item.val)
+ )
+ for item in val.map.sc_map
+ ],
+ )
+ elif val.type == xdr.SCValType.SCV_ADDRESS:
+ return messages.StellarSCVal(
+ type=messages.StellarSCValType.SCV_ADDRESS,
+ address=_read_sc_address(val.address),
+ )
+ else:
+ raise ValueError(f"Unsupported SCVal type: {val.type}")
+
+
+def _read_invoke_contract_args(
+ data: "xdr.InvokeContractArgs",
+) -> messages.StellarInvokeContractArgs:
+ """Read InvokeContractArgs from XDR."""
+ return messages.StellarInvokeContractArgs(
+ contract_address=_read_sc_address(data.contract_address),
+ function_name=data.function_name.sc_symbol.decode("utf-8"),
+ args=[_read_sc_val(arg) for arg in data.args],
+ )
+
+
+def _read_authorized_function(
+ function: "xdr.SorobanAuthorizedFunction",
+) -> messages.StellarSorobanAuthorizedFunction:
+ """Read SorobanAuthorizedFunction from XDR."""
+ if (
+ function.type
+ == xdr.SorobanAuthorizedFunctionType.SOROBAN_AUTHORIZED_FUNCTION_TYPE_CONTRACT_FN
+ ):
+ return messages.StellarSorobanAuthorizedFunction(
+ type=messages.StellarSorobanAuthorizedFunctionType.SOROBAN_AUTHORIZED_FUNCTION_TYPE_CONTRACT_FN,
+ contract_fn=_read_invoke_contract_args(function.contract_fn),
+ )
+ else:
+ raise ValueError(f"Unsupported SorobanAuthorizedFunction type: {function.type}")
+
+
+def _read_address_credentials(
+ address_credentials: "xdr.SorobanAddressCredentials",
+) -> messages.StellarSorobanAddressCredentials:
+ """Read SorobanAddressCredentials from XDR."""
+ return messages.StellarSorobanAddressCredentials(
+ address=_read_sc_address(address_credentials.address),
+ nonce=address_credentials.nonce.int64,
+ signature_expiration_ledger=address_credentials.signature_expiration_ledger.uint32,
+ signature=_read_sc_val(address_credentials.signature),
+ )
+
+
+def _read_credentials(
+ credentials: "xdr.SorobanCredentials",
+) -> messages.StellarSorobanCredentials:
+ """Read SorobanCredentials from XDR."""
+ if (
+ credentials.type
+ == xdr.SorobanCredentialsType.SOROBAN_CREDENTIALS_SOURCE_ACCOUNT
+ ):
+ return messages.StellarSorobanCredentials(
+ type=messages.StellarSorobanCredentialsType.SOROBAN_CREDENTIALS_SOURCE_ACCOUNT
+ )
+ elif credentials.type == xdr.SorobanCredentialsType.SOROBAN_CREDENTIALS_ADDRESS:
+ return messages.StellarSorobanCredentials(
+ type=messages.StellarSorobanCredentialsType.SOROBAN_CREDENTIALS_ADDRESS,
+ address=_read_address_credentials(credentials.address),
+ )
+ else:
+ raise ValueError(f"Unsupported SorobanCredentials type: {credentials.type}")
+
+
+def _read_authorized_invocation(
+ invocation: "xdr.SorobanAuthorizedInvocation",
+) -> messages.StellarSorobanAuthorizedInvocation:
+ """Read SorobanAuthorizedInvocation from XDR."""
+ return messages.StellarSorobanAuthorizedInvocation(
+ function=_read_authorized_function(invocation.function),
+ sub_invocations=[
+ _read_authorized_invocation(sub) for sub in invocation.sub_invocations
+ ],
+ )
+
+
+def _read_authorization_entry(
+ entry: "xdr.SorobanAuthorizationEntry",
+) -> messages.StellarSorobanAuthorizationEntry:
+ """Read SorobanAuthorizationEntry from XDR."""
+ return messages.StellarSorobanAuthorizationEntry(
+ credentials=_read_credentials(entry.credentials),
+ root_invocation=_read_authorized_invocation(entry.root_invocation),
+ )
+
+
+def _read_host_function(
+ host_function: "xdr.HostFunction",
+) -> messages.StellarHostFunction:
+ """Read HostFunction from XDR."""
+ if host_function.type != xdr.HostFunctionType.HOST_FUNCTION_TYPE_INVOKE_CONTRACT:
+ raise ValueError(f"Unsupported host function type: {host_function.type}")
+
+ return messages.StellarHostFunction(
+ type=messages.StellarHostFunctionType.HOST_FUNCTION_TYPE_INVOKE_CONTRACT,
+ invoke_contract=_read_invoke_contract_args(host_function.invoke_contract),
+ )
diff --git a/python/tests/test_stellar.py b/python/tests/test_stellar.py
index 6aebfaf8..d1eb03fa 100644
--- a/python/tests/test_stellar.py
+++ b/python/tests/test_stellar.py
@@ -61,7 +61,8 @@ def make_default_tx(default_op: bool = False, **kwargs) -> TransactionBuilder:
def test_simple():
envelope = make_default_tx(default_op=True).build()
- tx, operations = stellar.from_envelope(envelope)
+ tx, operations, ext = stellar.from_envelope(envelope)
+ assert ext == messages.StellarTxExt(v=0)
assert tx.source_account == TX_SOURCE
assert tx.fee == envelope.transaction.fee
assert tx.sequence_number == SEQUENCE + 1
@@ -80,7 +81,8 @@ def test_memo_text():
make_default_tx(default_op=True).add_text_memo(memo_text.encode()).build()
)
- tx, operations = stellar.from_envelope(envelope)
+ tx, operations, ext = stellar.from_envelope(envelope)
+ assert ext == messages.StellarTxExt(v=0)
assert tx.memo_type == messages.StellarMemoType.TEXT
assert tx.memo_text == memo_text
assert tx.memo_id is None
@@ -91,7 +93,8 @@ def test_memo_id():
memo_id = 123456789
envelope = make_default_tx(default_op=True).add_id_memo(memo_id).build()
- tx, operations = stellar.from_envelope(envelope)
+ tx, operations, ext = stellar.from_envelope(envelope)
+ assert ext == messages.StellarTxExt(v=0)
assert tx.memo_type == messages.StellarMemoType.ID
assert tx.memo_text is None
assert tx.memo_id == memo_id
@@ -104,7 +107,8 @@ def test_memo_hash():
make_default_tx(v1=False, default_op=True).add_hash_memo(memo_hash).build()
)
- tx, operations = stellar.from_envelope(envelope)
+ tx, operations, ext = stellar.from_envelope(envelope)
+ assert ext == messages.StellarTxExt(v=0)
assert tx.memo_type == messages.StellarMemoType.HASH
assert tx.memo_text is None
assert tx.memo_id is None
@@ -119,7 +123,8 @@ def test_memo_return_hash():
.build()
)
- tx, operations = stellar.from_envelope(envelope)
+ tx, operations, ext = stellar.from_envelope(envelope)
+ assert ext == messages.StellarTxExt(v=0)
assert tx.memo_type == messages.StellarMemoType.RETURN
assert tx.memo_text is None
assert tx.memo_id is None
@@ -162,7 +167,8 @@ def test_multiple_operations():
.build()
)
- tx, operations = stellar.from_envelope(envelope)
+ tx, operations, ext = stellar.from_envelope(envelope)
+ assert ext == messages.StellarTxExt(v=0)
assert tx.source_account == TX_SOURCE
assert tx.fee == envelope.transaction.fee
assert tx.sequence_number == SEQUENCE + 1
@@ -200,7 +206,8 @@ def test_create_account():
source=operation_source,
).build()
- tx, operations = stellar.from_envelope(envelope)
+ tx, operations, ext = stellar.from_envelope(envelope)
+ assert ext == messages.StellarTxExt(v=0)
assert len(operations) == 1
assert isinstance(operations[0], messages.StellarCreateAccountOp)
assert operations[0].source_account == operation_source
@@ -223,7 +230,8 @@ def test_payment_native_asset():
source=operation_source,
).build()
- tx, operations = stellar.from_envelope(envelope)
+ tx, operations, ext = stellar.from_envelope(envelope)
+ assert ext == messages.StellarTxExt(v=0)
assert len(operations) == 1
assert isinstance(operations[0], messages.StellarPaymentOp)
assert operations[0].source_account == operation_source
@@ -249,7 +257,8 @@ def test_payment_alpha4_asset():
source=operation_source,
).build()
- tx, operations = stellar.from_envelope(envelope)
+ tx, operations, ext = stellar.from_envelope(envelope)
+ assert ext == messages.StellarTxExt(v=0)
assert len(operations) == 1
assert isinstance(operations[0], messages.StellarPaymentOp)
assert operations[0].source_account == operation_source
@@ -275,7 +284,8 @@ def test_payment_alpha12_asset():
source=operation_source,
).build()
- tx, operations = stellar.from_envelope(envelope)
+ tx, operations, ext = stellar.from_envelope(envelope)
+ assert ext == messages.StellarTxExt(v=0)
assert len(operations) == 1
assert isinstance(operations[0], messages.StellarPaymentOp)
assert operations[0].source_account == operation_source
@@ -313,7 +323,8 @@ def test_path_payment_strict_receive():
source=operation_source,
).build()
- tx, operations = stellar.from_envelope(envelope)
+ tx, operations, ext = stellar.from_envelope(envelope)
+ assert ext == messages.StellarTxExt(v=0)
assert len(operations) == 1
assert isinstance(operations[0], messages.StellarPathPaymentStrictReceiveOp)
@@ -352,7 +363,8 @@ def test_manage_sell_offer_new_offer():
source=operation_source,
).build()
- tx, operations = stellar.from_envelope(envelope)
+ tx, operations, ext = stellar.from_envelope(envelope)
+ assert ext == messages.StellarTxExt(v=0)
assert len(operations) == 1
assert isinstance(operations[0], messages.StellarManageSellOfferOp)
assert operations[0].source_account == operation_source
@@ -386,7 +398,8 @@ def test_manage_sell_offer_update_offer():
source=operation_source,
).build()
- tx, operations = stellar.from_envelope(envelope)
+ tx, operations, ext = stellar.from_envelope(envelope)
+ assert ext == messages.StellarTxExt(v=0)
assert len(operations) == 1
assert isinstance(operations[0], messages.StellarManageSellOfferOp)
assert operations[0].source_account == operation_source
@@ -418,7 +431,8 @@ def test_create_passive_sell_offer():
source=operation_source,
).build()
- tx, operations = stellar.from_envelope(envelope)
+ tx, operations, ext = stellar.from_envelope(envelope)
+ assert ext == messages.StellarTxExt(v=0)
assert len(operations) == 1
assert isinstance(operations[0], messages.StellarCreatePassiveSellOfferOp)
assert operations[0].source_account == operation_source
@@ -458,7 +472,8 @@ def test_set_options():
source=operation_source,
).build()
- tx, operations = stellar.from_envelope(envelope)
+ tx, operations, ext = stellar.from_envelope(envelope)
+ assert ext == messages.StellarTxExt(v=0)
assert len(operations) == 1
assert isinstance(operations[0], messages.StellarSetOptionsOp)
assert operations[0].source_account == operation_source
@@ -485,7 +500,8 @@ def test_set_options_ed25519_signer():
account_id=signer, weight=weight, source=operation_source
).build()
- tx, operations = stellar.from_envelope(envelope)
+ tx, operations, ext = stellar.from_envelope(envelope)
+ assert ext == messages.StellarTxExt(v=0)
assert len(operations) == 1
assert isinstance(operations[0], messages.StellarSetOptionsOp)
assert operations[0].source_account == operation_source
@@ -514,7 +530,8 @@ def test_set_options_pre_auth_tx_signer():
pre_auth_tx_hash=signer, weight=weight, source=operation_source
).build()
- tx, operations = stellar.from_envelope(envelope)
+ tx, operations, ext = stellar.from_envelope(envelope)
+ assert ext == messages.StellarTxExt(v=0)
assert len(operations) == 1
assert isinstance(operations[0], messages.StellarSetOptionsOp)
assert operations[0].signer_type == messages.StellarSignerType.PRE_AUTH
@@ -534,7 +551,8 @@ def test_set_options_hashx_signer():
sha256_hash=signer, weight=weight, source=operation_source
).build()
- tx, operations = stellar.from_envelope(envelope)
+ tx, operations, ext = stellar.from_envelope(envelope)
+ assert ext == messages.StellarTxExt(v=0)
assert len(operations) == 1
assert isinstance(operations[0], messages.StellarSetOptionsOp)
assert operations[0].signer_type == messages.StellarSignerType.HASH
@@ -555,7 +573,8 @@ def test_change_trust():
source=operation_source,
).build()
- tx, operations = stellar.from_envelope(envelope)
+ tx, operations, ext = stellar.from_envelope(envelope)
+ assert ext == messages.StellarTxExt(v=0)
assert len(operations) == 1
assert isinstance(operations[0], messages.StellarChangeTrustOp)
assert operations[0].source_account == operation_source
@@ -583,7 +602,8 @@ def test_allow_trust():
source=operation_source,
).build()
- tx, operations = stellar.from_envelope(envelope)
+ tx, operations, ext = stellar.from_envelope(envelope)
+ assert ext == messages.StellarTxExt(v=0)
assert len(operations) == 1
assert isinstance(operations[0], messages.StellarAllowTrustOp)
assert operations[0].source_account == operation_source
@@ -602,7 +622,8 @@ def test_account_merge():
destination=destination, source=operation_source
).build()
- tx, operations = stellar.from_envelope(envelope)
+ tx, operations, ext = stellar.from_envelope(envelope)
+ assert ext == messages.StellarTxExt(v=0)
assert len(operations) == 1
assert isinstance(operations[0], messages.StellarAccountMergeOp)
assert operations[0].source_account == operation_source
@@ -619,7 +640,8 @@ def test_manage_data():
data_name=data_name, data_value=data_value, source=operation_source
).build()
- tx, operations = stellar.from_envelope(envelope)
+ tx, operations, ext = stellar.from_envelope(envelope)
+ assert ext == messages.StellarTxExt(v=0)
assert len(operations) == 1
assert isinstance(operations[0], messages.StellarManageDataOp)
assert operations[0].source_account == operation_source
@@ -637,7 +659,8 @@ def test_manage_data_remove_data_entity():
data_name=data_name, data_value=data_value, source=operation_source
).build()
- tx, operations = stellar.from_envelope(envelope)
+ tx, operations, ext = stellar.from_envelope(envelope)
+ assert ext == messages.StellarTxExt(v=0)
assert len(operations) == 1
assert isinstance(operations[0], messages.StellarManageDataOp)
assert operations[0].source_account == operation_source
@@ -654,7 +677,8 @@ def test_bump_sequence():
bump_to=bump_to, source=operation_source
).build()
- tx, operations = stellar.from_envelope(envelope)
+ tx, operations, ext = stellar.from_envelope(envelope)
+ assert ext == messages.StellarTxExt(v=0)
assert len(operations) == 1
assert isinstance(operations[0], messages.StellarBumpSequenceOp)
assert operations[0].source_account == operation_source
@@ -679,7 +703,8 @@ def test_manage_buy_offer_new_offer():
source=operation_source,
).build()
- tx, operations = stellar.from_envelope(envelope)
+ tx, operations, ext = stellar.from_envelope(envelope)
+ assert ext == messages.StellarTxExt(v=0)
assert len(operations) == 1
assert isinstance(operations[0], messages.StellarManageBuyOfferOp)
assert operations[0].source_account == operation_source
@@ -713,7 +738,8 @@ def test_manage_buy_offer_update_offer():
source=operation_source,
).build()
- tx, operations = stellar.from_envelope(envelope)
+ tx, operations, ext = stellar.from_envelope(envelope)
+ assert ext == messages.StellarTxExt(v=0)
assert len(operations) == 1
assert isinstance(operations[0], messages.StellarManageBuyOfferOp)
assert operations[0].source_account == operation_source
@@ -754,7 +780,8 @@ def test_path_payment_strict_send():
source=operation_source,
).build()
- tx, operations = stellar.from_envelope(envelope)
+ tx, operations, ext = stellar.from_envelope(envelope)
+ assert ext == messages.StellarTxExt(v=0)
assert len(operations) == 1
assert isinstance(operations[0], messages.StellarPathPaymentStrictSendOp)
@@ -937,7 +964,8 @@ def test_claim_claimable_balance():
balance_id=balance_id, source=operation_source
).build()
- tx, operations = stellar.from_envelope(envelope)
+ tx, operations, ext = stellar.from_envelope(envelope)
+ assert ext == messages.StellarTxExt(v=0)
assert len(operations) == 1
assert isinstance(operations[0], messages.StellarClaimClaimableBalanceOp)
assert operations[0].source_account == operation_source
diff --git a/rust/trezor-client/src/messages/generated.rs b/rust/trezor-client/src/messages/generated.rs
index 9214ea9c..170237ab 100644
--- a/rust/trezor-client/src/messages/generated.rs
+++ b/rust/trezor-client/src/messages/generated.rs
@@ -313,6 +313,9 @@ trezor_message_impl! {
StellarPathPaymentStrictSendOp => MessageType_StellarPathPaymentStrictSendOp,
StellarClaimClaimableBalanceOp => MessageType_StellarClaimClaimableBalanceOp,
StellarSignedTx => MessageType_StellarSignedTx,
+ StellarInvokeHostFunctionOp => MessageType_StellarInvokeHostFunctionOp,
+ StellarTxExtRequest => MessageType_StellarTxExtRequest,
+ StellarTxExt => MessageType_StellarTxExt,
}
#[cfg(feature = "tezos")]
diff --git a/rust/trezor-client/src/protos/generated/messages.rs b/rust/trezor-client/src/protos/generated/messages.rs
index 589444b2..fc5f04ce 100644
--- a/rust/trezor-client/src/protos/generated/messages.rs
+++ b/rust/trezor-client/src/protos/generated/messages.rs
@@ -361,6 +361,12 @@ pub enum MessageType {
MessageType_StellarClaimClaimableBalanceOp = 225,
// @@protoc_insertion_point(enum_value:hw.trezor.messages.MessageType.MessageType_StellarSignedTx)
MessageType_StellarSignedTx = 230,
+ // @@protoc_insertion_point(enum_value:hw.trezor.messages.MessageType.MessageType_StellarInvokeHostFunctionOp)
+ MessageType_StellarInvokeHostFunctionOp = 235,
+ // @@protoc_insertion_point(enum_value:hw.trezor.messages.MessageType.MessageType_StellarTxExtRequest)
+ MessageType_StellarTxExtRequest = 238,
+ // @@protoc_insertion_point(enum_value:hw.trezor.messages.MessageType.MessageType_StellarTxExt)
+ MessageType_StellarTxExt = 239,
// @@protoc_insertion_point(enum_value:hw.trezor.messages.MessageType.MessageType_CardanoGetPublicKey)
MessageType_CardanoGetPublicKey = 305,
// @@protoc_insertion_point(enum_value:hw.trezor.messages.MessageType.MessageType_CardanoPublicKey)
@@ -791,6 +797,9 @@ impl ::protobuf::Enum for MessageType {
223 => ::std::option::Option::Some(MessageType::MessageType_StellarPathPaymentStrictSendOp),
225 => ::std::option::Option::Some(MessageType::MessageType_StellarClaimClaimableBalanceOp),
230 => ::std::option::Option::Some(MessageType::MessageType_StellarSignedTx),
+ 235 => ::std::option::Option::Some(MessageType::MessageType_StellarInvokeHostFunctionOp),
+ 238 => ::std::option::Option::Some(MessageType::MessageType_StellarTxExtRequest),
+ 239 => ::std::option::Option::Some(MessageType::MessageType_StellarTxExt),
305 => ::std::option::Option::Some(MessageType::MessageType_CardanoGetPublicKey),
306 => ::std::option::Option::Some(MessageType::MessageType_CardanoPublicKey),
307 => ::std::option::Option::Some(MessageType::MessageType_CardanoGetAddress),
@@ -1090,6 +1099,9 @@ impl ::protobuf::Enum for MessageType {
"MessageType_StellarPathPaymentStrictSendOp" => ::std::option::Option::Some(MessageType::MessageType_StellarPathPaymentStrictSendOp),
"MessageType_StellarClaimClaimableBalanceOp" => ::std::option::Option::Some(MessageType::MessageType_StellarClaimClaimableBalanceOp),
"MessageType_StellarSignedTx" => ::std::option::Option::Some(MessageType::MessageType_StellarSignedTx),
+ "MessageType_StellarInvokeHostFunctionOp" => ::std::option::Option::Some(MessageType::MessageType_StellarInvokeHostFunctionOp),
+ "MessageType_StellarTxExtRequest" => ::std::option::Option::Some(MessageType::MessageType_StellarTxExtRequest),
+ "MessageType_StellarTxExt" => ::std::option::Option::Some(MessageType::MessageType_StellarTxExt),
"MessageType_CardanoGetPublicKey" => ::std::option::Option::Some(MessageType::MessageType_CardanoGetPublicKey),
"MessageType_CardanoPublicKey" => ::std::option::Option::Some(MessageType::MessageType_CardanoPublicKey),
"MessageType_CardanoGetAddress" => ::std::option::Option::Some(MessageType::MessageType_CardanoGetAddress),
@@ -1388,6 +1400,9 @@ impl ::protobuf::Enum for MessageType {
MessageType::MessageType_StellarPathPaymentStrictSendOp,
MessageType::MessageType_StellarClaimClaimableBalanceOp,
MessageType::MessageType_StellarSignedTx,
+ MessageType::MessageType_StellarInvokeHostFunctionOp,
+ MessageType::MessageType_StellarTxExtRequest,
+ MessageType::MessageType_StellarTxExt,
MessageType::MessageType_CardanoGetPublicKey,
MessageType::MessageType_CardanoPublicKey,
MessageType::MessageType_CardanoGetAddress,
@@ -1692,132 +1707,135 @@ impl ::protobuf::EnumFull for MessageType {
MessageType::MessageType_StellarPathPaymentStrictSendOp => 164,
MessageType::MessageType_StellarClaimClaimableBalanceOp => 165,
MessageType::MessageType_StellarSignedTx => 166,
- MessageType::MessageType_CardanoGetPublicKey => 167,
- MessageType::MessageType_CardanoPublicKey => 168,
- MessageType::MessageType_CardanoGetAddress => 169,
- MessageType::MessageType_CardanoAddress => 170,
- MessageType::MessageType_CardanoTxItemAck => 171,
- MessageType::MessageType_CardanoTxAuxiliaryDataSupplement => 172,
- MessageType::MessageType_CardanoTxWitnessRequest => 173,
- MessageType::MessageType_CardanoTxWitnessResponse => 174,
- MessageType::MessageType_CardanoTxHostAck => 175,
- MessageType::MessageType_CardanoTxBodyHash => 176,
- MessageType::MessageType_CardanoSignTxFinished => 177,
- MessageType::MessageType_CardanoSignTxInit => 178,
- MessageType::MessageType_CardanoTxInput => 179,
- MessageType::MessageType_CardanoTxOutput => 180,
- MessageType::MessageType_CardanoAssetGroup => 181,
- MessageType::MessageType_CardanoToken => 182,
- MessageType::MessageType_CardanoTxCertificate => 183,
- MessageType::MessageType_CardanoTxWithdrawal => 184,
- MessageType::MessageType_CardanoTxAuxiliaryData => 185,
- MessageType::MessageType_CardanoPoolOwner => 186,
- MessageType::MessageType_CardanoPoolRelayParameters => 187,
- MessageType::MessageType_CardanoGetNativeScriptHash => 188,
- MessageType::MessageType_CardanoNativeScriptHash => 189,
- MessageType::MessageType_CardanoTxMint => 190,
- MessageType::MessageType_CardanoTxCollateralInput => 191,
- MessageType::MessageType_CardanoTxRequiredSigner => 192,
- MessageType::MessageType_CardanoTxInlineDatumChunk => 193,
- MessageType::MessageType_CardanoTxReferenceScriptChunk => 194,
- MessageType::MessageType_CardanoTxReferenceInput => 195,
- MessageType::MessageType_CardanoSignMessageInit => 196,
- MessageType::MessageType_CardanoMessageDataRequest => 197,
- MessageType::MessageType_CardanoMessageDataResponse => 198,
- MessageType::MessageType_CardanoMessageSignature => 199,
- MessageType::MessageType_RippleGetAddress => 200,
- MessageType::MessageType_RippleAddress => 201,
- MessageType::MessageType_RippleSignTx => 202,
- MessageType::MessageType_RippleSignedTx => 203,
- MessageType::MessageType_MoneroTransactionInitRequest => 204,
- MessageType::MessageType_MoneroTransactionInitAck => 205,
- MessageType::MessageType_MoneroTransactionSetInputRequest => 206,
- MessageType::MessageType_MoneroTransactionSetInputAck => 207,
- MessageType::MessageType_MoneroTransactionInputViniRequest => 208,
- MessageType::MessageType_MoneroTransactionInputViniAck => 209,
- MessageType::MessageType_MoneroTransactionAllInputsSetRequest => 210,
- MessageType::MessageType_MoneroTransactionAllInputsSetAck => 211,
- MessageType::MessageType_MoneroTransactionSetOutputRequest => 212,
- MessageType::MessageType_MoneroTransactionSetOutputAck => 213,
- MessageType::MessageType_MoneroTransactionAllOutSetRequest => 214,
- MessageType::MessageType_MoneroTransactionAllOutSetAck => 215,
- MessageType::MessageType_MoneroTransactionSignInputRequest => 216,
- MessageType::MessageType_MoneroTransactionSignInputAck => 217,
- MessageType::MessageType_MoneroTransactionFinalRequest => 218,
- MessageType::MessageType_MoneroTransactionFinalAck => 219,
- MessageType::MessageType_MoneroKeyImageExportInitRequest => 220,
- MessageType::MessageType_MoneroKeyImageExportInitAck => 221,
- MessageType::MessageType_MoneroKeyImageSyncStepRequest => 222,
- MessageType::MessageType_MoneroKeyImageSyncStepAck => 223,
- MessageType::MessageType_MoneroKeyImageSyncFinalRequest => 224,
- MessageType::MessageType_MoneroKeyImageSyncFinalAck => 225,
- MessageType::MessageType_MoneroGetAddress => 226,
- MessageType::MessageType_MoneroAddress => 227,
- MessageType::MessageType_MoneroGetWatchKey => 228,
- MessageType::MessageType_MoneroWatchKey => 229,
- MessageType::MessageType_DebugMoneroDiagRequest => 230,
- MessageType::MessageType_DebugMoneroDiagAck => 231,
- MessageType::MessageType_MoneroGetTxKeyRequest => 232,
- MessageType::MessageType_MoneroGetTxKeyAck => 233,
- MessageType::MessageType_MoneroLiveRefreshStartRequest => 234,
- MessageType::MessageType_MoneroLiveRefreshStartAck => 235,
- MessageType::MessageType_MoneroLiveRefreshStepRequest => 236,
- MessageType::MessageType_MoneroLiveRefreshStepAck => 237,
- MessageType::MessageType_MoneroLiveRefreshFinalRequest => 238,
- MessageType::MessageType_MoneroLiveRefreshFinalAck => 239,
- MessageType::MessageType_EosGetPublicKey => 240,
- MessageType::MessageType_EosPublicKey => 241,
- MessageType::MessageType_EosSignTx => 242,
- MessageType::MessageType_EosTxActionRequest => 243,
- MessageType::MessageType_EosTxActionAck => 244,
- MessageType::MessageType_EosSignedTx => 245,
- MessageType::MessageType_WebAuthnListResidentCredentials => 246,
- MessageType::MessageType_WebAuthnCredentials => 247,
- MessageType::MessageType_WebAuthnAddResidentCredential => 248,
- MessageType::MessageType_WebAuthnRemoveResidentCredential => 249,
- MessageType::MessageType_WebAuthnCredentialsAck => 250,
- MessageType::MessageType_SolanaGetPublicKey => 251,
- MessageType::MessageType_SolanaPublicKey => 252,
- MessageType::MessageType_SolanaGetAddress => 253,
- MessageType::MessageType_SolanaAddress => 254,
- MessageType::MessageType_SolanaSignTx => 255,
- MessageType::MessageType_SolanaTxSignature => 256,
- MessageType::MessageType_SolanaSignMessage => 257,
- MessageType::MessageType_SolanaMessageSignature => 258,
- MessageType::MessageType_SolanaVerifyMessage => 259,
- MessageType::MessageType_ThpCreateNewSession => 260,
- MessageType::MessageType_ThpCredentialRequest => 261,
- MessageType::MessageType_ThpCredentialResponse => 262,
- MessageType::MessageType_NostrGetPubkey => 263,
- MessageType::MessageType_NostrPubkey => 264,
- MessageType::MessageType_NostrSignEvent => 265,
- MessageType::MessageType_NostrEventSignature => 266,
- MessageType::MessageType_EvoluGetNode => 267,
- MessageType::MessageType_EvoluNode => 268,
- MessageType::MessageType_EvoluSignRegistrationRequest => 269,
- MessageType::MessageType_EvoluRegistrationRequest => 270,
- MessageType::MessageType_EvoluGetDelegatedIdentityKey => 271,
- MessageType::MessageType_EvoluDelegatedIdentityKey => 272,
- MessageType::MessageType_EvoluIndexManagement => 273,
- MessageType::MessageType_EvoluIndexManagementResponse => 274,
- MessageType::MessageType_TronGetAddress => 275,
- MessageType::MessageType_TronAddress => 276,
- MessageType::MessageType_TronSignTx => 277,
- MessageType::MessageType_TronSignature => 278,
- MessageType::MessageType_TronContractRequest => 279,
- MessageType::MessageType_TronTransferContract => 280,
- MessageType::MessageType_TronTriggerSmartContract => 281,
- MessageType::MessageType_TronFreezeBalanceV2Contract => 282,
- MessageType::MessageType_TronUnfreezeBalanceV2Contract => 283,
- MessageType::MessageType_TronWithdrawUnfreeze => 284,
- MessageType::MessageType_TronVoteWitnessContract => 285,
- MessageType::MessageType_TronWithdrawBalance => 286,
- MessageType::MessageType_BenchmarkListNames => 287,
- MessageType::MessageType_BenchmarkNames => 288,
- MessageType::MessageType_BenchmarkRun => 289,
- MessageType::MessageType_BenchmarkResult => 290,
- MessageType::MessageType_TelemetryGet => 291,
- MessageType::MessageType_Telemetry => 292,
+ MessageType::MessageType_StellarInvokeHostFunctionOp => 167,
+ MessageType::MessageType_StellarTxExtRequest => 168,
+ MessageType::MessageType_StellarTxExt => 169,
+ MessageType::MessageType_CardanoGetPublicKey => 170,
+ MessageType::MessageType_CardanoPublicKey => 171,
+ MessageType::MessageType_CardanoGetAddress => 172,
+ MessageType::MessageType_CardanoAddress => 173,
+ MessageType::MessageType_CardanoTxItemAck => 174,
+ MessageType::MessageType_CardanoTxAuxiliaryDataSupplement => 175,
+ MessageType::MessageType_CardanoTxWitnessRequest => 176,
+ MessageType::MessageType_CardanoTxWitnessResponse => 177,
+ MessageType::MessageType_CardanoTxHostAck => 178,
+ MessageType::MessageType_CardanoTxBodyHash => 179,
+ MessageType::MessageType_CardanoSignTxFinished => 180,
+ MessageType::MessageType_CardanoSignTxInit => 181,
+ MessageType::MessageType_CardanoTxInput => 182,
+ MessageType::MessageType_CardanoTxOutput => 183,
+ MessageType::MessageType_CardanoAssetGroup => 184,
+ MessageType::MessageType_CardanoToken => 185,
+ MessageType::MessageType_CardanoTxCertificate => 186,
+ MessageType::MessageType_CardanoTxWithdrawal => 187,
+ MessageType::MessageType_CardanoTxAuxiliaryData => 188,
+ MessageType::MessageType_CardanoPoolOwner => 189,
+ MessageType::MessageType_CardanoPoolRelayParameters => 190,
+ MessageType::MessageType_CardanoGetNativeScriptHash => 191,
+ MessageType::MessageType_CardanoNativeScriptHash => 192,
+ MessageType::MessageType_CardanoTxMint => 193,
+ MessageType::MessageType_CardanoTxCollateralInput => 194,
+ MessageType::MessageType_CardanoTxRequiredSigner => 195,
+ MessageType::MessageType_CardanoTxInlineDatumChunk => 196,
+ MessageType::MessageType_CardanoTxReferenceScriptChunk => 197,
+ MessageType::MessageType_CardanoTxReferenceInput => 198,
+ MessageType::MessageType_CardanoSignMessageInit => 199,
+ MessageType::MessageType_CardanoMessageDataRequest => 200,
+ MessageType::MessageType_CardanoMessageDataResponse => 201,
+ MessageType::MessageType_CardanoMessageSignature => 202,
+ MessageType::MessageType_RippleGetAddress => 203,
+ MessageType::MessageType_RippleAddress => 204,
+ MessageType::MessageType_RippleSignTx => 205,
+ MessageType::MessageType_RippleSignedTx => 206,
+ MessageType::MessageType_MoneroTransactionInitRequest => 207,
+ MessageType::MessageType_MoneroTransactionInitAck => 208,
+ MessageType::MessageType_MoneroTransactionSetInputRequest => 209,
+ MessageType::MessageType_MoneroTransactionSetInputAck => 210,
+ MessageType::MessageType_MoneroTransactionInputViniRequest => 211,
+ MessageType::MessageType_MoneroTransactionInputViniAck => 212,
+ MessageType::MessageType_MoneroTransactionAllInputsSetRequest => 213,
+ MessageType::MessageType_MoneroTransactionAllInputsSetAck => 214,
+ MessageType::MessageType_MoneroTransactionSetOutputRequest => 215,
+ MessageType::MessageType_MoneroTransactionSetOutputAck => 216,
+ MessageType::MessageType_MoneroTransactionAllOutSetRequest => 217,
+ MessageType::MessageType_MoneroTransactionAllOutSetAck => 218,
+ MessageType::MessageType_MoneroTransactionSignInputRequest => 219,
+ MessageType::MessageType_MoneroTransactionSignInputAck => 220,
+ MessageType::MessageType_MoneroTransactionFinalRequest => 221,
+ MessageType::MessageType_MoneroTransactionFinalAck => 222,
+ MessageType::MessageType_MoneroKeyImageExportInitRequest => 223,
+ MessageType::MessageType_MoneroKeyImageExportInitAck => 224,
+ MessageType::MessageType_MoneroKeyImageSyncStepRequest => 225,
+ MessageType::MessageType_MoneroKeyImageSyncStepAck => 226,
+ MessageType::MessageType_MoneroKeyImageSyncFinalRequest => 227,
+ MessageType::MessageType_MoneroKeyImageSyncFinalAck => 228,
+ MessageType::MessageType_MoneroGetAddress => 229,
+ MessageType::MessageType_MoneroAddress => 230,
+ MessageType::MessageType_MoneroGetWatchKey => 231,
+ MessageType::MessageType_MoneroWatchKey => 232,
+ MessageType::MessageType_DebugMoneroDiagRequest => 233,
+ MessageType::MessageType_DebugMoneroDiagAck => 234,
+ MessageType::MessageType_MoneroGetTxKeyRequest => 235,
+ MessageType::MessageType_MoneroGetTxKeyAck => 236,
+ MessageType::MessageType_MoneroLiveRefreshStartRequest => 237,
+ MessageType::MessageType_MoneroLiveRefreshStartAck => 238,
+ MessageType::MessageType_MoneroLiveRefreshStepRequest => 239,
+ MessageType::MessageType_MoneroLiveRefreshStepAck => 240,
+ MessageType::MessageType_MoneroLiveRefreshFinalRequest => 241,
+ MessageType::MessageType_MoneroLiveRefreshFinalAck => 242,
+ MessageType::MessageType_EosGetPublicKey => 243,
+ MessageType::MessageType_EosPublicKey => 244,
+ MessageType::MessageType_EosSignTx => 245,
+ MessageType::MessageType_EosTxActionRequest => 246,
+ MessageType::MessageType_EosTxActionAck => 247,
+ MessageType::MessageType_EosSignedTx => 248,
+ MessageType::MessageType_WebAuthnListResidentCredentials => 249,
+ MessageType::MessageType_WebAuthnCredentials => 250,
+ MessageType::MessageType_WebAuthnAddResidentCredential => 251,
+ MessageType::MessageType_WebAuthnRemoveResidentCredential => 252,
+ MessageType::MessageType_WebAuthnCredentialsAck => 253,
+ MessageType::MessageType_SolanaGetPublicKey => 254,
+ MessageType::MessageType_SolanaPublicKey => 255,
+ MessageType::MessageType_SolanaGetAddress => 256,
+ MessageType::MessageType_SolanaAddress => 257,
+ MessageType::MessageType_SolanaSignTx => 258,
+ MessageType::MessageType_SolanaTxSignature => 259,
+ MessageType::MessageType_SolanaSignMessage => 260,
+ MessageType::MessageType_SolanaMessageSignature => 261,
+ MessageType::MessageType_SolanaVerifyMessage => 262,
+ MessageType::MessageType_ThpCreateNewSession => 263,
+ MessageType::MessageType_ThpCredentialRequest => 264,
+ MessageType::MessageType_ThpCredentialResponse => 265,
+ MessageType::MessageType_NostrGetPubkey => 266,
+ MessageType::MessageType_NostrPubkey => 267,
+ MessageType::MessageType_NostrSignEvent => 268,
+ MessageType::MessageType_NostrEventSignature => 269,
+ MessageType::MessageType_EvoluGetNode => 270,
+ MessageType::MessageType_EvoluNode => 271,
+ MessageType::MessageType_EvoluSignRegistrationRequest => 272,
+ MessageType::MessageType_EvoluRegistrationRequest => 273,
+ MessageType::MessageType_EvoluGetDelegatedIdentityKey => 274,
+ MessageType::MessageType_EvoluDelegatedIdentityKey => 275,
+ MessageType::MessageType_EvoluIndexManagement => 276,
+ MessageType::MessageType_EvoluIndexManagementResponse => 277,
+ MessageType::MessageType_TronGetAddress => 278,
+ MessageType::MessageType_TronAddress => 279,
+ MessageType::MessageType_TronSignTx => 280,
+ MessageType::MessageType_TronSignature => 281,
+ MessageType::MessageType_TronContractRequest => 282,
+ MessageType::MessageType_TronTransferContract => 283,
+ MessageType::MessageType_TronTriggerSmartContract => 284,
+ MessageType::MessageType_TronFreezeBalanceV2Contract => 285,
+ MessageType::MessageType_TronUnfreezeBalanceV2Contract => 286,
+ MessageType::MessageType_TronWithdrawUnfreeze => 287,
+ MessageType::MessageType_TronVoteWitnessContract => 288,
+ MessageType::MessageType_TronWithdrawBalance => 289,
+ MessageType::MessageType_BenchmarkListNames => 290,
+ MessageType::MessageType_BenchmarkNames => 291,
+ MessageType::MessageType_BenchmarkRun => 292,
+ MessageType::MessageType_BenchmarkResult => 293,
+ MessageType::MessageType_TelemetryGet => 294,
+ MessageType::MessageType_Telemetry => 295,
};
Self::enum_descriptor().value_by_index(index)
}
@@ -1836,7 +1854,7 @@ impl MessageType {
}
static file_descriptor_proto_data: &'static [u8] = b"\
- \n\x0emessages.proto\x12\x12hw.trezor.messages\x1a\roptions.proto*\x85g\
+ \n\x0emessages.proto\x12\x12hw.trezor.messages\x1a\roptions.proto*\xdah\
\n\x0bMessageType\x12(\n\x16MessageType_Initialize\x10\0\x1a\x0c\xb0\xb5\
\x18\x01\x80\xa6\x1d\x01\x90\xb5\x18\x01\x12\x1e\n\x10MessageType_Ping\
\x10\x01\x1a\x08\x80\xa6\x1d\x01\x90\xb5\x18\x01\x12%\n\x13MessageType_S\
@@ -2031,154 +2049,162 @@ static file_descriptor_proto_data: &'static [u8] = b"\
\x01\x125\n*MessageType_StellarPathPaymentStrictSendOp\x10\xdf\x01\x1a\
\x04\x90\xb5\x18\x01\x125\n*MessageType_StellarClaimClaimableBalanceOp\
\x10\xe1\x01\x1a\x04\x90\xb5\x18\x01\x12&\n\x1bMessageType_StellarSigned\
- Tx\x10\xe6\x01\x1a\x04\x98\xb5\x18\x01\x12*\n\x1fMessageType_CardanoGetP\
- ublicKey\x10\xb1\x02\x1a\x04\x90\xb5\x18\x01\x12'\n\x1cMessageType_Carda\
- noPublicKey\x10\xb2\x02\x1a\x04\x98\xb5\x18\x01\x12(\n\x1dMessageType_Ca\
- rdanoGetAddress\x10\xb3\x02\x1a\x04\x90\xb5\x18\x01\x12%\n\x1aMessageTyp\
- e_CardanoAddress\x10\xb4\x02\x1a\x04\x98\xb5\x18\x01\x12'\n\x1cMessageTy\
- pe_CardanoTxItemAck\x10\xb9\x02\x1a\x04\x98\xb5\x18\x01\x127\n,MessageTy\
- pe_CardanoTxAuxiliaryDataSupplement\x10\xba\x02\x1a\x04\x98\xb5\x18\x01\
- \x12.\n#MessageType_CardanoTxWitnessRequest\x10\xbb\x02\x1a\x04\x90\xb5\
- \x18\x01\x12/\n$MessageType_CardanoTxWitnessResponse\x10\xbc\x02\x1a\x04\
- \x98\xb5\x18\x01\x12'\n\x1cMessageType_CardanoTxHostAck\x10\xbd\x02\x1a\
- \x04\x90\xb5\x18\x01\x12(\n\x1dMessageType_CardanoTxBodyHash\x10\xbe\x02\
- \x1a\x04\x98\xb5\x18\x01\x12,\n!MessageType_CardanoSignTxFinished\x10\
- \xbf\x02\x1a\x04\x98\xb5\x18\x01\x12(\n\x1dMessageType_CardanoSignTxInit\
- \x10\xc0\x02\x1a\x04\x90\xb5\x18\x01\x12%\n\x1aMessageType_CardanoTxInpu\
- t\x10\xc1\x02\x1a\x04\x90\xb5\x18\x01\x12&\n\x1bMessageType_CardanoTxOut\
- put\x10\xc2\x02\x1a\x04\x90\xb5\x18\x01\x12(\n\x1dMessageType_CardanoAss\
- etGroup\x10\xc3\x02\x1a\x04\x90\xb5\x18\x01\x12#\n\x18MessageType_Cardan\
- oToken\x10\xc4\x02\x1a\x04\x90\xb5\x18\x01\x12+\n\x20MessageType_Cardano\
- TxCertificate\x10\xc5\x02\x1a\x04\x90\xb5\x18\x01\x12*\n\x1fMessageType_\
- CardanoTxWithdrawal\x10\xc6\x02\x1a\x04\x90\xb5\x18\x01\x12-\n\"MessageT\
- ype_CardanoTxAuxiliaryData\x10\xc7\x02\x1a\x04\x90\xb5\x18\x01\x12'\n\
- \x1cMessageType_CardanoPoolOwner\x10\xc8\x02\x1a\x04\x90\xb5\x18\x01\x12\
- 1\n&MessageType_CardanoPoolRelayParameters\x10\xc9\x02\x1a\x04\x90\xb5\
- \x18\x01\x121\n&MessageType_CardanoGetNativeScriptHash\x10\xca\x02\x1a\
- \x04\x90\xb5\x18\x01\x12.\n#MessageType_CardanoNativeScriptHash\x10\xcb\
- \x02\x1a\x04\x98\xb5\x18\x01\x12$\n\x19MessageType_CardanoTxMint\x10\xcc\
- \x02\x1a\x04\x90\xb5\x18\x01\x12/\n$MessageType_CardanoTxCollateralInput\
- \x10\xcd\x02\x1a\x04\x90\xb5\x18\x01\x12.\n#MessageType_CardanoTxRequire\
- dSigner\x10\xce\x02\x1a\x04\x90\xb5\x18\x01\x120\n%MessageType_CardanoTx\
- InlineDatumChunk\x10\xcf\x02\x1a\x04\x90\xb5\x18\x01\x124\n)MessageType_\
- CardanoTxReferenceScriptChunk\x10\xd0\x02\x1a\x04\x90\xb5\x18\x01\x12.\n\
- #MessageType_CardanoTxReferenceInput\x10\xd1\x02\x1a\x04\x90\xb5\x18\x01\
- \x12-\n\"MessageType_CardanoSignMessageInit\x10\xd2\x02\x1a\x04\x90\xb5\
- \x18\x01\x120\n%MessageType_CardanoMessageDataRequest\x10\xd3\x02\x1a\
- \x04\x98\xb5\x18\x01\x121\n&MessageType_CardanoMessageDataResponse\x10\
- \xd4\x02\x1a\x04\x90\xb5\x18\x01\x12.\n#MessageType_CardanoMessageSignat\
- ure\x10\xd5\x02\x1a\x04\x98\xb5\x18\x01\x12'\n\x1cMessageType_RippleGetA\
- ddress\x10\x90\x03\x1a\x04\x90\xb5\x18\x01\x12$\n\x19MessageType_RippleA\
- ddress\x10\x91\x03\x1a\x04\x98\xb5\x18\x01\x12#\n\x18MessageType_RippleS\
- ignTx\x10\x92\x03\x1a\x04\x90\xb5\x18\x01\x12%\n\x1aMessageType_RippleSi\
- gnedTx\x10\x93\x03\x1a\x04\x98\xb5\x18\x01\x123\n(MessageType_MoneroTran\
- sactionInitRequest\x10\xf5\x03\x1a\x04\x90\xb5\x18\x01\x12/\n$MessageTyp\
- e_MoneroTransactionInitAck\x10\xf6\x03\x1a\x04\x98\xb5\x18\x01\x127\n,Me\
- ssageType_MoneroTransactionSetInputRequest\x10\xf7\x03\x1a\x04\x90\xb5\
- \x18\x01\x123\n(MessageType_MoneroTransactionSetInputAck\x10\xf8\x03\x1a\
- \x04\x98\xb5\x18\x01\x128\n-MessageType_MoneroTransactionInputViniReques\
- t\x10\xfb\x03\x1a\x04\x90\xb5\x18\x01\x124\n)MessageType_MoneroTransacti\
- onInputViniAck\x10\xfc\x03\x1a\x04\x98\xb5\x18\x01\x12;\n0MessageType_Mo\
- neroTransactionAllInputsSetRequest\x10\xfd\x03\x1a\x04\x90\xb5\x18\x01\
- \x127\n,MessageType_MoneroTransactionAllInputsSetAck\x10\xfe\x03\x1a\x04\
- \x98\xb5\x18\x01\x128\n-MessageType_MoneroTransactionSetOutputRequest\
- \x10\xff\x03\x1a\x04\x90\xb5\x18\x01\x124\n)MessageType_MoneroTransactio\
- nSetOutputAck\x10\x80\x04\x1a\x04\x98\xb5\x18\x01\x128\n-MessageType_Mon\
- eroTransactionAllOutSetRequest\x10\x81\x04\x1a\x04\x90\xb5\x18\x01\x124\
- \n)MessageType_MoneroTransactionAllOutSetAck\x10\x82\x04\x1a\x04\x98\xb5\
- \x18\x01\x128\n-MessageType_MoneroTransactionSignInputRequest\x10\x83\
- \x04\x1a\x04\x90\xb5\x18\x01\x124\n)MessageType_MoneroTransactionSignInp\
- utAck\x10\x84\x04\x1a\x04\x98\xb5\x18\x01\x124\n)MessageType_MoneroTrans\
- actionFinalRequest\x10\x85\x04\x1a\x04\x90\xb5\x18\x01\x120\n%MessageTyp\
- e_MoneroTransactionFinalAck\x10\x86\x04\x1a\x04\x98\xb5\x18\x01\x126\n+M\
- essageType_MoneroKeyImageExportInitRequest\x10\x92\x04\x1a\x04\x90\xb5\
- \x18\x01\x122\n'MessageType_MoneroKeyImageExportInitAck\x10\x93\x04\x1a\
- \x04\x98\xb5\x18\x01\x124\n)MessageType_MoneroKeyImageSyncStepRequest\
- \x10\x94\x04\x1a\x04\x90\xb5\x18\x01\x120\n%MessageType_MoneroKeyImageSy\
- ncStepAck\x10\x95\x04\x1a\x04\x98\xb5\x18\x01\x125\n*MessageType_MoneroK\
- eyImageSyncFinalRequest\x10\x96\x04\x1a\x04\x90\xb5\x18\x01\x121\n&Messa\
- geType_MoneroKeyImageSyncFinalAck\x10\x97\x04\x1a\x04\x98\xb5\x18\x01\
- \x12'\n\x1cMessageType_MoneroGetAddress\x10\x9c\x04\x1a\x04\x90\xb5\x18\
- \x01\x12$\n\x19MessageType_MoneroAddress\x10\x9d\x04\x1a\x04\x98\xb5\x18\
- \x01\x12(\n\x1dMessageType_MoneroGetWatchKey\x10\x9e\x04\x1a\x04\x90\xb5\
- \x18\x01\x12%\n\x1aMessageType_MoneroWatchKey\x10\x9f\x04\x1a\x04\x98\
- \xb5\x18\x01\x12-\n\"MessageType_DebugMoneroDiagRequest\x10\xa2\x04\x1a\
- \x04\x90\xb5\x18\x01\x12)\n\x1eMessageType_DebugMoneroDiagAck\x10\xa3\
- \x04\x1a\x04\x98\xb5\x18\x01\x12,\n!MessageType_MoneroGetTxKeyRequest\
- \x10\xa6\x04\x1a\x04\x90\xb5\x18\x01\x12(\n\x1dMessageType_MoneroGetTxKe\
- yAck\x10\xa7\x04\x1a\x04\x98\xb5\x18\x01\x124\n)MessageType_MoneroLiveRe\
- freshStartRequest\x10\xa8\x04\x1a\x04\x90\xb5\x18\x01\x120\n%MessageType\
- _MoneroLiveRefreshStartAck\x10\xa9\x04\x1a\x04\x98\xb5\x18\x01\x123\n(Me\
- ssageType_MoneroLiveRefreshStepRequest\x10\xaa\x04\x1a\x04\x90\xb5\x18\
- \x01\x12/\n$MessageType_MoneroLiveRefreshStepAck\x10\xab\x04\x1a\x04\x98\
- \xb5\x18\x01\x124\n)MessageType_MoneroLiveRefreshFinalRequest\x10\xac\
- \x04\x1a\x04\x90\xb5\x18\x01\x120\n%MessageType_MoneroLiveRefreshFinalAc\
- k\x10\xad\x04\x1a\x04\x98\xb5\x18\x01\x12&\n\x1bMessageType_EosGetPublic\
- Key\x10\xd8\x04\x1a\x04\x90\xb5\x18\x01\x12#\n\x18MessageType_EosPublicK\
- ey\x10\xd9\x04\x1a\x04\x98\xb5\x18\x01\x12\x20\n\x15MessageType_EosSignT\
- x\x10\xda\x04\x1a\x04\x90\xb5\x18\x01\x12)\n\x1eMessageType_EosTxActionR\
- equest\x10\xdb\x04\x1a\x04\x98\xb5\x18\x01\x12%\n\x1aMessageType_EosTxAc\
- tionAck\x10\xdc\x04\x1a\x04\x90\xb5\x18\x01\x12\"\n\x17MessageType_EosSi\
- gnedTx\x10\xdd\x04\x1a\x04\x98\xb5\x18\x01\x126\n+MessageType_WebAuthnLi\
- stResidentCredentials\x10\xa0\x06\x1a\x04\x90\xb5\x18\x01\x12*\n\x1fMess\
- ageType_WebAuthnCredentials\x10\xa1\x06\x1a\x04\x98\xb5\x18\x01\x124\n)M\
- essageType_WebAuthnAddResidentCredential\x10\xa2\x06\x1a\x04\x90\xb5\x18\
- \x01\x127\n,MessageType_WebAuthnRemoveResidentCredential\x10\xa3\x06\x1a\
- \x04\x90\xb5\x18\x01\x12-\n\"MessageType_WebAuthnCredentialsAck\x10\xa4\
- \x06\x1a\x04\x90\xb5\x18\x01\x12)\n\x1eMessageType_SolanaGetPublicKey\
- \x10\x84\x07\x1a\x04\x90\xb5\x18\x01\x12&\n\x1bMessageType_SolanaPublicK\
- ey\x10\x85\x07\x1a\x04\x98\xb5\x18\x01\x12'\n\x1cMessageType_SolanaGetAd\
- dress\x10\x86\x07\x1a\x04\x90\xb5\x18\x01\x12$\n\x19MessageType_SolanaAd\
- dress\x10\x87\x07\x1a\x04\x98\xb5\x18\x01\x12#\n\x18MessageType_SolanaSi\
- gnTx\x10\x88\x07\x1a\x04\x90\xb5\x18\x01\x12(\n\x1dMessageType_SolanaTxS\
- ignature\x10\x89\x07\x1a\x04\x98\xb5\x18\x01\x12(\n\x1dMessageType_Solan\
- aSignMessage\x10\x8a\x07\x1a\x04\x90\xb5\x18\x01\x12-\n\"MessageType_Sol\
- anaMessageSignature\x10\x8b\x07\x1a\x04\x98\xb5\x18\x01\x12*\n\x1fMessag\
- eType_SolanaVerifyMessage\x10\x8c\x07\x1a\x04\x90\xb5\x18\x01\x12.\n\x1f\
- MessageType_ThpCreateNewSession\x10\xe8\x07\x1a\x08\x80\xa6\x1d\x01\x90\
- \xb5\x18\x01\x12/\n\x20MessageType_ThpCredentialRequest\x10\xf8\x07\x1a\
- \x08\x80\xa6\x1d\x01\x90\xb5\x18\x01\x120\n!MessageType_ThpCredentialRes\
- ponse\x10\xf9\x07\x1a\x08\x80\xa6\x1d\x01\x98\xb5\x18\x01\x12%\n\x1aMess\
- ageType_NostrGetPubkey\x10\xd1\x0f\x1a\x04\x90\xb5\x18\x01\x12\"\n\x17Me\
- ssageType_NostrPubkey\x10\xd2\x0f\x1a\x04\x98\xb5\x18\x01\x12%\n\x1aMess\
- ageType_NostrSignEvent\x10\xd3\x0f\x1a\x04\x90\xb5\x18\x01\x12*\n\x1fMes\
- sageType_NostrEventSignature\x10\xd4\x0f\x1a\x04\x98\xb5\x18\x01\x12'\n\
- \x18MessageType_EvoluGetNode\x10\xb4\x10\x1a\x08\x80\xa6\x1d\x01\x90\xb5\
- \x18\x01\x12$\n\x15MessageType_EvoluNode\x10\xb5\x10\x1a\x08\x80\xa6\x1d\
- \x01\x98\xb5\x18\x01\x127\n(MessageType_EvoluSignRegistrationRequest\x10\
- \xb6\x10\x1a\x08\x80\xa6\x1d\x01\x90\xb5\x18\x01\x123\n$MessageType_Evol\
- uRegistrationRequest\x10\xb7\x10\x1a\x08\x80\xa6\x1d\x01\x98\xb5\x18\x01\
- \x127\n(MessageType_EvoluGetDelegatedIdentityKey\x10\xb8\x10\x1a\x08\x80\
- \xa6\x1d\x01\x90\xb5\x18\x01\x124\n%MessageType_EvoluDelegatedIdentityKe\
- y\x10\xb9\x10\x1a\x08\x80\xa6\x1d\x01\x98\xb5\x18\x01\x12/\n\x20MessageT\
- ype_EvoluIndexManagement\x10\xba\x10\x1a\x08\x80\xa6\x1d\x01\x90\xb5\x18\
- \x01\x127\n(MessageType_EvoluIndexManagementResponse\x10\xbb\x10\x1a\x08\
- \x80\xa6\x1d\x01\x98\xb5\x18\x01\x12%\n\x1aMessageType_TronGetAddress\
- \x10\x98\x11\x1a\x04\x90\xb5\x18\x01\x12\"\n\x17MessageType_TronAddress\
- \x10\x99\x11\x1a\x04\x98\xb5\x18\x01\x12!\n\x16MessageType_TronSignTx\
- \x10\x9a\x11\x1a\x04\x90\xb5\x18\x01\x12$\n\x19MessageType_TronSignature\
- \x10\x9b\x11\x1a\x04\x98\xb5\x18\x01\x12*\n\x1fMessageType_TronContractR\
- equest\x10\x9c\x11\x1a\x04\x98\xb5\x18\x01\x12+\n\x20MessageType_TronTra\
- nsferContract\x10\x9d\x11\x1a\x04\x90\xb5\x18\x01\x12/\n$MessageType_Tro\
- nTriggerSmartContract\x10\x9e\x11\x1a\x04\x90\xb5\x18\x01\x122\n'Message\
- Type_TronFreezeBalanceV2Contract\x10\x9f\x11\x1a\x04\x90\xb5\x18\x01\x12\
- 4\n)MessageType_TronUnfreezeBalanceV2Contract\x10\xa0\x11\x1a\x04\x90\
- \xb5\x18\x01\x12+\n\x20MessageType_TronWithdrawUnfreeze\x10\xa1\x11\x1a\
- \x04\x90\xb5\x18\x01\x12.\n#MessageType_TronVoteWitnessContract\x10\xa2\
- \x11\x1a\x04\x90\xb5\x18\x01\x12*\n\x1fMessageType_TronWithdrawBalance\
- \x10\xa5\x11\x1a\x04\x90\xb5\x18\x01\x12)\n\x1eMessageType_BenchmarkList\
- Names\x10\x8cG\x1a\x04\x80\xa6\x1d\x01\x12%\n\x1aMessageType_BenchmarkNa\
- mes\x10\x8dG\x1a\x04\x80\xa6\x1d\x01\x12#\n\x18MessageType_BenchmarkRun\
- \x10\x8eG\x1a\x04\x80\xa6\x1d\x01\x12&\n\x1bMessageType_BenchmarkResult\
- \x10\x8fG\x1a\x04\x80\xa6\x1d\x01\x12'\n\x18MessageType_TelemetryGet\x10\
- \xcc\x08\x1a\x08\x80\xa6\x1d\x01\x90\xb5\x18\x01\x12$\n\x15MessageType_T\
- elemetry\x10\xcd\x08\x1a\x08\x80\xa6\x1d\x01\x98\xb5\x18\x01\x1a\x08\xc8\
- \xf3\x18\x01\xd0\xf3\x18\x01\"\x04\x08Z\x10\\\"\x04\x08M\x10N\"\x04\x08G\
- \x10J\"\x04\x08r\x10z\"\x05\x08{\x10\x95\x01\"\x06\x08\xdb\x01\x10\xdb\
- \x01\"\x06\x08\xe0\x01\x10\xe0\x01\"\x06\x08\xac\x02\x10\xb0\x02\"\x06\
- \x08\xb5\x02\x10\xb8\x02\"\x06\x08\xbc\x05\x10\xc5\x05\"\x06\x08\xe9\x07\
- \x10\xf7\x07\"\x06\x08\xfa\x07\x10\xcb\x08B8\n#com.satoshilabs.trezor.li\
- b.protobufB\rTrezorMessage\x80\xa6\x1d\x01\
+ Tx\x10\xe6\x01\x1a\x04\x98\xb5\x18\x01\x122\n'MessageType_StellarInvokeH\
+ ostFunctionOp\x10\xeb\x01\x1a\x04\x90\xb5\x18\x01\x12*\n\x1fMessageType_\
+ StellarTxExtRequest\x10\xee\x01\x1a\x04\x98\xb5\x18\x01\x12#\n\x18Messag\
+ eType_StellarTxExt\x10\xef\x01\x1a\x04\x90\xb5\x18\x01\x12*\n\x1fMessage\
+ Type_CardanoGetPublicKey\x10\xb1\x02\x1a\x04\x90\xb5\x18\x01\x12'\n\x1cM\
+ essageType_CardanoPublicKey\x10\xb2\x02\x1a\x04\x98\xb5\x18\x01\x12(\n\
+ \x1dMessageType_CardanoGetAddress\x10\xb3\x02\x1a\x04\x90\xb5\x18\x01\
+ \x12%\n\x1aMessageType_CardanoAddress\x10\xb4\x02\x1a\x04\x98\xb5\x18\
+ \x01\x12'\n\x1cMessageType_CardanoTxItemAck\x10\xb9\x02\x1a\x04\x98\xb5\
+ \x18\x01\x127\n,MessageType_CardanoTxAuxiliaryDataSupplement\x10\xba\x02\
+ \x1a\x04\x98\xb5\x18\x01\x12.\n#MessageType_CardanoTxWitnessRequest\x10\
+ \xbb\x02\x1a\x04\x90\xb5\x18\x01\x12/\n$MessageType_CardanoTxWitnessResp\
+ onse\x10\xbc\x02\x1a\x04\x98\xb5\x18\x01\x12'\n\x1cMessageType_CardanoTx\
+ HostAck\x10\xbd\x02\x1a\x04\x90\xb5\x18\x01\x12(\n\x1dMessageType_Cardan\
+ oTxBodyHash\x10\xbe\x02\x1a\x04\x98\xb5\x18\x01\x12,\n!MessageType_Carda\
+ noSignTxFinished\x10\xbf\x02\x1a\x04\x98\xb5\x18\x01\x12(\n\x1dMessageTy\
+ pe_CardanoSignTxInit\x10\xc0\x02\x1a\x04\x90\xb5\x18\x01\x12%\n\x1aMessa\
+ geType_CardanoTxInput\x10\xc1\x02\x1a\x04\x90\xb5\x18\x01\x12&\n\x1bMess\
+ ageType_CardanoTxOutput\x10\xc2\x02\x1a\x04\x90\xb5\x18\x01\x12(\n\x1dMe\
+ ssageType_CardanoAssetGroup\x10\xc3\x02\x1a\x04\x90\xb5\x18\x01\x12#\n\
+ \x18MessageType_CardanoToken\x10\xc4\x02\x1a\x04\x90\xb5\x18\x01\x12+\n\
+ \x20MessageType_CardanoTxCertificate\x10\xc5\x02\x1a\x04\x90\xb5\x18\x01\
+ \x12*\n\x1fMessageType_CardanoTxWithdrawal\x10\xc6\x02\x1a\x04\x90\xb5\
+ \x18\x01\x12-\n\"MessageType_CardanoTxAuxiliaryData\x10\xc7\x02\x1a\x04\
+ \x90\xb5\x18\x01\x12'\n\x1cMessageType_CardanoPoolOwner\x10\xc8\x02\x1a\
+ \x04\x90\xb5\x18\x01\x121\n&MessageType_CardanoPoolRelayParameters\x10\
+ \xc9\x02\x1a\x04\x90\xb5\x18\x01\x121\n&MessageType_CardanoGetNativeScri\
+ ptHash\x10\xca\x02\x1a\x04\x90\xb5\x18\x01\x12.\n#MessageType_CardanoNat\
+ iveScriptHash\x10\xcb\x02\x1a\x04\x98\xb5\x18\x01\x12$\n\x19MessageType_\
+ CardanoTxMint\x10\xcc\x02\x1a\x04\x90\xb5\x18\x01\x12/\n$MessageType_Car\
+ danoTxCollateralInput\x10\xcd\x02\x1a\x04\x90\xb5\x18\x01\x12.\n#Message\
+ Type_CardanoTxRequiredSigner\x10\xce\x02\x1a\x04\x90\xb5\x18\x01\x120\n%\
+ MessageType_CardanoTxInlineDatumChunk\x10\xcf\x02\x1a\x04\x90\xb5\x18\
+ \x01\x124\n)MessageType_CardanoTxReferenceScriptChunk\x10\xd0\x02\x1a\
+ \x04\x90\xb5\x18\x01\x12.\n#MessageType_CardanoTxReferenceInput\x10\xd1\
+ \x02\x1a\x04\x90\xb5\x18\x01\x12-\n\"MessageType_CardanoSignMessageInit\
+ \x10\xd2\x02\x1a\x04\x90\xb5\x18\x01\x120\n%MessageType_CardanoMessageDa\
+ taRequest\x10\xd3\x02\x1a\x04\x98\xb5\x18\x01\x121\n&MessageType_Cardano\
+ MessageDataResponse\x10\xd4\x02\x1a\x04\x90\xb5\x18\x01\x12.\n#MessageTy\
+ pe_CardanoMessageSignature\x10\xd5\x02\x1a\x04\x98\xb5\x18\x01\x12'\n\
+ \x1cMessageType_RippleGetAddress\x10\x90\x03\x1a\x04\x90\xb5\x18\x01\x12\
+ $\n\x19MessageType_RippleAddress\x10\x91\x03\x1a\x04\x98\xb5\x18\x01\x12\
+ #\n\x18MessageType_RippleSignTx\x10\x92\x03\x1a\x04\x90\xb5\x18\x01\x12%\
+ \n\x1aMessageType_RippleSignedTx\x10\x93\x03\x1a\x04\x98\xb5\x18\x01\x12\
+ 3\n(MessageType_MoneroTransactionInitRequest\x10\xf5\x03\x1a\x04\x90\xb5\
+ \x18\x01\x12/\n$MessageType_MoneroTransactionInitAck\x10\xf6\x03\x1a\x04\
+ \x98\xb5\x18\x01\x127\n,MessageType_MoneroTransactionSetInputRequest\x10\
+ \xf7\x03\x1a\x04\x90\xb5\x18\x01\x123\n(MessageType_MoneroTransactionSet\
+ InputAck\x10\xf8\x03\x1a\x04\x98\xb5\x18\x01\x128\n-MessageType_MoneroTr\
+ ansactionInputViniRequest\x10\xfb\x03\x1a\x04\x90\xb5\x18\x01\x124\n)Mes\
+ sageType_MoneroTransactionInputViniAck\x10\xfc\x03\x1a\x04\x98\xb5\x18\
+ \x01\x12;\n0MessageType_MoneroTransactionAllInputsSetRequest\x10\xfd\x03\
+ \x1a\x04\x90\xb5\x18\x01\x127\n,MessageType_MoneroTransactionAllInputsSe\
+ tAck\x10\xfe\x03\x1a\x04\x98\xb5\x18\x01\x128\n-MessageType_MoneroTransa\
+ ctionSetOutputRequest\x10\xff\x03\x1a\x04\x90\xb5\x18\x01\x124\n)Message\
+ Type_MoneroTransactionSetOutputAck\x10\x80\x04\x1a\x04\x98\xb5\x18\x01\
+ \x128\n-MessageType_MoneroTransactionAllOutSetRequest\x10\x81\x04\x1a\
+ \x04\x90\xb5\x18\x01\x124\n)MessageType_MoneroTransactionAllOutSetAck\
+ \x10\x82\x04\x1a\x04\x98\xb5\x18\x01\x128\n-MessageType_MoneroTransactio\
+ nSignInputRequest\x10\x83\x04\x1a\x04\x90\xb5\x18\x01\x124\n)MessageType\
+ _MoneroTransactionSignInputAck\x10\x84\x04\x1a\x04\x98\xb5\x18\x01\x124\
+ \n)MessageType_MoneroTransactionFinalRequest\x10\x85\x04\x1a\x04\x90\xb5\
+ \x18\x01\x120\n%MessageType_MoneroTransactionFinalAck\x10\x86\x04\x1a\
+ \x04\x98\xb5\x18\x01\x126\n+MessageType_MoneroKeyImageExportInitRequest\
+ \x10\x92\x04\x1a\x04\x90\xb5\x18\x01\x122\n'MessageType_MoneroKeyImageEx\
+ portInitAck\x10\x93\x04\x1a\x04\x98\xb5\x18\x01\x124\n)MessageType_Moner\
+ oKeyImageSyncStepRequest\x10\x94\x04\x1a\x04\x90\xb5\x18\x01\x120\n%Mess\
+ ageType_MoneroKeyImageSyncStepAck\x10\x95\x04\x1a\x04\x98\xb5\x18\x01\
+ \x125\n*MessageType_MoneroKeyImageSyncFinalRequest\x10\x96\x04\x1a\x04\
+ \x90\xb5\x18\x01\x121\n&MessageType_MoneroKeyImageSyncFinalAck\x10\x97\
+ \x04\x1a\x04\x98\xb5\x18\x01\x12'\n\x1cMessageType_MoneroGetAddress\x10\
+ \x9c\x04\x1a\x04\x90\xb5\x18\x01\x12$\n\x19MessageType_MoneroAddress\x10\
+ \x9d\x04\x1a\x04\x98\xb5\x18\x01\x12(\n\x1dMessageType_MoneroGetWatchKey\
+ \x10\x9e\x04\x1a\x04\x90\xb5\x18\x01\x12%\n\x1aMessageType_MoneroWatchKe\
+ y\x10\x9f\x04\x1a\x04\x98\xb5\x18\x01\x12-\n\"MessageType_DebugMoneroDia\
+ gRequest\x10\xa2\x04\x1a\x04\x90\xb5\x18\x01\x12)\n\x1eMessageType_Debug\
+ MoneroDiagAck\x10\xa3\x04\x1a\x04\x98\xb5\x18\x01\x12,\n!MessageType_Mon\
+ eroGetTxKeyRequest\x10\xa6\x04\x1a\x04\x90\xb5\x18\x01\x12(\n\x1dMessage\
+ Type_MoneroGetTxKeyAck\x10\xa7\x04\x1a\x04\x98\xb5\x18\x01\x124\n)Messag\
+ eType_MoneroLiveRefreshStartRequest\x10\xa8\x04\x1a\x04\x90\xb5\x18\x01\
+ \x120\n%MessageType_MoneroLiveRefreshStartAck\x10\xa9\x04\x1a\x04\x98\
+ \xb5\x18\x01\x123\n(MessageType_MoneroLiveRefreshStepRequest\x10\xaa\x04\
+ \x1a\x04\x90\xb5\x18\x01\x12/\n$MessageType_MoneroLiveRefreshStepAck\x10\
+ \xab\x04\x1a\x04\x98\xb5\x18\x01\x124\n)MessageType_MoneroLiveRefreshFin\
+ alRequest\x10\xac\x04\x1a\x04\x90\xb5\x18\x01\x120\n%MessageType_MoneroL\
+ iveRefreshFinalAck\x10\xad\x04\x1a\x04\x98\xb5\x18\x01\x12&\n\x1bMessage\
+ Type_EosGetPublicKey\x10\xd8\x04\x1a\x04\x90\xb5\x18\x01\x12#\n\x18Messa\
+ geType_EosPublicKey\x10\xd9\x04\x1a\x04\x98\xb5\x18\x01\x12\x20\n\x15Mes\
+ sageType_EosSignTx\x10\xda\x04\x1a\x04\x90\xb5\x18\x01\x12)\n\x1eMessage\
+ Type_EosTxActionRequest\x10\xdb\x04\x1a\x04\x98\xb5\x18\x01\x12%\n\x1aMe\
+ ssageType_EosTxActionAck\x10\xdc\x04\x1a\x04\x90\xb5\x18\x01\x12\"\n\x17\
+ MessageType_EosSignedTx\x10\xdd\x04\x1a\x04\x98\xb5\x18\x01\x126\n+Messa\
+ geType_WebAuthnListResidentCredentials\x10\xa0\x06\x1a\x04\x90\xb5\x18\
+ \x01\x12*\n\x1fMessageType_WebAuthnCredentials\x10\xa1\x06\x1a\x04\x98\
+ \xb5\x18\x01\x124\n)MessageType_WebAuthnAddResidentCredential\x10\xa2\
+ \x06\x1a\x04\x90\xb5\x18\x01\x127\n,MessageType_WebAuthnRemoveResidentCr\
+ edential\x10\xa3\x06\x1a\x04\x90\xb5\x18\x01\x12-\n\"MessageType_WebAuth\
+ nCredentialsAck\x10\xa4\x06\x1a\x04\x90\xb5\x18\x01\x12)\n\x1eMessageTyp\
+ e_SolanaGetPublicKey\x10\x84\x07\x1a\x04\x90\xb5\x18\x01\x12&\n\x1bMessa\
+ geType_SolanaPublicKey\x10\x85\x07\x1a\x04\x98\xb5\x18\x01\x12'\n\x1cMes\
+ sageType_SolanaGetAddress\x10\x86\x07\x1a\x04\x90\xb5\x18\x01\x12$\n\x19\
+ MessageType_SolanaAddress\x10\x87\x07\x1a\x04\x98\xb5\x18\x01\x12#\n\x18\
+ MessageType_SolanaSignTx\x10\x88\x07\x1a\x04\x90\xb5\x18\x01\x12(\n\x1dM\
+ essageType_SolanaTxSignature\x10\x89\x07\x1a\x04\x98\xb5\x18\x01\x12(\n\
+ \x1dMessageType_SolanaSignMessage\x10\x8a\x07\x1a\x04\x90\xb5\x18\x01\
+ \x12-\n\"MessageType_SolanaMessageSignature\x10\x8b\x07\x1a\x04\x98\xb5\
+ \x18\x01\x12*\n\x1fMessageType_SolanaVerifyMessage\x10\x8c\x07\x1a\x04\
+ \x90\xb5\x18\x01\x12.\n\x1fMessageType_ThpCreateNewSession\x10\xe8\x07\
+ \x1a\x08\x80\xa6\x1d\x01\x90\xb5\x18\x01\x12/\n\x20MessageType_ThpCreden\
+ tialRequest\x10\xf8\x07\x1a\x08\x80\xa6\x1d\x01\x90\xb5\x18\x01\x120\n!M\
+ essageType_ThpCredentialResponse\x10\xf9\x07\x1a\x08\x80\xa6\x1d\x01\x98\
+ \xb5\x18\x01\x12%\n\x1aMessageType_NostrGetPubkey\x10\xd1\x0f\x1a\x04\
+ \x90\xb5\x18\x01\x12\"\n\x17MessageType_NostrPubkey\x10\xd2\x0f\x1a\x04\
+ \x98\xb5\x18\x01\x12%\n\x1aMessageType_NostrSignEvent\x10\xd3\x0f\x1a\
+ \x04\x90\xb5\x18\x01\x12*\n\x1fMessageType_NostrEventSignature\x10\xd4\
+ \x0f\x1a\x04\x98\xb5\x18\x01\x12'\n\x18MessageType_EvoluGetNode\x10\xb4\
+ \x10\x1a\x08\x80\xa6\x1d\x01\x90\xb5\x18\x01\x12$\n\x15MessageType_Evolu\
+ Node\x10\xb5\x10\x1a\x08\x80\xa6\x1d\x01\x98\xb5\x18\x01\x127\n(MessageT\
+ ype_EvoluSignRegistrationRequest\x10\xb6\x10\x1a\x08\x80\xa6\x1d\x01\x90\
+ \xb5\x18\x01\x123\n$MessageType_EvoluRegistrationRequest\x10\xb7\x10\x1a\
+ \x08\x80\xa6\x1d\x01\x98\xb5\x18\x01\x127\n(MessageType_EvoluGetDelegate\
+ dIdentityKey\x10\xb8\x10\x1a\x08\x80\xa6\x1d\x01\x90\xb5\x18\x01\x124\n%\
+ MessageType_EvoluDelegatedIdentityKey\x10\xb9\x10\x1a\x08\x80\xa6\x1d\
+ \x01\x98\xb5\x18\x01\x12/\n\x20MessageType_EvoluIndexManagement\x10\xba\
+ \x10\x1a\x08\x80\xa6\x1d\x01\x90\xb5\x18\x01\x127\n(MessageType_EvoluInd\
+ exManagementResponse\x10\xbb\x10\x1a\x08\x80\xa6\x1d\x01\x98\xb5\x18\x01\
+ \x12%\n\x1aMessageType_TronGetAddress\x10\x98\x11\x1a\x04\x90\xb5\x18\
+ \x01\x12\"\n\x17MessageType_TronAddress\x10\x99\x11\x1a\x04\x98\xb5\x18\
+ \x01\x12!\n\x16MessageType_TronSignTx\x10\x9a\x11\x1a\x04\x90\xb5\x18\
+ \x01\x12$\n\x19MessageType_TronSignature\x10\x9b\x11\x1a\x04\x98\xb5\x18\
+ \x01\x12*\n\x1fMessageType_TronContractRequest\x10\x9c\x11\x1a\x04\x98\
+ \xb5\x18\x01\x12+\n\x20MessageType_TronTransferContract\x10\x9d\x11\x1a\
+ \x04\x90\xb5\x18\x01\x12/\n$MessageType_TronTriggerSmartContract\x10\x9e\
+ \x11\x1a\x04\x90\xb5\x18\x01\x122\n'MessageType_TronFreezeBalanceV2Contr\
+ act\x10\x9f\x11\x1a\x04\x90\xb5\x18\x01\x124\n)MessageType_TronUnfreezeB\
+ alanceV2Contract\x10\xa0\x11\x1a\x04\x90\xb5\x18\x01\x12+\n\x20MessageTy\
+ pe_TronWithdrawUnfreeze\x10\xa1\x11\x1a\x04\x90\xb5\x18\x01\x12.\n#Messa\
+ geType_TronVoteWitnessContract\x10\xa2\x11\x1a\x04\x90\xb5\x18\x01\x12*\
+ \n\x1fMessageType_TronWithdrawBalance\x10\xa5\x11\x1a\x04\x90\xb5\x18\
+ \x01\x12)\n\x1eMessageType_BenchmarkListNames\x10\x8cG\x1a\x04\x80\xa6\
+ \x1d\x01\x12%\n\x1aMessageType_BenchmarkNames\x10\x8dG\x1a\x04\x80\xa6\
+ \x1d\x01\x12#\n\x18MessageType_BenchmarkRun\x10\x8eG\x1a\x04\x80\xa6\x1d\
+ \x01\x12&\n\x1bMessageType_BenchmarkResult\x10\x8fG\x1a\x04\x80\xa6\x1d\
+ \x01\x12'\n\x18MessageType_TelemetryGet\x10\xcc\x08\x1a\x08\x80\xa6\x1d\
+ \x01\x90\xb5\x18\x01\x12$\n\x15MessageType_Telemetry\x10\xcd\x08\x1a\x08\
+ \x80\xa6\x1d\x01\x98\xb5\x18\x01\x1a\x08\xc8\xf3\x18\x01\xd0\xf3\x18\x01\
+ \"\x04\x08Z\x10\\\"\x04\x08M\x10N\"\x04\x08G\x10J\"\x04\x08r\x10z\"\x05\
+ \x08{\x10\x95\x01\"\x06\x08\xdb\x01\x10\xdb\x01\"\x06\x08\xe0\x01\x10\
+ \xe0\x01\"\x06\x08\xe2\x01\x10\xe2\x01\"\x06\x08\xe3\x01\x10\xe3\x01\"\
+ \x06\x08\xe4\x01\x10\xe4\x01\"\x06\x08\xe5\x01\x10\xe5\x01\"\x06\x08\xe7\
+ \x01\x10\xe7\x01\"\x06\x08\xe8\x01\x10\xe8\x01\"\x06\x08\xe9\x01\x10\xe9\
+ \x01\"\x06\x08\xea\x01\x10\xea\x01\"\x06\x08\xec\x01\x10\xec\x01\"\x06\
+ \x08\xed\x01\x10\xed\x01\"\x06\x08\xac\x02\x10\xb0\x02\"\x06\x08\xb5\x02\
+ \x10\xb8\x02\"\x06\x08\xbc\x05\x10\xc5\x05\"\x06\x08\xe9\x07\x10\xf7\x07\
+ \"\x06\x08\xfa\x07\x10\xcb\x08B8\n#com.satoshilabs.trezor.lib.protobufB\
+ \rTrezorMessage\x80\xa6\x1d\x01\
";
/// `FileDescriptorProto` object which was a source for this generated file
diff --git a/rust/trezor-client/src/protos/generated/messages_stellar.rs b/rust/trezor-client/src/protos/generated/messages_stellar.rs
index 0be0572c..57757839 100644
--- a/rust/trezor-client/src/protos/generated/messages_stellar.rs
+++ b/rust/trezor-client/src/protos/generated/messages_stellar.rs
@@ -6267,6 +6267,4021 @@ impl ::protobuf::reflect::ProtobufValue for StellarSignedTx {
type RuntimeType = ::protobuf::reflect::rt::RuntimeTypeMessage<Self>;
}
+// @@protoc_insertion_point(message:hw.trezor.messages.stellar.StellarSCVal)
+#[derive(PartialEq,Clone,Default,Debug)]
+pub struct StellarSCVal {
+ // message fields
+ // @@protoc_insertion_point(field:hw.trezor.messages.stellar.StellarSCVal.type)
+ pub type_: ::std::option::Option<::protobuf::EnumOrUnknown<stellar_scval::StellarSCValType>>,
+ // @@protoc_insertion_point(field:hw.trezor.messages.stellar.StellarSCVal.b)
+ pub b: ::std::option::Option<bool>,
+ // @@protoc_insertion_point(field:hw.trezor.messages.stellar.StellarSCVal.u32)
+ pub u32: ::std::option::Option<u32>,
+ // @@protoc_insertion_point(field:hw.trezor.messages.stellar.StellarSCVal.i32)
+ pub i32: ::std::option::Option<i32>,
+ // @@protoc_insertion_point(field:hw.trezor.messages.stellar.StellarSCVal.u64)
+ pub u64: ::std::option::Option<u64>,
+ // @@protoc_insertion_point(field:hw.trezor.messages.stellar.StellarSCVal.i64)
+ pub i64: ::std::option::Option<i64>,
+ // @@protoc_insertion_point(field:hw.trezor.messages.stellar.StellarSCVal.timepoint)
+ pub timepoint: ::std::option::Option<u64>,
+ // @@protoc_insertion_point(field:hw.trezor.messages.stellar.StellarSCVal.duration)
+ pub duration: ::std::option::Option<u64>,
+ // @@protoc_insertion_point(field:hw.trezor.messages.stellar.StellarSCVal.u128)
+ pub u128: ::protobuf::MessageField<stellar_scval::StellarUInt128Parts>,
+ // @@protoc_insertion_point(field:hw.trezor.messages.stellar.StellarSCVal.i128)
+ pub i128: ::protobuf::MessageField<stellar_scval::StellarInt128Parts>,
+ // @@protoc_insertion_point(field:hw.trezor.messages.stellar.StellarSCVal.u256)
+ pub u256: ::protobuf::MessageField<stellar_scval::StellarUInt256Parts>,
+ // @@protoc_insertion_point(field:hw.trezor.messages.stellar.StellarSCVal.i256)
+ pub i256: ::protobuf::MessageField<stellar_scval::StellarInt256Parts>,
+ // @@protoc_insertion_point(field:hw.trezor.messages.stellar.StellarSCVal.bytes)
+ pub bytes: ::std::option::Option<::std::vec::Vec<u8>>,
+ // @@protoc_insertion_point(field:hw.trezor.messages.stellar.StellarSCVal.string)
+ pub string: ::std::option::Option<::std::vec::Vec<u8>>,
+ // @@protoc_insertion_point(field:hw.trezor.messages.stellar.StellarSCVal.symbol)
+ pub symbol: ::std::option::Option<::std::string::String>,
+ // @@protoc_insertion_point(field:hw.trezor.messages.stellar.StellarSCVal.vec)
+ pub vec: ::std::vec::Vec<StellarSCVal>,
+ // @@protoc_insertion_point(field:hw.trezor.messages.stellar.StellarSCVal.map)
+ pub map: ::std::vec::Vec<stellar_scval::StellarSCValMapEntry>,
+ // @@protoc_insertion_point(field:hw.trezor.messages.stellar.StellarSCVal.address)
+ pub address: ::std::option::Option<::std::string::String>,
+ // special fields
+ // @@protoc_insertion_point(special_field:hw.trezor.messages.stellar.StellarSCVal.special_fields)
+ pub special_fields: ::protobuf::SpecialFields,
+}
+
+impl<'a> ::std::default::Default for &'a StellarSCVal {
+ fn default() -> &'a StellarSCVal {
+ <StellarSCVal as ::protobuf::Message>::default_instance()
+ }
+}
+
+impl StellarSCVal {
+ pub fn new() -> StellarSCVal {
+ ::std::default::Default::default()
+ }
+
+ // required .hw.trezor.messages.stellar.StellarSCVal.StellarSCValType type = 1;
+
+ pub fn type_(&self) -> stellar_scval::StellarSCValType {
+ match self.type_ {
+ Some(e) => e.enum_value_or(stellar_scval::StellarSCValType::SCV_BOOL),
+ None => stellar_scval::StellarSCValType::SCV_BOOL,
+ }
+ }
+
+ pub fn clear_type_(&mut self) {
+ self.type_ = ::std::option::Option::None;
+ }
+
+ pub fn has_type(&self) -> bool {
+ self.type_.is_some()
+ }
+
+ // Param is passed by value, moved
+ pub fn set_type(&mut self, v: stellar_scval::StellarSCValType) {
+ self.type_ = ::std::option::Option::Some(::protobuf::EnumOrUnknown::new(v));
+ }
+
+ // optional bool b = 2;
+
+ pub fn b(&self) -> bool {
+ self.b.unwrap_or(false)
+ }
+
+ pub fn clear_b(&mut self) {
+ self.b = ::std::option::Option::None;
+ }
+
+ pub fn has_b(&self) -> bool {
+ self.b.is_some()
+ }
+
+ // Param is passed by value, moved
+ pub fn set_b(&mut self, v: bool) {
+ self.b = ::std::option::Option::Some(v);
+ }
+
+ // optional uint32 u32 = 4;
+
+ pub fn u32(&self) -> u32 {
+ self.u32.unwrap_or(0)
+ }
+
+ pub fn clear_u32(&mut self) {
+ self.u32 = ::std::option::Option::None;
+ }
+
+ pub fn has_u32(&self) -> bool {
+ self.u32.is_some()
+ }
+
+ // Param is passed by value, moved
+ pub fn set_u32(&mut self, v: u32) {
+ self.u32 = ::std::option::Option::Some(v);
+ }
+
+ // optional sint32 i32 = 5;
+
+ pub fn i32(&self) -> i32 {
+ self.i32.unwrap_or(0)
+ }
+
+ pub fn clear_i32(&mut self) {
+ self.i32 = ::std::option::Option::None;
+ }
+
+ pub fn has_i32(&self) -> bool {
+ self.i32.is_some()
+ }
+
+ // Param is passed by value, moved
+ pub fn set_i32(&mut self, v: i32) {
+ self.i32 = ::std::option::Option::Some(v);
+ }
+
+ // optional uint64 u64 = 6;
+
+ pub fn u64(&self) -> u64 {
+ self.u64.unwrap_or(0)
+ }
+
+ pub fn clear_u64(&mut self) {
+ self.u64 = ::std::option::Option::None;
+ }
+
+ pub fn has_u64(&self) -> bool {
+ self.u64.is_some()
+ }
+
+ // Param is passed by value, moved
+ pub fn set_u64(&mut self, v: u64) {
+ self.u64 = ::std::option::Option::Some(v);
+ }
+
+ // optional sint64 i64 = 7;
+
+ pub fn i64(&self) -> i64 {
+ self.i64.unwrap_or(0)
+ }
+
+ pub fn clear_i64(&mut self) {
+ self.i64 = ::std::option::Option::None;
+ }
+
+ pub fn has_i64(&self) -> bool {
+ self.i64.is_some()
+ }
+
+ // Param is passed by value, moved
+ pub fn set_i64(&mut self, v: i64) {
+ self.i64 = ::std::option::Option::Some(v);
+ }
+
+ // optional uint64 timepoint = 8;
+
+ pub fn timepoint(&self) -> u64 {
+ self.timepoint.unwrap_or(0)
+ }
+
+ pub fn clear_timepoint(&mut self) {
+ self.timepoint = ::std::option::Option::None;
+ }
+
+ pub fn has_timepoint(&self) -> bool {
+ self.timepoint.is_some()
+ }
+
+ // Param is passed by value, moved
+ pub fn set_timepoint(&mut self, v: u64) {
+ self.timepoint = ::std::option::Option::Some(v);
+ }
+
+ // optional uint64 duration = 9;
+
+ pub fn duration(&self) -> u64 {
+ self.duration.unwrap_or(0)
+ }
+
+ pub fn clear_duration(&mut self) {
+ self.duration = ::std::option::Option::None;
+ }
+
+ pub fn has_duration(&self) -> bool {
+ self.duration.is_some()
+ }
+
+ // Param is passed by value, moved
+ pub fn set_duration(&mut self, v: u64) {
+ self.duration = ::std::option::Option::Some(v);
+ }
+
+ // optional bytes bytes = 14;
+
+ pub fn bytes(&self) -> &[u8] {
+ match self.bytes.as_ref() {
+ Some(v) => v,
+ None => &[],
+ }
+ }
+
+ pub fn clear_bytes(&mut self) {
+ self.bytes = ::std::option::Option::None;
+ }
+
+ pub fn has_bytes(&self) -> bool {
+ self.bytes.is_some()
+ }
+
+ // Param is passed by value, moved
+ pub fn set_bytes(&mut self, v: ::std::vec::Vec<u8>) {
+ self.bytes = ::std::option::Option::Some(v);
+ }
+
+ // Mutable pointer to the field.
+ // If field is not initialized, it is initialized with default value first.
+ pub fn mut_bytes(&mut self) -> &mut ::std::vec::Vec<u8> {
+ if self.bytes.is_none() {
+ self.bytes = ::std::option::Option::Some(::std::vec::Vec::new());
+ }
+ self.bytes.as_mut().unwrap()
+ }
+
+ // Take field
+ pub fn take_bytes(&mut self) -> ::std::vec::Vec<u8> {
+ self.bytes.take().unwrap_or_else(|| ::std::vec::Vec::new())
+ }
+
+ // optional bytes string = 15;
+
+ pub fn string(&self) -> &[u8] {
+ match self.string.as_ref() {
+ Some(v) => v,
+ None => &[],
+ }
+ }
+
+ pub fn clear_string(&mut self) {
+ self.string = ::std::option::Option::None;
+ }
+
+ pub fn has_string(&self) -> bool {
+ self.string.is_some()
+ }
+
+ // Param is passed by value, moved
+ pub fn set_string(&mut self, v: ::std::vec::Vec<u8>) {
+ self.string = ::std::option::Option::Some(v);
+ }
+
+ // Mutable pointer to the field.
+ // If field is not initialized, it is initialized with default value first.
+ pub fn mut_string(&mut self) -> &mut ::std::vec::Vec<u8> {
+ if self.string.is_none() {
+ self.string = ::std::option::Option::Some(::std::vec::Vec::new());
+ }
+ self.string.as_mut().unwrap()
+ }
+
+ // Take field
+ pub fn take_string(&mut self) -> ::std::vec::Vec<u8> {
+ self.string.take().unwrap_or_else(|| ::std::vec::Vec::new())
+ }
+
+ // optional string symbol = 16;
+
+ pub fn symbol(&self) -> &str {
+ match self.symbol.as_ref() {
+ Some(v) => v,
+ None => "",
+ }
+ }
+
+ pub fn clear_symbol(&mut self) {
+ self.symbol = ::std::option::Option::None;
+ }
+
+ pub fn has_symbol(&self) -> bool {
+ self.symbol.is_some()
+ }
+
+ // Param is passed by value, moved
+ pub fn set_symbol(&mut self, v: ::std::string::String) {
+ self.symbol = ::std::option::Option::Some(v);
+ }
+
+ // Mutable pointer to the field.
+ // If field is not initialized, it is initialized with default value first.
+ pub fn mut_symbol(&mut self) -> &mut ::std::string::String {
+ if self.symbol.is_none() {
+ self.symbol = ::std::option::Option::Some(::std::string::String::new());
+ }
+ self.symbol.as_mut().unwrap()
+ }
+
+ // Take field
+ pub fn take_symbol(&mut self) -> ::std::string::String {
+ self.symbol.take().unwrap_or_else(|| ::std::string::String::new())
+ }
+
+ // optional string address = 19;
+
+ pub fn address(&self) -> &str {
+ match self.address.as_ref() {
+ Some(v) => v,
+ None => "",
+ }
+ }
+
+ pub fn clear_address(&mut self) {
+ self.address = ::std::option::Option::None;
+ }
+
+ pub fn has_address(&self) -> bool {
+ self.address.is_some()
+ }
+
+ // Param is passed by value, moved
+ pub fn set_address(&mut self, v: ::std::string::String) {
+ self.address = ::std::option::Option::Some(v);
+ }
+
+ // Mutable pointer to the field.
+ // If field is not initialized, it is initialized with default value first.
+ pub fn mut_address(&mut self) -> &mut ::std::string::String {
+ if self.address.is_none() {
+ self.address = ::std::option::Option::Some(::std::string::String::new());
+ }
+ self.address.as_mut().unwrap()
+ }
+
+ // Take field
+ pub fn take_address(&mut self) -> ::std::string::String {
+ self.address.take().unwrap_or_else(|| ::std::string::String::new())
+ }
+
+ fn generated_message_descriptor_data() -> ::protobuf::reflect::GeneratedMessageDescriptorData {
+ let mut fields = ::std::vec::Vec::with_capacity(18);
+ let mut oneofs = ::std::vec::Vec::with_capacity(0);
+ fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>(
+ "type",
+ |m: &StellarSCVal| { &m.type_ },
+ |m: &mut StellarSCVal| { &mut m.type_ },
+ ));
+ fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>(
+ "b",
+ |m: &StellarSCVal| { &m.b },
+ |m: &mut StellarSCVal| { &mut m.b },
+ ));
+ fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>(
+ "u32",
+ |m: &StellarSCVal| { &m.u32 },
+ |m: &mut StellarSCVal| { &mut m.u32 },
+ ));
+ fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>(
+ "i32",
+ |m: &StellarSCVal| { &m.i32 },
+ |m: &mut StellarSCVal| { &mut m.i32 },
+ ));
+ fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>(
+ "u64",
+ |m: &StellarSCVal| { &m.u64 },
+ |m: &mut StellarSCVal| { &mut m.u64 },
+ ));
+ fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>(
+ "i64",
+ |m: &StellarSCVal| { &m.i64 },
+ |m: &mut StellarSCVal| { &mut m.i64 },
+ ));
+ fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>(
+ "timepoint",
+ |m: &StellarSCVal| { &m.timepoint },
+ |m: &mut StellarSCVal| { &mut m.timepoint },
+ ));
+ fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>(
+ "duration",
+ |m: &StellarSCVal| { &m.duration },
+ |m: &mut StellarSCVal| { &mut m.duration },
+ ));
+ fields.push(::protobuf::reflect::rt::v2::make_message_field_accessor::<_, stellar_scval::StellarUInt128Parts>(
+ "u128",
+ |m: &StellarSCVal| { &m.u128 },
+ |m: &mut StellarSCVal| { &mut m.u128 },
+ ));
+ fields.push(::protobuf::reflect::rt::v2::make_message_field_accessor::<_, stellar_scval::StellarInt128Parts>(
+ "i128",
+ |m: &StellarSCVal| { &m.i128 },
+ |m: &mut StellarSCVal| { &mut m.i128 },
+ ));
+ fields.push(::protobuf::reflect::rt::v2::make_message_field_accessor::<_, stellar_scval::StellarUInt256Parts>(
+ "u256",
+ |m: &StellarSCVal| { &m.u256 },
+ |m: &mut StellarSCVal| { &mut m.u256 },
+ ));
+ fields.push(::protobuf::reflect::rt::v2::make_message_field_accessor::<_, stellar_scval::StellarInt256Parts>(
+ "i256",
+ |m: &StellarSCVal| { &m.i256 },
+ |m: &mut StellarSCVal| { &mut m.i256 },
+ ));
+ fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>(
+ "bytes",
+ |m: &StellarSCVal| { &m.bytes },
+ |m: &mut StellarSCVal| { &mut m.bytes },
+ ));
+ fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>(
+ "string",
+ |m: &StellarSCVal| { &m.string },
+ |m: &mut StellarSCVal| { &mut m.string },
+ ));
+ fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>(
+ "symbol",
+ |m: &StellarSCVal| { &m.symbol },
+ |m: &mut StellarSCVal| { &mut m.symbol },
+ ));
+ fields.push(::protobuf::reflect::rt::v2::make_vec_simpler_accessor::<_, _>(
+ "vec",
+ |m: &StellarSCVal| { &m.vec },
+ |m: &mut StellarSCVal| { &mut m.vec },
+ ));
+ fields.push(::protobuf::reflect::rt::v2::make_vec_simpler_accessor::<_, _>(
+ "map",
+ |m: &StellarSCVal| { &m.map },
+ |m: &mut StellarSCVal| { &mut m.map },
+ ));
+ fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>(
+ "address",
+ |m: &StellarSCVal| { &m.address },
+ |m: &mut StellarSCVal| { &mut m.address },
+ ));
+ ::protobuf::reflect::GeneratedMessageDescriptorData::new_2::<StellarSCVal>(
+ "StellarSCVal",
+ fields,
+ oneofs,
+ )
+ }
+}
+
+impl ::protobuf::Message for StellarSCVal {
+ const NAME: &'static str = "StellarSCVal";
+
+ fn is_initialized(&self) -> bool {
+ if self.type_.is_none() {
+ return false;
+ }
+ for v in &self.u128 {
+ if !v.is_initialized() {
+ return false;
+ }
+ };
+ for v in &self.i128 {
+ if !v.is_initialized() {
+ return false;
+ }
+ };
+ for v in &self.u256 {
+ if !v.is_initialized() {
+ return false;
+ }
+ };
+ for v in &self.i256 {
+ if !v.is_initialized() {
+ return false;
+ }
+ };
+ for v in &self.vec {
+ if !v.is_initialized() {
+ return false;
+ }
+ };
+ for v in &self.map {
+ if !v.is_initialized() {
+ return false;
+ }
+ };
+ true
+ }
+
+ fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::Result<()> {
+ while let Some(tag) = is.read_raw_tag_or_eof()? {
+ match tag {
+ 8 => {
+ self.type_ = ::std::option::Option::Some(is.read_enum_or_unknown()?);
+ },
+ 16 => {
+ self.b = ::std::option::Option::Some(is.read_bool()?);
+ },
+ 32 => {
+ self.u32 = ::std::option::Option::Some(is.read_uint32()?);
+ },
+ 40 => {
+ self.i32 = ::std::option::Option::Some(is.read_sint32()?);
+ },
+ 48 => {
+ self.u64 = ::std::option::Option::Some(is.read_uint64()?);
+ },
+ 56 => {
+ self.i64 = ::std::option::Option::Some(is.read_sint64()?);
+ },
+ 64 => {
+ self.timepoint = ::std::option::Option::Some(is.read_uint64()?);
+ },
+ 72 => {
+ self.duration = ::std::option::Option::Some(is.read_uint64()?);
+ },
+ 82 => {
+ ::protobuf::rt::read_singular_message_into_field(is, &mut self.u128)?;
+ },
+ 90 => {
+ ::protobuf::rt::read_singular_message_into_field(is, &mut self.i128)?;
+ },
+ 98 => {
+ ::protobuf::rt::read_singular_message_into_field(is, &mut self.u256)?;
+ },
+ 106 => {
+ ::protobuf::rt::read_singular_message_into_field(is, &mut self.i256)?;
+ },
+ 114 => {
+ self.bytes = ::std::option::Option::Some(is.read_bytes()?);
+ },
+ 122 => {
+ self.string = ::std::option::Option::Some(is.read_bytes()?);
+ },
+ 130 => {
+ self.symbol = ::std::option::Option::Some(is.read_string()?);
+ },
+ 138 => {
+ self.vec.push(is.read_message()?);
+ },
+ 146 => {
+ self.map.push(is.read_message()?);
+ },
+ 154 => {
+ self.address = ::std::option::Option::Some(is.read_string()?);
+ },
+ tag => {
+ ::protobuf::rt::read_unknown_or_skip_group(tag, is, self.special_fields.mut_unknown_fields())?;
+ },
+ };
+ }
+ ::std::result::Result::Ok(())
+ }
+
+ // Compute sizes of nested messages
+ #[allow(unused_variables)]
+ fn compute_size(&self) -> u64 {
+ let mut my_size = 0;
+ if let Some(v) = self.type_ {
+ my_size += ::protobuf::rt::int32_size(1, v.value());
+ }
+ if let Some(v) = self.b {
+ my_size += 1 + 1;
+ }
+ if let Some(v) = self.u32 {
+ my_size += ::protobuf::rt::uint32_size(4, v);
+ }
+ if let Some(v) = self.i32 {
+ my_size += ::protobuf::rt::sint32_size(5, v);
+ }
+ if let Some(v) = self.u64 {
+ my_size += ::protobuf::rt::uint64_size(6, v);
+ }
+ if let Some(v) = self.i64 {
+ my_size += ::protobuf::rt::sint64_size(7, v);
+ }
+ if let Some(v) = self.timepoint {
+ my_size += ::protobuf::rt::uint64_size(8, v);
+ }
+ if let Some(v) = self.duration {
+ my_size += ::protobuf::rt::uint64_size(9, v);
+ }
+ if let Some(v) = self.u128.as_ref() {
+ let len = v.compute_size();
+ my_size += 1 + ::protobuf::rt::compute_raw_varint64_size(len) + len;
+ }
+ if let Some(v) = self.i128.as_ref() {
+ let len = v.compute_size();
+ my_size += 1 + ::protobuf::rt::compute_raw_varint64_size(len) + len;
+ }
+ if let Some(v) = self.u256.as_ref() {
+ let len = v.compute_size();
+ my_size += 1 + ::protobuf::rt::compute_raw_varint64_size(len) + len;
+ }
+ if let Some(v) = self.i256.as_ref() {
+ let len = v.compute_size();
+ my_size += 1 + ::protobuf::rt::compute_raw_varint64_size(len) + len;
+ }
+ if let Some(v) = self.bytes.as_ref() {
+ my_size += ::protobuf::rt::bytes_size(14, &v);
+ }
+ if let Some(v) = self.string.as_ref() {
+ my_size += ::protobuf::rt::bytes_size(15, &v);
+ }
+ if let Some(v) = self.symbol.as_ref() {
+ my_size += ::protobuf::rt::string_size(16, &v);
+ }
+ for value in &self.vec {
+ let len = value.compute_size();
+ my_size += 2 + ::protobuf::rt::compute_raw_varint64_size(len) + len;
+ };
+ for value in &self.map {
+ let len = value.compute_size();
+ my_size += 2 + ::protobuf::rt::compute_raw_varint64_size(len) + len;
+ };
+ if let Some(v) = self.address.as_ref() {
+ my_size += ::protobuf::rt::string_size(19, &v);
+ }
+ my_size += ::protobuf::rt::unknown_fields_size(self.special_fields.unknown_fields());
+ self.special_fields.cached_size().set(my_size as u32);
+ my_size
+ }
+
+ fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::Result<()> {
+ if let Some(v) = self.type_ {
+ os.write_enum(1, ::protobuf::EnumOrUnknown::value(&v))?;
+ }
+ if let Some(v) = self.b {
+ os.write_bool(2, v)?;
+ }
+ if let Some(v) = self.u32 {
+ os.write_uint32(4, v)?;
+ }
+ if let Some(v) = self.i32 {
+ os.write_sint32(5, v)?;
+ }
+ if let Some(v) = self.u64 {
+ os.write_uint64(6, v)?;
+ }
+ if let Some(v) Why this scored 36/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.