Skip to content

[ANCHOR-1341]: Reject SEP-31 quotes priced for a different funding method - #2099

Open
ceciliaromao wants to merge 4 commits into
developfrom
fix/anchor-1341
Open

ceciliaromao wants to merge 4 commits into
developfrom
fix/anchor-1341

Conversation

@ceciliaromao

@ceciliaromao ceciliaromao commented Nov 9, 2026 •

Copy link
Copy Markdown
Collaborator

Description

  • POST /sep31/transactions now rejects, with a 400, a request whose funding_method differs from the buy_delivery_method of the SEP-38 quote it spends (funding_method(X) does not match quote buy delivery method(Y)). A quote with no buy delivery method is not bound to one, as in SEP-6. The check runs before the quote is bound, so a rejected request leaves the quote unused.
  • The deprecated PATCH /transactions validator (TransactionService.validateQuoteAndAmounts) applies the same comparison between the SEP-31 transaction's funding_method and its quote (transaction.funding_method != quote.buy_delivery_method).
  • quick-run/config/assets.yaml.template: USD and CAD list SEPA and SWIFT as SEP-38 sell and buy delivery methods (replacing WIRE), so the SEP-6/SEP-31 and SEP-38 method sets overlap in the stock stack.

Context

  • HackerOne 4051855, duplicated by 4171166
  • SEP-31 never compared the request's funding method with the quote's delivery method, so a quote priced for one rail could be spent on another and the transaction kept the cheaper rail's payout. The SEP-6 exchange paths were fixed in ANCHOR-1296; this applies the same rule to SEP-31 (POST and the deprecated PATCH route). The quick-run template had disjoint method sets, which kept the stock stack from exercising the check.

Testing

  • ./gradlew test
  • Unit: ./gradlew :core:test --tests "org.stellar.anchor.sep31.Sep31ServiceTest" (SEPA quote spent as SWIFT and the mirror are rejected; matching method, and null or empty quote method, are accepted; earlier quote errors still win; a rejection leaves the quote unbound and saves nothing; the comparison is case-sensitive)
  • Unit: ./gradlew :platform:test --tests "org.stellar.anchor.platform.service.TransactionServiceTest" (PATCH rejects a conflicting method, including a case-only difference; accepts equal, null or empty methods and a transaction with no quote)
  • Unit: ./gradlew :core:test --tests "org.stellar.anchor.asset.QuickRunAssetsTemplateTest" (the template loads and its method sets overlap)
  • Manual: with SEP-31 enabled for USD, request a SEP-38 quote with buy_delivery_method=SEPA, then POST /sep31/transactions with that quote_id and funding_method=SWIFT: expect 400. With funding_method=SEPA: expect success.

Documentation

  • N/A: no new feature or configuration option. If the stellar-docs SEP-31 page describes funding_method against the quote, it already says it must match; no change needed.

Known limitations

  • Behavior change: a pre-existing SEP-31 transaction whose stored funding_method conflicts with its quote now gets a 400 on every PATCH until the method is corrected.
  • Behavior change: with two methods listed in the quick-run template, a quote request for USD or CAD must name the delivery method.
  • The integration and e2e suites were not run locally (they need testnet); their SEP-31 quotes carry no delivery method, so the new check treats them as a match.
  • SEP-6 withdraw methods in the quick-run template (bank_account, cash) are unchanged, and SEP-31 stays disabled for the fiat assets there.

@ceciliaromao ceciliaromao self-assigned this Nov 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant