Problem
The Settings page (src/pages/Settings.tsx) lets a user configure five preferences, shows a "Settings Saved!" confirmation, and persists them to localStorage — but four of the five silently have no effect on the app's actual behavior. Only network is genuinely wired end to end.
autoRefreshInterval is read from a localStorage key that nothing ever writes. Settings.tsx's useSettings() hook persists the entire settings object as one JSON blob under a single key:
// src/pages/Settings.tsx:36-38
const save = (next: AppSettings) => {
setSettings(next)
localStorage.setItem('stellarsend_settings', JSON.stringify(next))
}
But WalletContext.tsx's account-refresh interval reads a completely different, flat key that is never written anywhere in the codebase:
// src/context/WalletContext.tsx:154-159
const interval = parseInt(
localStorage.getItem('stellarsend_refresh_interval') || '30',
10,
)
refreshTimerRef.current = setInterval(refreshAccount, interval * 1_000)
$ grep -rn "stellarsend_refresh_interval" src --include="*.tsx" --include="*.ts"
src/context/WalletContext.tsx:156: localStorage.getItem('stellarsend_refresh_interval') || '30',
That's the only occurrence in the whole codebase. Whatever the user picks in the "Auto-Refresh Interval" dropdown (10s / 30s / 1 min / 5 min), WalletContext always falls back to the hardcoded default of 30, forever.
slippageTolerance is saved but never read by the quote/payment pipeline. QuoteRequest (src/types/index.ts:130-138) — what actually gets sent to quoteApi.getQuote — has no slippageTolerance field at all; the slippage value used everywhere in the send flow (QuoteCard.tsx, buildTransactionFromQuote in lib/stellar.ts) comes from quote.slippageTolerance, a field the backend returns on the quote response, not from the user's Settings selection:
$ grep -rn "slippageTolerance" src --include="*.tsx" --include="*.ts" | grep -v types/index.ts
src/components/send/QuoteCard.tsx:167,171 # uses quote.slippageTolerance (from the backend quote)
src/lib/stellar.ts:204 # uses quote.slippageTolerance
src/pages/Settings.tsx:265-266 # reads/writes localSettings.slippageTolerance
src/lib/api.ts:121 # slippageTolerance?: string — an unused field on an unused buildTransaction params type
src/pages/Send.tsx:177 # hardcodes '0.5' in an unrelated placeholder quote object
The user's configured tolerance is round-tripped to and from localStorage purely for its own sake.
defaultMemo is never read outside Settings.tsx itself. Send.tsx seeds SendForm's defaultValues from state.formValues only (src/pages/Send.tsx:145), never from settings.defaultMemo — so the memo field is never pre-filled despite the setting existing specifically to do that (per its own hint text: "Will be pre-filled on the send form").
showTestnetWarning is never read outside Settings.tsx either. The testnet banner in Layout.tsx is shown unconditionally whenever network === 'testnet', with no reference to the setting meant to control it:
// src/components/layout/Layout.tsx:13-20
{network === 'testnet' && (
<div className="bg-warning-500/15 ...">
<p>You are on <strong>Testnet</strong> — ...</p>
</div>
)}
Toggling "Show testnet banner" off in Settings has no observable effect anywhere.
Why it matters
This isn't a cosmetic gap in one setting — it's four out of five controls on the entire Settings page silently doing nothing, while the page actively tells the user otherwise: the "Saved!" confirmation, the descriptive hint text under each control ("Maximum price deviation before transaction reverts", "Will be pre-filled on the send form", "Display a warning banner when using testnet"), and the very existence of a form with a save button all signal that these are live, meaningful preferences. A user who deliberately sets a tighter slippage tolerance to protect themselves from a bad path-payment fill, for example, gets no such protection — the number they typed is inert.
Reproduction
- Set "Auto-Refresh Interval" to "5 minutes", save, reload, connect a wallet, and watch network requests —
refreshAccount() still fires every 30 seconds (grep above confirms nothing ever writes stellarsend_refresh_interval).
- Set "Default Memo" to some text, save, navigate to Send — the memo field on
SendForm is empty, not pre-filled.
- Turn off "Show testnet banner" while connected to testnet — the banner in
Layout.tsx remains visible.
- Set "Slippage Tolerance" to
5, then send a path payment — buildTransactionFromQuote's destMin calculation uses quote.slippageTolerance from the backend's quote response, not the 5 just configured.
Suggested fix
autoRefreshInterval: either write it under the key WalletContext actually reads (stellarsend_refresh_interval) when saving settings, or — better — have WalletContext read autoRefreshInterval out of the same stellarsend_settings blob Settings.tsx already writes, so there's one source of truth.
defaultMemo: thread settings.defaultMemo into SendForm's defaultValues in Send.tsx when there's no in-progress state.formValues yet.
showTestnetWarning: gate Layout.tsx's banner on the setting, not just network === 'testnet'.
slippageTolerance: either add it to QuoteRequest and have the backend honor it when computing the quote's own slippageTolerance/destMin, or remove the control from Settings if slippage is intentionally always server-determined, to stop presenting it as a working client-side control.
Additional Notes
src/pages/Settings.tsx:25-44 (useSettings), src/context/WalletContext.tsx:154-159, src/components/layout/Layout.tsx:13-20, src/pages/Send.tsx:145, src/types/index.ts:130-138,216-230 (QuoteRequest, AppSettings/DEFAULT_SETTINGS).
- Testing strategy: an integration test that saves a non-default
AppSettings object and asserts observable effects — WalletContext's refresh timer interval, SendForm's initial memo value, and Layout's banner visibility — currently none of the three would pass.
Problem
The Settings page (
src/pages/Settings.tsx) lets a user configure five preferences, shows a "Settings Saved!" confirmation, and persists them tolocalStorage— but four of the five silently have no effect on the app's actual behavior. Onlynetworkis genuinely wired end to end.autoRefreshIntervalis read from alocalStoragekey that nothing ever writes.Settings.tsx'suseSettings()hook persists the entire settings object as one JSON blob under a single key:But
WalletContext.tsx's account-refresh interval reads a completely different, flat key that is never written anywhere in the codebase:That's the only occurrence in the whole codebase. Whatever the user picks in the "Auto-Refresh Interval" dropdown (10s / 30s / 1 min / 5 min),
WalletContextalways falls back to the hardcoded default of30, forever.slippageToleranceis saved but never read by the quote/payment pipeline.QuoteRequest(src/types/index.ts:130-138) — what actually gets sent toquoteApi.getQuote— has noslippageTolerancefield at all; the slippage value used everywhere in the send flow (QuoteCard.tsx,buildTransactionFromQuoteinlib/stellar.ts) comes fromquote.slippageTolerance, a field the backend returns on the quote response, not from the user's Settings selection:The user's configured tolerance is round-tripped to and from
localStoragepurely for its own sake.defaultMemois never read outsideSettings.tsxitself.Send.tsxseedsSendForm'sdefaultValuesfromstate.formValuesonly (src/pages/Send.tsx:145), never fromsettings.defaultMemo— so the memo field is never pre-filled despite the setting existing specifically to do that (per its own hint text: "Will be pre-filled on the send form").showTestnetWarningis never read outsideSettings.tsxeither. The testnet banner inLayout.tsxis shown unconditionally whenevernetwork === 'testnet', with no reference to the setting meant to control it:Toggling "Show testnet banner" off in Settings has no observable effect anywhere.
Why it matters
This isn't a cosmetic gap in one setting — it's four out of five controls on the entire Settings page silently doing nothing, while the page actively tells the user otherwise: the "Saved!" confirmation, the descriptive hint text under each control ("Maximum price deviation before transaction reverts", "Will be pre-filled on the send form", "Display a warning banner when using testnet"), and the very existence of a form with a save button all signal that these are live, meaningful preferences. A user who deliberately sets a tighter slippage tolerance to protect themselves from a bad path-payment fill, for example, gets no such protection — the number they typed is inert.
Reproduction
refreshAccount()still fires every 30 seconds (grepabove confirms nothing ever writesstellarsend_refresh_interval).SendFormis empty, not pre-filled.Layout.tsxremains visible.5, then send a path payment —buildTransactionFromQuote'sdestMincalculation usesquote.slippageTolerancefrom the backend's quote response, not the5just configured.Suggested fix
autoRefreshInterval: either write it under the keyWalletContextactually reads (stellarsend_refresh_interval) when saving settings, or — better — haveWalletContextreadautoRefreshIntervalout of the samestellarsend_settingsblobSettings.tsxalready writes, so there's one source of truth.defaultMemo: threadsettings.defaultMemointoSendForm'sdefaultValuesinSend.tsxwhen there's no in-progressstate.formValuesyet.showTestnetWarning: gateLayout.tsx's banner on the setting, not justnetwork === 'testnet'.slippageTolerance: either add it toQuoteRequestand have the backend honor it when computing the quote's ownslippageTolerance/destMin, or remove the control from Settings if slippage is intentionally always server-determined, to stop presenting it as a working client-side control.Additional Notes
src/pages/Settings.tsx:25-44(useSettings),src/context/WalletContext.tsx:154-159,src/components/layout/Layout.tsx:13-20,src/pages/Send.tsx:145,src/types/index.ts:130-138,216-230(QuoteRequest,AppSettings/DEFAULT_SETTINGS).AppSettingsobject and asserts observable effects —WalletContext's refresh timer interval,SendForm's initial memo value, andLayout's banner visibility — currently none of the three would pass.