feat: harden Stellar payment, trustline, and path-payment safety (#948 #949 #950 #951) - #1019
Merged
llinsss merged 1 commit intoAug 28, 2026
Conversation
Closes DogStark#948: validate Stellar destination addresses (StrKey ed25519/muxed) and enforce SEP-0029 memo-required accounts before building a payment. Closes DogStark#949: refresh the source account sequence number immediately before signing and rebuild once on tx_bad_seq so concurrent devices or queued payments cannot submit stale transactions. Closes DogStark#950: explain trustline reserve impact and block trustline removal when a balance or buying/selling liabilities would be stranded. Closes DogStark#951: bind a reviewed path-payment quote to its min destination amount, deadline, routing path, and assets, and reject any drifted or expired quote at submission time. Adds focused unit tests for every new guard.
|
@maztah1 Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits. You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes #948
Closes #949
Closes #950
Closes #951
Hardens the Stellar money-movement paths against four classes of unsafe state that can lose or strand funds. Each fix is a small, isolated guard with focused unit tests; no behavioural change on the happy path.
Hardens the Stellar money-movement paths against four classes of unsafe state that can lose or strand funds. Each fix is a small, isolated guard with focused unit tests; no behavioural change on the happy path.
blockchainService.sendPaymentblockchainServicesigningtx_bad_seqtrustlineService/TrustlineScreenstellarPathPaymentService#948 — Validate Stellar destination and memo requirements
src/services/blockchainService.tsvalidateStellarDestination(address)— uses the SDK'sStrKeychecks to accept classicG...(ed25519) and multiplexedM...addresses, trims input, and throwsBlockchainServiceError('INVALID_DESTINATION')for anything else so a mistyped address can never reach transaction building.destinationRequiresMemo(pk, loadAccount?)— implements SEP-0029: returnstruewhen the destination account publishes aconfig.memo_requireddata entry. Muxed addresses are exempt (they carry their own routing id). Unfunded accounts returnfalse; the account loader is injectable for tests.assertDestinationAndMemo(pk, memo?)— combined guard now called at the top ofsendPayment; throwsMEMO_REQUIREDwhen a memo is missing/blank for an opted-in account.#949 — Protect signing against stale account sequence numbers
src/services/blockchainService.tsisBadSequenceError(error)— recognises Horizon'stx_bad_seq/txBadSeqrejection across raw Horizon error shapes,error.code, and the wrappedBlockchainServiceErrormessage.signAndSubmitWithFreshSequence(secret, buildOps, opts?)— loads the source account (and therefore its sequence) immediately before signing, and rebuilds once with a fresh sequence if Horizon still returnstx_bad_seq. This is idempotent: atx_bad_seqtransaction is provably not in the ledger, so re-applying the same operation cannot double-spend.sendPaymentnow routes through this helper.#950 — Trustline reserve and liability checks
src/services/trustlineService.ts,src/screens/TrustlineScreen.tsx,src/models/Trustline.tsdescribeReserveImpact(state, 'add' | 'remove')— returns the exact±0.5 XLMreserve delta, the projected locked/available balance, asufficientflag, and a human-readable summary for the confirmation dialog.canAffordNewTrustline(state)— boundary-correct check that the account can cover the additional reserve.assertTrustlineRemovable(line)— blocks removal withNON_ZERO_BALANCEwhen a balance remains, and withHAS_LIABILITIESwhenselling_liabilities/buying_liabilities(open offers) would be stranded. Wired intoremoveTrustlineand the screen's remove flow.parseBalancenow surfacessellingLiabilities/buyingLiabilitiesonTrustlineAssetso the UI can guard before hitting the network.TrustlineScreennow shows the reserve-impact summary in both the add and remove dialogs and blocks the add action when the account is short.#951 — Path-payment slippage and expiry
src/services/stellarPathPaymentService.tscomputeMinDestinationAmount(amount, slippageBps = 50)— 7-dp floor on delivered amount; validates amount and a 0–10000 bps integer tolerance.assertQuoteNotExpired(quote, now?)— rejects missing/malformed expiry (QUOTE_NO_EXPIRY) and past deadlines (QUOTE_EXPIRED).bindReviewedQuote(quote, slippageBps?)— freezes the reviewed quote into an immutableQuoteBinding(min amount, deadline, path, path count, both assets).assertQuoteMatchesBinding(binding, freshQuote, now?)— rejectsASSET_DRIFT,PATH_DRIFT,SLIPPAGE_EXCEEDED, and expiry when a re-quote no longer matches what the user approved.submitPathPaymentaccepts an optionalreviewedQuote(+freshQuote,slippageBps); when supplied it validates before submitting and forwardsminDestinationAmount+deadlineto the backend. The wire payload is unchanged when these are omitted.Tests
src/services/__tests__/blockchainService.validation.test.ts— destination validation (valid/empty/malformed), SEP-0029 memo detection (opted-in, not, unfunded, offline/timeout propagation),assertDestinationAndMemosuccess/blank-memo/no-memo-required/invalid-before-lookup,isBadSequenceErroracross all shapes.src/services/__tests__/stellarPathPaymentService.slippage.test.ts— min-amount math and validation, expiry, quote binding, and drift detection (slippage, asset, path, hop count, expiry).src/services/__tests__/trustlineService.reserve.test.ts— reserve delta and boundary (exactly-enough) for add/remove, non-negative projected reserve, removal blocked on balance and on buy/sell liabilities (snake_case and camelCase).Acceptance criteria notes
TrustlineScreen; no native or platform-specific code.tx_bad_seqrebuild), and cancellation (guards run before submission) are exercised.tx_bad_seq.