[ANCHOR-1228]: SEP-24 per-asset transaction limit (min/max_amount) bypassable via quote_id with omitted amount #1970
Merged
Merged
Conversation
* update `validateandpopulatequote` to return the `sep38quote` object * add amount validation for quoted `buy_amount` in deposit requests * add amount validation for quoted `sell_amount` in withdraw requests * add unit tests to verify quote amount validation
Contributor
There was a problem hiding this comment.
Pull request overview
This PR closes a SEP-24 validation gap where min_amount/max_amount could be bypassed by providing quote_id while omitting amount, by ensuring the quote’s relevant side is validated against the asset limits and adding targeted unit tests for the omitted-amount path.
Changes:
Sep24Service.withdraw/deposit: validate quote-derived amount against the asset’smin_amount/max_amounton thequote_idpath.Sep24Service.validateAndPopulateQuote: now returns the fetchedSep38Quoteso callers can validate it.Sep24ServiceTest: adds coverage for quote_id + omitted amount (in-bounds and out-of-bounds).
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 7 comments.
| File | Description |
|---|---|
| core/src/main/java/org/stellar/anchor/sep24/Sep24Service.java | Adds min/max validation for quote-derived amounts and returns the quote from validateAndPopulateQuote. |
| core/src/test/kotlin/org/stellar/anchor/sep24/Sep24ServiceTest.kt | Adds tests for quote_id requests that omit amount, including min/max boundary cases. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
amandagonsalves
marked this pull request as draft
July 7, 2026 17:43
* refactor amount limit validation logic into a new private method, `validateamountlimits` * remove duplicated min and max amount validation code from deposit and withdraw flows * update deposit and withdraw methods to use the new `validateamountlimits` method * update sep24service tests to assert specific validation error messages
amandagonsalves
marked this pull request as ready for review
July 13, 2026 13:22
JiahuiWho
approved these changes
Jul 13, 2026
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.
Description
SEP-24
withdraw/depositguarded their asset'smin_amount/max_amountchecks withstrAmount != null(Sep24Service.java). When a client suppliedquote_idand omittedamount- both legal per the SEP-24 spec - those checks were skipped entirely, andvalidateAndPopulateQuotecopied the quote'ssell_amount/buy_amountstraight into the transaction (amountExpected/amountIn/amountOut) with no limit check anywhere in that path: not inExchangeAmountsCalculator.validateQuoteAgainstRequestInfo(only cross-checks the request amount against the quote when the request amount is non-null), and not in SEP-38 quote creation (only validates sign/scale). A user could drive a deposit or withdrawal arbitrarily above or below the operator's configured cap with two ordinary requests. SEP-6's exchange endpoints were never affected - they call the min/max-checkingvalidateAmountunconditionally, before the quote is ever applied.Changes
Sep24Service.validateAndPopulateQuote: now returns theSep38Quoteit fetches instead of discarding it.Sep24Service.withdraw/deposit: on thequoteId != nullbranch, unconditionally validate the relevant side of the quote against the asset's already-knownminAmount/maxAmountviarequestValidator.validateAmount(...)(the same generic method SEP-6 already uses unconditionally). Withdraw checksquote.getSellAmount()(the withdraw asset is the quote's sell side); deposit checksquote.getBuyAmount()(the deposit asset is the quote's buy side). Runs regardless of whetheramountwas also supplied - when both are present, the existing cross-check already ties them together, so this is harmless-redundant there and load-bearing only on the omitted-amountpath.Sep24ServiceTest: added six tests - for each of withdraw/deposit: rejects a quote abovemax_amount, rejects a quote belowmin_amount, and succeeds whenamountis omitted and the quote is legitimately in-bounds (previously uncovered - the existingquote_idtests always included anamountfield).Acceptance Criteria
POST /sep24/transactions/{withdraw,deposit}/interactivewithquote_idand noamount, where the quote's relevant amount is outside the asset'smin_amount/max_amount, is rejected with the same error as the explicit-amount path.Context
#3819378
Testing
./gradlew :core:test --tests "org.stellar.anchor.sep24.Sep24ServiceTest"./gradlew :core:buildDocumentation
N/A
Known limitations
N/A