-
Notifications
You must be signed in to change notification settings - Fork 5
Description
Background
Flash is a Bitcoin banking platform for Jamaica. Users who have set JMD as their display currency are seeing incorrect amounts across three critical surfaces: their wallet balance, the send/receive flows, and their transaction history. These are not cosmetic issues — users are losing money on precision truncation and seeing a transaction history that changes every time the BTC price moves.
The bugs span the full stack. The backend serves stale/wrong data from its transaction adaptor layer, and the mobile app compounds the problem with its own conversion errors. Both must be fixed for JMD amounts to be trustworthy end-to-end.
Scope
This bounty covers all three affected surfaces:
Wallet Balance
The BTC wallet balance query (me.btcWallet.balance) currently returns a serialized class object ({}) instead of a number, because BtcWallet.balance serializes the WalletBalance class instance rather than its numeric value.
File: src/graphql/shared/types/object/btc-wallet.ts (~line 49)
Fix: Return Number(balance.asCents(8)) — the raw satoshi count as a number.
Send & Receive Flows
When a JMD user enters an amount (e.g., JMD$1,000) to create a Lightning invoice or send a payment via the USD wallet, the conversion from JMD to USD cents produces a JavaScript float (e.g., 625.78...). This float is sent directly to the GraphQL API, which expects an integer. The API truncates (e.g., to 625), and the user receives or sends a slightly different amount than intended — consistently losing a small amount to rounding error on every transaction.
Root cause: convertMoneyAmount() in use-price-conversion.ts only calls Math.round() when the target currency is BTC. USD conversions are left as floats.
Files:
app/hooks/use-price-conversion.ts (lines 74–81) — missing Math.round() for USD target
app/screens/receive-bitcoin-screen/payment/payment-request.ts (lines 155–167) — float passed to lnUsdInvoiceCreate
app/screens/send-bitcoin-screen/payment-details/intraledger.ts (lines 58–78) — float passed to intraLedgerUsdPaymentSend
Fix: Apply Math.round() to all currency conversion results, not just BTC. Additionally, add a defensive Math.round() at the API mutation call sites so the fix survives future refactoring.
Backend dependency: The backend price service (src/services/price/index.ts) currently derives the JMD/USD rate by triangulating through BTC rather than using the static ExchangeRates.jmd.sell value from config/yaml.ts. This compounds the mobile rounding error because the exchange rate itself is imprecise. The price service fix (see item 3 below) must land before mobile amounts can be validated as correct.
Transaction History
Transaction history is broken at two layers:
Backend layer (lnflash/flash repo):
The transaction history adaptor (src/app/wallets/get-transactions-for-wallet.ts) has four compounding bugs:
Hardcodes displayCurrency: "USD" regardless of the user's actual display currency
Uses the wrong exponent offset when converting the price, producing amounts off by a large factor
Calls Math.floor() on the price, which truncates sub-dollar JMD prices to 0
Does not apply the JMD/USD conversion — JMD users receive USD amounts from the API
Additionally, the price service derives JMD rate via BTC triangulation (src/services/price/index.ts) — it should use the static ExchangeRates.jmd.sell value from config/yaml.ts directly.
Files:
src/app/wallets/get-transactions-for-wallet.ts — fix displayCurrency, offset, Math.floor, and JMD conversion
src/services/price/index.ts — use ExchangeRates.jmd.sell instead of BTC triangulation
Mobile layer (lnflash/flash-mobile repo):
The TxItem component ignores the settlementDisplayAmount field already returned by the API (which contains the correct historical JMD amount locked in at settlement time) and instead re-computes the JMD amount using the current BTC price. This means every BTC transaction in history shows a different JMD amount each time the price moves. The transaction detail screen already does this correctly (using settlementDisplayAmount) — the list should match it.
Additionally, for USD wallet transactions, TxItem multiplies settlementAmount by 100 (tx.settlementAmount * 100), but settlementAmount is already in cents. This inflates all USD wallet transaction amounts by 100×.
For BTC wallet transactions from the Breez SDK, useBreezPayments.ts hardcodes settlementDisplayCurrency: "USD" and converts to USD, ignoring the user's JMD preference entirely.
Files:
app/components/transaction-item/TxItem.tsx (lines 42–60) — use settlementDisplayAmount / settlementDisplayCurrency instead of live price reconversion
app/components/transaction-item/TxItem.tsx (line 55) — remove * 100 for USD wallet amounts
app/hooks/useBreezPayments.ts (lines 13–18, 46–48) — use DisplayCurrency target and user's currency code, not hardcoded USD