Repository navigation
Fix: feat(frontend): make quote freshness and currency validation explicit in send flow - #300
Open
euniceamoni wants to merge 1 commit into
Open
euniceamoni wants to merge 1 commit into
euniceamoni wants to merge 1 commit into
Conversation
Co-authored-by: Simple Agent <agent@simple.dev>
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.
Closes #279
Issues
#279 feat(frontend): make quote freshness and currency validation explicit in send flow
Summary
Keep the Quote and Transfer contract changes backward compatible where possible: existing fixtures and tests must keep passing, so add new fields as required only if fixtures are updated in the same change, otherwise mark them optional and enforce presence in validateQuoteForTransfer. The quoteId must be generated deterministically enough for tests (inject a clock/id factory via buildQuote options). Invalidate the active quote on every input and network change, and re-validate at confirmation time against the live clock so a quote that expires while the dialog is open cannot be submitted. Do not strip quoteId in useTransfers or api.createTransfer.
Changes
source(string, e.g. the quote provider/feed identifier),quoteId(string, minLength 1),fromCurrency/toCurrencymetadata objects (or reusefrom/toplus addfromMinorUnits/toMinorUnitsintegers), and acanonicalAmountdecimal string. Add avalidateQuoteForTransfer(quote, { now, from, to, amount })helper that returns{ valid, reasons }covering expiry (via isQuoteExpired), currency-pair mismatch against the requested from/to, and canonical-amount/precision mismatch (compare the quote's canonicalAmount against the requested amount quantized to the source currency minor unit). Keep the existing fields andisQuoteExpiredbehaviour so current fixtures and tests continue to pass. (src/services/contracts/quote.js)quoteId(e.g. crypto.randomUUID or a deterministic id derived from inputs+timestamp for tests), setsourceto the FX feed identifier (e.g. 'mock-fx'), include currency metadata (from/to minor units via currencyExponent) and acanonicalAmountequal to the quantized send amount. Keep the injectablenow/ttlMsoptions and the parseQuote round-trip so every produced quote is contract-valid. Do not change existing numeric outputs (sendAmount, fee, amountAfterFee, receiveAmount, rate) so precision tests stay green. (src/services/quote.js)FX_SOURCE = 'mock-fx') and agetRateMetadata(from, to)helper returning{ source, from, to, rate, fromMinorUnits, toMinorUnits }(minor units via currencyExponent) so the quote service and QuoteCard can display where a rate came from and its precision. Keep getRateDecimal/getRate/convert unchanged. (src/services/fx.js)quoteIdfield (string, required, minLength 1) to the Transfer v1 contract so the quote that priced a transfer is bound to the stored record. Keep it optional-compatible by marking itrequired: falseif legacy records must still parse, but document that new transfers always include it. Do not change other fields or status handling. (src/services/contracts/transfer.js)getCurrencyMetadata(code)helper (or extend getCurrency) returning{ code, name, symbol, minorUnits }for display in QuoteCard, and export aSUPPORTED_CURRENCY_CODESlist derived from CURRENCIES for the currency-matrix validation. Keep existing exports (CURRENCIES, DEFAULT_SOURCE, DEFAULT_DEST, getCurrency, getCurrencyMinorUnits) unchanged. (src/constants/currencies.js)validateQuoteForTransfer(quote, { now, from, to, amount, locale })helper that returns{ valid: boolean, reasons: string[] }by delegating to the quote contract's validity check and additionally verifying the quote's currency pair matches the form's from/to and that the quote's canonicalAmount equals the form amount quantized to the source currency minor unit. This centralises the expiry/currency/precision checks used by SendMoney and keeps them unit-testable. (src/utils/validate.js)nowprop (default Date.now()) so tests can pin the clock, and add aninvalid/reasonsprop to render a warning list when validateQuoteForTransfer fails. Keep the existing rate/fee/receive rows and formatting helpers. (src/components/QuoteCard.jsx).quote-metablock for source/timestamp/expiry/currency metadata, a.quote-expiredwarning banner (contrast-safe, with a[data-theme='light']variant), and.quote-invalid-reasonslist styling. Ensureprefers-contrast: morekeeps borders visible. (src/components/QuoteCard.css)quoteId: finalQuote.quoteIdin the addTransfer payload. Disable the confirm button when the quote is invalid. Passnow/invalid/reasonsthrough to QuoteCard. (src/pages/SendMoney.jsx).send-quote-invalidnotice shown when the active quote is expired or mismatched, and any layout tweaks needed for the expanded QuoteCard metadata. Keep existing offline/reconnected notice styles and theprefers-contrast: moreoverrides. (src/pages/SendMoney.css)quoteIdto createTransfer unchanged (no field stripping), and that a ContractViolationError from a missing/mismatched quoteId is surfaced through the existing error path. Add a brief comment documenting that the quote ID binds the transfer to the quote that priced it. (src/hooks/useTransfers.js)quoteIdand that the QuoteCard displays the canonical amount, source, timestamp and expiry, and that a precision mismatch between the form amount and the quote's canonicalAmount blocks confirmation. Keep the existing fee/rate/receipt assertions unchanged. (test/integration/send-money-precision.test.jsx)