ZeusLN / ZeusLN/zeus

Fiat sentinel strings ("Disabled"/"N/A") leak into editable amount inputs when no fiat rate is available

Open
#4,635 2 comments 0 reactions 1 assignee View on GitHub

@TheSeydiCharyyev is already working on this.

Since Sep 13, 2026.

Amounts Bug Exchange rates
Dominant language
TypeScript
Stars
1.4k
Forks
247
Avg merge
2d 2h
Merged PRs (30d)
129

Description

Spawned from @ajaysehwal's review note on #4625: https://github.com/ZeusLN/zeus/pull/4625#pullrequestreview-5170149054

Problem

getUnformattedAmount() returns a localized sentinel string in its amount field in all three fiat failure branches:

  • no fiat currency configured while units are fiat (utils/AmountUtils.ts L147-L153, the branch with the TODO: is this the right place to catch this? comment)
  • rates fetched but no rate for the selected currency (L158-L164)
  • no rates at all (L186-L192)

Because the sentinel ("Disabled" in English) is truthy, callers that do getUnformattedAmount(...).amount || fallback never hit their fallback. With #4625 this pattern is centralized in getRawAmountFromSats(), which feeds the editable amount state in views/Send.tsx and views/ClinkPay/ClinkPay.tsx. So with fiat as the active unit and no rate available, scanning a BIP21 QR or opening Send with a preset amount puts the literal text "Disabled" into the editable AmountInput field.

This predates #4625: the Send constructor used the identical getUnformattedAmount(...).amount || satAmount expression before the extraction, and the param-update path used getAmountFromSats(), whose own sentinel is "N/A" (general.notAvailable, L268). Same bug class either way, so not a regression from #4625.

Impact

Low. getSatAmount() in fiat mode with no rate computes with rate = 0 and returns 0 for any input, so there is no NaN and no wrong-amount send. The symptom is sentinel text sitting in an editable input.

Why the obvious fix is not enough

Making the || fallback fire (raw sats string) would be wrong-unit: with the input in fiat mode, 12618 reads as 12,618 fiat units. The sentinel needs to be handled structurally instead:

  1. getUnformattedAmount() already sets an error field in two of the three branches; add it to the no-currency branch too, and have callers key off error/unit rather than comparing display strings.
  2. getRawAmountFromSats() should detect the failure and either return the sats-unit raw value while forcing the input's unit to sats, or return an empty string and leave the parsed satAmount intact.
Relation to existing issues

Same root-cause family as #2261 and #3831: amount formatters return localized sentinel strings instead of signaling failure structurally, and callers either render them ("N/A" everywhere in Amount.tsx) or, here, feed them into editable inputs. #3831 covers the display/UX side (and the FiatStore rate-clobbering behavior discussed there); this issue covers the editable-input side. A structural error signal from getUnformattedAmount() (point 1 above) would serve both.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.