diff --git a/common/protob/messages-stellar.proto b/common/protob/messages-stellar.proto index f31df2f03..21ca7e8a7 100644 --- a/common/protob/messages-stellar.proto +++ b/common/protob/messages-stellar.proto @@ -76,6 +76,7 @@ message StellarSignTx { * @next StellarCreateAccountOp * @next StellarPathPaymentStrictReceiveOp * @next StellarManageSellOfferOp + * @next StellarManageBuyOfferOp * @next StellarCreatePassiveOfferOp * @next StellarSetOptionsOp * @next StellarChangeTrustOp @@ -140,6 +141,21 @@ message StellarManageSellOfferOp { required uint64 offer_id = 7; // Offer ID for updating an existing offer } +/** + * Request: ask device to confirm this operation type + * @next StellarTxOpRequest + * @next StellarSignedTx + */ +message StellarManageBuyOfferOp { + optional string source_account = 1; // (optional) source account address + required StellarAsset selling_asset = 2; + required StellarAsset buying_asset = 3; + required sint64 amount = 4; + required uint32 price_n = 5; // Price numerator + required uint32 price_d = 6; // Price denominator + required uint64 offer_id = 7; // Offer ID for updating an existing offer +} + /** * Request: ask device to confirm this operation type * @next StellarTxOpRequest diff --git a/common/protob/messages.proto b/common/protob/messages.proto index 88bce3404..935d8e43a 100644 --- a/common/protob/messages.proto +++ b/common/protob/messages.proto @@ -239,6 +239,7 @@ enum MessageType { // omitted: StellarInflationOp is not a supported operation, would be 219 MessageType_StellarManageDataOp = 220 [(wire_in) = true]; MessageType_StellarBumpSequenceOp = 221 [(wire_in) = true]; + MessageType_StellarManageBuyOfferOp = 222 [(wire_in) = true]; MessageType_StellarSignedTx = 230 [(wire_out) = true]; // Cardano diff --git a/common/tests/fixtures/stellar/sign_tx.json b/common/tests/fixtures/stellar/sign_tx.json index 0f01048ed..455db179b 100644 --- a/common/tests/fixtures/stellar/sign_tx.json +++ b/common/tests/fixtures/stellar/sign_tx.json @@ -335,6 +335,45 @@ "signature": "y9xb8IgPpkjgFa87I9alTD0mVc6EUcJrD7erZVPVGLdDs7rjh7fVtLAJS7iin85Yle0AwnqqEADYAjVzHzz7Bg==" } }, + { + "name": "StellarManageBuyOfferOp", + "parameters": { + "xdr": "AAAAAgAAAAAvIrnGLwi3dPPr5t1ufbk8PsLL3gJ5Vho9nFIluMMikgAAAGQAAAAAAAAD6AAAAAEAAAAAG4J3zQAAAABd5CqEAAAAAAAAAAEAAAAAAAAADAAAAAJBQkNERUZHSElKS0wAAAAAKYSWAsIOWDZfEjwS2HocpFUNEM0hsK4OGNROPlb9ahUAAAABWAAAAAAAAABwi6oxX35cFm2EtGS/s4/WJXj+OtJyJ+dsy7ehecRRIQAAAAAdzxaYAAAAAwAAAAQAAAAAAAAFOQAAAAAAAAAA", + "address_n": "m/44'/148'/0'", + "network_passphrase": "Test SDF Network ; September 2015", + "tx": { + "source_account": "GAXSFOOGF4ELO5HT5PTN23T5XE6D5QWL3YBHSVQ2HWOFEJNYYMRJENBV", + "fee": 100, + "sequence_number": 1000, + "timebounds_start": 461535181, + "timebounds_end": 1575234180, + "memo_type": "NONE" + }, + "operations": [ + { + "_message_type": "StellarManageBuyOfferOp", + "selling_asset": { + "type": "ALPHANUM12", + "code": "ABCDEFGHIJKL", + "issuer": "GAUYJFQCYIHFQNS7CI6BFWD2DSSFKDIQZUQ3BLQODDKE4PSW7VVBKENC" + }, + "buying_asset": { + "type": "ALPHANUM4", + "code": "X", + "issuer": "GBYIXKRRL57FYFTNQS2GJP5TR7LCK6H6HLJHEJ7HNTF3PILZYRISDLNQ" + }, + "amount": 500111000, + "price_n": 3, + "price_d": 4, + "offer_id": 1337 + } + ] + }, + "result": { + "public_key": "2f22b9c62f08b774f3ebe6dd6e7db93c3ec2cbde0279561a3d9c5225b8c32292", + "signature": "sDQuKEm7j6Lsuw+QUTrotSloZiF+8LrXsuoLCBadewWpArO8+qmMgonrG3bJfaZ4dYdD8WcpfP5LNLOfU+lDBA==" + } + }, { "name": "StellarManageSellOfferOp", "parameters": { diff --git a/core/.changelog.d/1838.added b/core/.changelog.d/1838.added new file mode 100644 index 000000000..657fef175 --- /dev/null +++ b/core/.changelog.d/1838.added @@ -0,0 +1 @@ +Stellar: add support for StellarManageBuyOfferOp. diff --git a/core/src/apps/stellar/README.md b/core/src/apps/stellar/README.md index eb5acdb9a..e0ed21173 100644 --- a/core/src/apps/stellar/README.md +++ b/core/src/apps/stellar/README.md @@ -25,6 +25,7 @@ Stellar transaction is composed of one or more operations. We support all [opera - Create Account - Create Passive Offer - Manage Data +- Manage Buy Offer - Manage Sell Offer - Path Payment Strict Receive - Payment diff --git a/core/src/apps/stellar/consts.py b/core/src/apps/stellar/consts.py index f2b7ddf54..7028db6d9 100644 --- a/core/src/apps/stellar/consts.py +++ b/core/src/apps/stellar/consts.py @@ -14,6 +14,7 @@ if False: StellarCreateAccountOp, StellarCreatePassiveOfferOp, StellarManageDataOp, + StellarManageBuyOfferOp, StellarManageSellOfferOp, StellarPathPaymentStrictReceiveOp, StellarPaymentOp, @@ -28,6 +29,7 @@ if False: StellarCreateAccountOp, StellarCreatePassiveOfferOp, StellarManageDataOp, + StellarManageBuyOfferOp, StellarManageSellOfferOp, StellarPathPaymentStrictReceiveOp, StellarPaymentOp, @@ -37,7 +39,7 @@ if False: TX_TYPE = b"\x00\x00\x00\x02" -# source: https://github.com/stellar/go/blob/3d2c1defe73dbfed00146ebe0e8d7e07ce4bb1b6/xdr/Stellar-transaction.x#L16 +# source: https://github.com/stellar/go/blob/a1db2a6b1f/xdr/Stellar-transaction.x#L35 # Inflation not supported see https://github.com/trezor/trezor-core/issues/202#issuecomment-393342089 op_codes: dict[int, int] = { MessageType.StellarAccountMergeOp: 8, @@ -47,6 +49,7 @@ op_codes: dict[int, int] = { MessageType.StellarCreateAccountOp: 0, MessageType.StellarCreatePassiveOfferOp: 4, MessageType.StellarManageDataOp: 10, + MessageType.StellarManageBuyOfferOp: 12, MessageType.StellarManageSellOfferOp: 3, MessageType.StellarPathPaymentStrictReceiveOp: 2, MessageType.StellarPaymentOp: 1, @@ -61,6 +64,7 @@ op_wire_types = [ MessageType.StellarCreateAccountOp, MessageType.StellarCreatePassiveOfferOp, MessageType.StellarManageDataOp, + MessageType.StellarManageBuyOfferOp, MessageType.StellarManageSellOfferOp, MessageType.StellarPathPaymentStrictReceiveOp, MessageType.StellarPaymentOp, diff --git a/core/src/apps/stellar/operations/__init__.py b/core/src/apps/stellar/operations/__init__.py index 57a3f7e85..058e5ae30 100644 --- a/core/src/apps/stellar/operations/__init__.py +++ b/core/src/apps/stellar/operations/__init__.py @@ -34,6 +34,9 @@ async def process_operation( elif serialize.StellarManageDataOp.is_type_of(op): await layout.confirm_manage_data_op(ctx, op) serialize.write_manage_data_op(w, op) + elif serialize.StellarManageBuyOfferOp.is_type_of(op): + await layout.confirm_manage_buy_offer_op(ctx, op) + serialize.write_manage_buy_offer_op(w, op) elif serialize.StellarManageSellOfferOp.is_type_of(op): await layout.confirm_manage_sell_offer_op(ctx, op) serialize.write_manage_sell_offer_op(w, op) diff --git a/core/src/apps/stellar/operations/layout.py b/core/src/apps/stellar/operations/layout.py index 9a4fc3aeb..e5e3e8596 100644 --- a/core/src/apps/stellar/operations/layout.py +++ b/core/src/apps/stellar/operations/layout.py @@ -7,6 +7,7 @@ from trezor.messages import ( StellarChangeTrustOp, StellarCreateAccountOp, StellarCreatePassiveOfferOp, + StellarManageBuyOfferOp, StellarManageDataOp, StellarManageSellOfferOp, StellarPathPaymentStrictReceiveOp, @@ -106,8 +107,20 @@ async def confirm_create_passive_offer_op( await _confirm_offer(ctx, text, op) +async def confirm_manage_buy_offer_op( + ctx: Context, op: StellarManageBuyOfferOp +) -> None: + await _confirm_manage_offer_op_common(ctx, op) + + async def confirm_manage_sell_offer_op( ctx: Context, op: StellarManageSellOfferOp +) -> None: + await _confirm_manage_offer_op_common(ctx, op) + + +async def _confirm_manage_offer_op_common( + ctx: Context, op: StellarManageBuyOfferOp | StellarManageSellOfferOp ) -> None: if op.offer_id == 0: text = "New Offer" @@ -123,21 +136,37 @@ async def confirm_manage_sell_offer_op( async def _confirm_offer( ctx: Context, title: str, - op: StellarCreatePassiveOfferOp | StellarManageSellOfferOp, + op: StellarCreatePassiveOfferOp + | StellarManageSellOfferOp + | StellarManageBuyOfferOp, ) -> None: - await confirm_properties( - ctx, - "op_offer", - title=title, - props=( - ("Selling:", format_amount(op.amount, op.selling_asset)), - ("Buying:", format_asset(op.buying_asset)), - ( - f"Price per {format_asset(op.buying_asset)}:", - str(op.price_n / op.price_d), - ), - ), - ) + if StellarManageBuyOfferOp.is_type_of(op): + buying = ("Buying:", format_amount(op.amount, op.buying_asset)) + selling = ("Selling:", format_asset(op.selling_asset)) + price = ( + f"Price per {format_asset(op.selling_asset)}:", + str(op.price_n / op.price_d), + ) + await confirm_properties( + ctx, + "op_offer", + title=title, + props=(buying, selling, price), + ) + else: + selling = ("Selling:", format_amount(op.amount, op.selling_asset)) + buying = ("Buying:", format_asset(op.buying_asset)) + price = ( + f"Price per {format_asset(op.buying_asset)}:", + str(op.price_n / op.price_d), + ) + await confirm_properties( + ctx, + "op_offer", + title=title, + props=(selling, buying, price), + ) + await confirm_asset_issuer(ctx, op.selling_asset) await confirm_asset_issuer(ctx, op.buying_asset) diff --git a/core/src/apps/stellar/operations/serialize.py b/core/src/apps/stellar/operations/serialize.py index 138a92092..e730958e8 100644 --- a/core/src/apps/stellar/operations/serialize.py +++ b/core/src/apps/stellar/operations/serialize.py @@ -7,6 +7,7 @@ from trezor.messages import ( StellarChangeTrustOp, StellarCreateAccountOp, StellarCreatePassiveOfferOp, + StellarManageBuyOfferOp, StellarManageDataOp, StellarManageSellOfferOp, StellarPathPaymentStrictReceiveOp, @@ -65,10 +66,20 @@ def write_manage_data_op(w: Writer, msg: StellarManageDataOp) -> None: writers.write_string(w, msg.value) +def write_manage_buy_offer_op(w: Writer, msg: StellarManageBuyOfferOp) -> None: + _write_manage_offer_op_common(w, msg) + + def write_manage_sell_offer_op(w: Writer, msg: StellarManageSellOfferOp) -> None: + _write_manage_offer_op_common(w, msg) + + +def _write_manage_offer_op_common( + w: Writer, msg: StellarManageSellOfferOp | StellarManageBuyOfferOp +) -> None: _write_asset(w, msg.selling_asset) _write_asset(w, msg.buying_asset) - writers.write_uint64(w, msg.amount) # amount to sell + writers.write_uint64(w, msg.amount) # amount to sell / buy writers.write_uint32(w, msg.price_n) # numerator writers.write_uint32(w, msg.price_d) # denominator writers.write_uint64(w, msg.offer_id) diff --git a/core/src/trezor/enums/MessageType.py b/core/src/trezor/enums/MessageType.py index 76cf11815..da6cba09b 100644 --- a/core/src/trezor/enums/MessageType.py +++ b/core/src/trezor/enums/MessageType.py @@ -130,6 +130,7 @@ if not utils.BITCOIN_ONLY: StellarAccountMergeOp = 218 StellarManageDataOp = 220 StellarBumpSequenceOp = 221 + StellarManageBuyOfferOp = 222 StellarSignedTx = 230 CardanoSignTx = 303 CardanoGetPublicKey = 305 diff --git a/core/src/trezor/enums/__init__.py b/core/src/trezor/enums/__init__.py index 16803018b..37c9ec917 100644 --- a/core/src/trezor/enums/__init__.py +++ b/core/src/trezor/enums/__init__.py @@ -135,6 +135,7 @@ if TYPE_CHECKING: StellarAccountMergeOp = 218 StellarManageDataOp = 220 StellarBumpSequenceOp = 221 + StellarManageBuyOfferOp = 222 StellarSignedTx = 230 CardanoSignTx = 303 CardanoGetPublicKey = 305 diff --git a/core/src/trezor/messages.py b/core/src/trezor/messages.py index 275be7478..c1d357c8a 100644 --- a/core/src/trezor/messages.py +++ b/core/src/trezor/messages.py @@ -4824,6 +4824,32 @@ if TYPE_CHECKING: def is_type_of(cls, msg: protobuf.MessageType) -> TypeGuard["StellarManageSellOfferOp"]: return isinstance(msg, cls) + class StellarManageBuyOfferOp(protobuf.MessageType): + source_account: "str | None" + selling_asset: "StellarAsset" + buying_asset: "StellarAsset" + amount: "int" + price_n: "int" + price_d: "int" + offer_id: "int" + + def __init__( + self, + *, + selling_asset: "StellarAsset", + buying_asset: "StellarAsset", + amount: "int", + price_n: "int", + price_d: "int", + offer_id: "int", + source_account: "str | None" = None, + ) -> None: + pass + + @classmethod + def is_type_of(cls, msg: protobuf.MessageType) -> TypeGuard["StellarManageBuyOfferOp"]: + return isinstance(msg, cls) + class StellarCreatePassiveOfferOp(protobuf.MessageType): source_account: "str | None" selling_asset: "StellarAsset" diff --git a/legacy/firmware/.changelog.d/1838.added b/legacy/firmware/.changelog.d/1838.added new file mode 100644 index 000000000..657fef175 --- /dev/null +++ b/legacy/firmware/.changelog.d/1838.added @@ -0,0 +1 @@ +Stellar: add support for StellarManageBuyOfferOp. diff --git a/legacy/firmware/fsm.h b/legacy/firmware/fsm.h index 6a3bbcdf1..aad610bb0 100644 --- a/legacy/firmware/fsm.h +++ b/legacy/firmware/fsm.h @@ -120,6 +120,7 @@ void fsm_msgStellarPaymentOp(const StellarPaymentOp *msg); void fsm_msgStellarCreateAccountOp(const StellarCreateAccountOp *msg); void fsm_msgStellarPathPaymentStrictReceiveOp( const StellarPathPaymentStrictReceiveOp *msg); +void fsm_msgStellarManageBuyOfferOp(const StellarManageBuyOfferOp *msg); void fsm_msgStellarManageSellOfferOp(const StellarManageSellOfferOp *msg); void fsm_msgStellarCreatePassiveOfferOp(const StellarCreatePassiveOfferOp *msg); void fsm_msgStellarSetOptionsOp(const StellarSetOptionsOp *msg); diff --git a/legacy/firmware/fsm_msg_stellar.h b/legacy/firmware/fsm_msg_stellar.h index 96b0995ec..62c4ba4c2 100644 --- a/legacy/firmware/fsm_msg_stellar.h +++ b/legacy/firmware/fsm_msg_stellar.h @@ -128,6 +128,24 @@ void fsm_msgStellarPathPaymentStrictReceiveOp( } } +void fsm_msgStellarManageBuyOfferOp(const StellarManageBuyOfferOp *msg) { + if (!stellar_confirmManageBuyOfferOp(msg)) return; + + if (stellar_allOperationsConfirmed()) { + RESP_INIT(StellarSignedTx); + + stellar_fillSignedTx(resp); + msg_write(MessageType_MessageType_StellarSignedTx, resp); + layoutHome(); + } + // Request the next operation to sign + else { + RESP_INIT(StellarTxOpRequest); + + msg_write(MessageType_MessageType_StellarTxOpRequest, resp); + } +} + void fsm_msgStellarManageSellOfferOp(const StellarManageSellOfferOp *msg) { if (!stellar_confirmManageSellOfferOp(msg)) return; diff --git a/legacy/firmware/protob/messages-stellar.options b/legacy/firmware/protob/messages-stellar.options index c87ed96dd..61ccb204b 100644 --- a/legacy/firmware/protob/messages-stellar.options +++ b/legacy/firmware/protob/messages-stellar.options @@ -21,6 +21,7 @@ StellarPathPaymentStrictReceiveOp.source_account max_size:57 StellarPathPaymentStrictReceiveOp.destination_account max_size:57 StellarPathPaymentStrictReceiveOp.paths max_count:5 +StellarManageBuyOfferOp.source_account max_size:57 StellarManageSellOfferOp.source_account max_size:57 diff --git a/legacy/firmware/stellar.c b/legacy/firmware/stellar.c index dab375240..4a13e72a4 100644 --- a/legacy/firmware/stellar.c +++ b/legacy/firmware/stellar.c @@ -388,6 +388,93 @@ bool stellar_confirmPathPaymentStrictReceiveOp( return true; } +bool stellar_confirmManageBuyOfferOp(const StellarManageBuyOfferOp *msg) { + if (!stellar_signing) return false; + + if (!stellar_confirmSourceAccount(msg->has_source_account, + msg->source_account)) { + stellar_signingAbort(_("Source account error")); + return false; + } + + // Hash: operation type + stellar_hashupdate_uint32(12); + + // New Offer / Delete #123 / Update #123 + char str_offer[32] = {0}; + if (msg->offer_id == 0) { + strlcpy(str_offer, _("New Offer"), sizeof(str_offer)); + } else { + char str_offer_id[20] = {0}; + stellar_format_uint64(msg->offer_id, str_offer_id, sizeof(str_offer_id)); + + if (msg->amount == 0) { + strlcpy(str_offer, _("Delete #"), sizeof(str_offer)); + } else { + strlcpy(str_offer, _("Update #"), sizeof(str_offer)); + } + + strlcat(str_offer, str_offer_id, sizeof(str_offer)); + } + + char str_buying[32] = {0}; + char str_buying_amount[32] = {0}; + char str_buying_asset[32] = {0}; + + stellar_format_asset(&(msg->buying_asset), str_buying_asset, + sizeof(str_buying_asset)); + stellar_format_stroops(msg->amount, str_buying_amount, + sizeof(str_buying_amount)); + + /* + Buy 200 + XLM (Native Asset) + */ + strlcpy(str_buying, _("Buy "), sizeof(str_buying)); + strlcat(str_buying, str_buying_amount, sizeof(str_buying)); + + char str_selling[32] = {0}; + char str_selling_asset[32] = {0}; + char str_price[32] = {0}; + + stellar_format_asset(&(msg->selling_asset), str_selling_asset, + sizeof(str_selling_asset)); + stellar_format_price(msg->price_n, msg->price_d, str_price, + sizeof(str_price)); + + /* + For 0.675952 Per + USD (G12345678) + */ + strlcpy(str_selling, _("For "), sizeof(str_selling)); + strlcat(str_selling, str_price, sizeof(str_selling)); + strlcat(str_selling, _(" Per"), sizeof(str_selling)); + + stellar_layoutTransactionDialog(str_offer, str_buying, str_buying_asset, + str_selling, str_selling_asset); + if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { + stellar_signingAbort(_("User canceled")); + return false; + } + + // Hash selling asset + stellar_hashupdate_asset(&(msg->selling_asset)); + // buying asset + stellar_hashupdate_asset(&(msg->buying_asset)); + // amount to buy (signed vs. unsigned doesn't matter wrt hashing) + stellar_hashupdate_uint64(msg->amount); + // numerator + stellar_hashupdate_uint32(msg->price_n); + // denominator + stellar_hashupdate_uint32(msg->price_d); + // offer ID + stellar_hashupdate_uint64(msg->offer_id); + + // At this point, the operation is confirmed + stellar_activeTx.confirmed_operations++; + return true; +} + bool stellar_confirmManageSellOfferOp(const StellarManageSellOfferOp *msg) { if (!stellar_signing) return false; diff --git a/legacy/firmware/stellar.h b/legacy/firmware/stellar.h index 0f28878b9..d50c66635 100644 --- a/legacy/firmware/stellar.h +++ b/legacy/firmware/stellar.h @@ -61,6 +61,7 @@ bool stellar_confirmCreateAccountOp(const StellarCreateAccountOp *msg); bool stellar_confirmPaymentOp(const StellarPaymentOp *msg); bool stellar_confirmPathPaymentStrictReceiveOp( const StellarPathPaymentStrictReceiveOp *msg); +bool stellar_confirmManageBuyOfferOp(const StellarManageBuyOfferOp *msg); bool stellar_confirmManageSellOfferOp(const StellarManageSellOfferOp *msg); bool stellar_confirmCreatePassiveOfferOp( const StellarCreatePassiveOfferOp *msg); diff --git a/python/src/trezorlib/messages.py b/python/src/trezorlib/messages.py index 278d43c24..52b7c3bb3 100644 --- a/python/src/trezorlib/messages.py +++ b/python/src/trezorlib/messages.py @@ -156,6 +156,7 @@ class MessageType(IntEnum): StellarAccountMergeOp = 218 StellarManageDataOp = 220 StellarBumpSequenceOp = 221 + StellarManageBuyOfferOp = 222 StellarSignedTx = 230 CardanoSignTx = 303 CardanoGetPublicKey = 305 @@ -6388,6 +6389,38 @@ class StellarManageSellOfferOp(protobuf.MessageType): self.source_account = source_account +class StellarManageBuyOfferOp(protobuf.MessageType): + MESSAGE_WIRE_TYPE = 222 + FIELDS = { + 1: protobuf.Field("source_account", "string", repeated=False, required=False), + 2: protobuf.Field("selling_asset", "StellarAsset", repeated=False, required=True), + 3: protobuf.Field("buying_asset", "StellarAsset", repeated=False, required=True), + 4: protobuf.Field("amount", "sint64", repeated=False, required=True), + 5: protobuf.Field("price_n", "uint32", repeated=False, required=True), + 6: protobuf.Field("price_d", "uint32", repeated=False, required=True), + 7: protobuf.Field("offer_id", "uint64", repeated=False, required=True), + } + + def __init__( + self, + *, + selling_asset: "StellarAsset", + buying_asset: "StellarAsset", + amount: "int", + price_n: "int", + price_d: "int", + offer_id: "int", + source_account: Optional["str"] = None, + ) -> None: + self.selling_asset = selling_asset + self.buying_asset = buying_asset + self.amount = amount + self.price_n = price_n + self.price_d = price_d + self.offer_id = offer_id + self.source_account = source_account + + class StellarCreatePassiveOfferOp(protobuf.MessageType): MESSAGE_WIRE_TYPE = 214 FIELDS = { diff --git a/python/src/trezorlib/stellar.py b/python/src/trezorlib/stellar.py index 8e6814a57..b0f969f8f 100644 --- a/python/src/trezorlib/stellar.py +++ b/python/src/trezorlib/stellar.py @@ -43,6 +43,7 @@ try: TrustLineEntryFlag, Price, Network, + ManageBuyOffer, ) from stellar_sdk.xdr.signer_key_type import SignerKeyType @@ -225,6 +226,17 @@ def _read_operation(op: "Operation"): return messages.StellarBumpSequenceOp( source_account=source_account, bump_to=op.bump_to ) + if isinstance(op, ManageBuyOffer): + price = _read_price(op.price) + return messages.StellarManageBuyOfferOp( + source_account=source_account, + selling_asset=_read_asset(op.selling), + buying_asset=_read_asset(op.buying), + amount=_read_amount(op.amount), + price_n=price.n, + price_d=price.d, + offer_id=op.offer_id, + ) raise ValueError(f"Unknown operation type: {op.__class__.__name__}") diff --git a/python/tests/test_stellar.py b/python/tests/test_stellar.py index 3d9f00204..ebd773a81 100644 --- a/python/tests/test_stellar.py +++ b/python/tests/test_stellar.py @@ -712,3 +712,73 @@ def test_bump_sequence(): assert isinstance(operations[0], messages.StellarBumpSequenceOp) assert operations[0].source_account == operation_source assert operations[0].bump_to == bump_to + + +def test_manage_buy_offer_new_offer(): + tx = make_default_tx() + price = "0.5" + amount = "50.0111" + selling_code = "XLM" + selling_issuer = None + buying_code = "USD" + buying_issuer = "GCSJ7MFIIGIRMAS4R3VT5FIFIAOXNMGDI5HPYTWS5X7HH74FSJ6STSGF" + operation_source = "GAEB4MRKRCONK4J7MVQXAHTNDPAECUCCCNE7YC5CKM34U3OJ673A4D6V" + + envelope = tx.append_manage_buy_offer_op( + selling_code=selling_code, + selling_issuer=selling_issuer, + buying_code=buying_code, + buying_issuer=buying_issuer, + amount=amount, + price=price, + source=operation_source, + ).build() + + tx, operations = stellar.from_envelope(envelope) + assert len(operations) == 1 + assert isinstance(operations[0], messages.StellarManageBuyOfferOp) + assert operations[0].source_account == operation_source + assert operations[0].selling_asset.type == messages.StellarAssetType.NATIVE + assert operations[0].buying_asset.type == messages.StellarAssetType.ALPHANUM4 + assert operations[0].buying_asset.code == buying_code + assert operations[0].buying_asset.issuer == buying_issuer + assert operations[0].amount == 500111000 + assert operations[0].price_n == 1 + assert operations[0].price_d == 2 + assert operations[0].offer_id == 0 # indicates a new offer + + +def test_manage_buy_offer_update_offer(): + tx = make_default_tx() + price = "0.5" + amount = "50.0111" + selling_code = "XLM" + selling_issuer = None + buying_code = "USD" + buying_issuer = "GCSJ7MFIIGIRMAS4R3VT5FIFIAOXNMGDI5HPYTWS5X7HH74FSJ6STSGF" + offer_id = 12345 + operation_source = "GAEB4MRKRCONK4J7MVQXAHTNDPAECUCCCNE7YC5CKM34U3OJ673A4D6V" + + envelope = tx.append_manage_buy_offer_op( + selling_code=selling_code, + selling_issuer=selling_issuer, + buying_code=buying_code, + buying_issuer=buying_issuer, + amount=amount, + price=price, + offer_id=offer_id, + source=operation_source, + ).build() + + tx, operations = stellar.from_envelope(envelope) + assert len(operations) == 1 + assert isinstance(operations[0], messages.StellarManageBuyOfferOp) + assert operations[0].source_account == operation_source + assert operations[0].selling_asset.type == messages.StellarAssetType.NATIVE + assert operations[0].buying_asset.type == messages.StellarAssetType.ALPHANUM4 + assert operations[0].buying_asset.code == buying_code + assert operations[0].buying_asset.issuer == buying_issuer + assert operations[0].amount == 500111000 + assert operations[0].price_n == 1 + assert operations[0].price_d == 2 + assert operations[0].offer_id == offer_id diff --git a/tests/ui_tests/fixtures.json b/tests/ui_tests/fixtures.json index d793b94a8..cd80afe66 100644 --- a/tests/ui_tests/fixtures.json +++ b/tests/ui_tests/fixtures.json @@ -846,6 +846,7 @@ "test_stellar.py::test_sign_tx[StellarCreateAccountOp]": "2582717c25974d2b3ee156624b00375148ff7fd12eeea73625a7c367fa610373", "test_stellar.py::test_sign_tx[StellarCreatePassiveOfferOp]": "6b0f0d2b746f98e2c85006ea7e2d5c49cd9277662e47f223138ff418066791e3", "test_stellar.py::test_sign_tx[StellarManageDataOp]": "8fbec6547a8f9d1f002181db0cbe57fe86abef8d365b1c06fd14292cd0b068a7", +"test_stellar.py::test_sign_tx[StellarManageBuyOfferOp]": "fc57e1ca8b65588aa16cc3524d6dc0f01e094ad5d16a6f7e739a69c101b554bc", "test_stellar.py::test_sign_tx[StellarManageSellOfferOp]": "6ed84765b2ed46711be0ed1219d91c27e927119d352f37b2baf8c6501186bbce", "test_stellar.py::test_sign_tx[StellarPathPaymentStrictReceiveOp]": "58f3bfaece0706bc172d6e6564b728ec0b7f8e2629d8c64dc60672786586076d", "test_stellar.py::test_sign_tx[StellarPaymentOp-asset12]": "1d8e9d5d65420a259f7e2deef1efaf0ce5be966a0f1e5b8e95b832f176f00de2",