Skip to content

fix(usdt): remove micros conversion - backend now returns cents#663

Merged
islandbitcoin merged 1 commit into
feat/fygarofrom
clean/usdt-remove-micros
Jul 1, 2026
Merged

fix(usdt): remove micros conversion - backend now returns cents#663
islandbitcoin merged 1 commit into
feat/fygarofrom
clean/usdt-remove-micros

Conversation

@forge0x

@forge0x forge0x commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

The backend fix (PR #413) now returns settlementAmount in cents, not micros. All micros-to-cents conversion on the mobile side is obsolete.

Removes:

  • USDT_MICROS_PER_USDT, USDT_MICROS_PER_USD_CENT constants
  • Micros division in use-display-currency.ts
  • USDT_MICROS_PER_USD_CENT scaling in price conversion hooks
  • Micros-to-cents conversion in payment-request.ts
  • Related test assertions

@islandbitcoin islandbitcoin merged commit a829b73 into feat/fygaro Jul 1, 2026
3 checks passed
islandbitcoin added a commit that referenced this pull request Jul 3, 2026
* add transfer feature mvp (topup with card)

* redesign and refactor transfer-screen, topup-screen and card-payment-screen

* BankTransfer screen is implemented and added in the root navigation

* restructure buy-sell-bitcoin flow screens

* add BankTransfer texts on the i18n file

* refactor and clean up CardPayment webview screen

* use texts from i18n on the BankTransfer screen and update BuyBitcoinDetails screen navigation logic

* update BuySellBitcoin screen icons and added navigation to cashout

* ArrowUpDown icon btn is added

* add back cashout mutations

* show Transfer button instead of Cashout button on the home screen

* update CashoutDetails screen

* update cashout description text

* update CashoutConfirmation screen to show amount in display currency

* add back exchangeRate to the requestCashout offer response and update the CashoutConfirmation screen accordingly

* add back exchangeRate on the generated.gql and generated.ts files

* BuyBitcoinSuccess screen is implemented and added in root navigation

* enabled card payments via Fygaro - webhook wip

* add developer comments

* add cashout api integration changes in this branch

* Withdraw to section is implemented and  added on the cashout confirmation screen to show user account details

* use TransferOptionModal to show topup option on the TopupCashout screen when press Top up button

* refactor and clean up topup/cashout flow updting structure

* Implement BridgeKycModal to get user details (full name, email and kyc type) and use on the TopupCashout screen

* Implement BridgeKycWebView screen to handle tos and kyc flow and add in the root navigation

* CashWalletCutoverModal is implemented to notice users about migration

* integrate bridgeKycStatus query and bridgeInitiateKyc mutation into TopupCashout screen

* use CashWalletCutoverModal in the home screen

* update ui to show pending status properly and show alert message to the user about kyc pending

* fix bridgeKycStatus response showing null and implement refetchKycStatus when user finish kyc process

* implement pull to refresh to fetch updated kyc status on the TopupCashout screen

* open external browser when user press term of service or privacy poicy on the tos webview screen

* update BankTransfer screen to show bridge virtual account details as well

* add texts for bridge integration

* update TopupCashout and TopupDetails screens to integrate bridge flow

* update button-group component styling

* replace DropDownField component with ButtonGroup for kycType section

* bridge related mutations and queries are added

* update codegen.ynl

* fix tos accept button routing to external browser issue

* add BankAccounts query that fetches user bank account from erpnext

* update CashoutDetails screen to implement requestCashout mutation changes

* resolve CashoutConfirmation screen issues after cashout mutations updated and update CashoutWithdrawTo component to use BankAccounts query to show user default bank account

* resolve ios build issue

* display cashout/topup button to all users except level Zero on the home screen

* update topup cashout option on the TopupCashout screen that level Zero/One users press the top up or cashout button navigates to account upgrade level

* fixed node versioning

* update Type Definitions & Core Infrastructure to support usdt wallet

* update Price Conversion & Display hooks to support usdt wallet

* change getUsdtWallet to getCashWallet that returns usdt wallet if exist otherwise fallback to usd wallet

* update UI Components to support USDT wallet

* update Payment Flows (interlader, lightning and onchain) to support USDT wallet

* update multiple screens to support usdt wallet

* replace getUsdWallet with getCashWallet method

* set the header X-Flash-Client-Capabilities: cash-wallet-usdt-v1 to support usdt wallet schemas

* enable card top up for level one users

* Add International upgrade option (#633)

* Add International upgrade option

* fix: open bridge kyc modal from account type

* clean up and open bridge kyc modal on the account upgrade type screen

* clean up

* Hide the international/bridge upgrade button when KYC is approved and Show orange border on the international card when KYC is pending

* hide the international kyc button if user level zero

---------

Co-authored-by: Vandana <forge@getflash.io>
Co-authored-by: nodirbek75 <nodirbek7077@gmail.com>

* hide and never display CashWalletCutover modal once user see it

* Update CashWalletCutoverModal to show modal only for existing users and not show newly registered users after cutover

* bridge withdrawal mutation and queries are added

* BridgeExternalAccountWebView is implemented and added on the root navigation

* integrate bridgeAddExternalAccount mutation and bridgeExternalAccounts query into TopupCashout screen to add and fetch external account

* ci: run checks on the feat/fygaro integration branch (#637)

Test/check workflows were filtered to `pull_request: branches: [main]`,
so PRs targeting the long-lived `feat/fygaro` integration branch never
triggered CI. Add the integration branch to the pull_request filter on
the gating workflows, and add a push trigger on the core check/test
workflows so the branch tip is checked as PRs stack onto it.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(ci): bump CI Node to 20 and resolve spelling-check failures (#638)

Once Actions ran on feat/fygaro for the first time, three checks failed.
All were pre-existing issues, not caused by enabling CI:

- Check Code & Test both died at `yarn install`: `@noble/ciphers@2.1.1`
  requires Node >=20.19 but CI pinned Node 18. Bump check-code.yml,
  test.yml, .node-version, and package.json engines to 20.
- Spelling flagged 13 typos. Fixed the safe ones (a local var and three
  code comments); excluded non-source files where the rest live
  (logcat.txt, *.storyboard); and allowlisted tokens that can't be
  renamed safely here — earns quiz IDs (backend contract), i18n message
  keys (need codegen pipeline), and cross-file identifiers.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* ci: stop conventional-pr from auto-closing invalid PRs (#639)

The Namchee/conventional-pr action defaults to closing any non-draft PR
whose title isn't conventional. With Actions re-enabled, this auto-closed
the feat/fygaro umbrella PR #621 on every reopen. Set close: false so it
comments/reports instead of closing.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix: render USDT amounts and currency tags (#634)

Co-authored-by: Vandana <forge@getflash.io>

* feat: add bridge cashout confirmation flow (#635)

Co-authored-by: Vandana <forge@getflash.io>

* update cutover label (#636)

Co-authored-by: Vandana <forge@getflash.io>

* fix(ci): resolve feat/fygaro tsconfig TS5095 + spelling allowlist (#642)

- tsconfig: module was 'Node16' while the extended @react-native config
  uses moduleResolution 'bundler', which requires an esnext-class module
  (TS5095). Set module=esnext + moduleResolution=bundler explicitly.
- typos: accept the earns quiz-ID misspellings (Aggrement/Governement) as
  bare words too — they're a backend contract and can't be renamed.

Addresses ENG-420. Does not cover Test (ttypescript — see #640) or the
i18n drift (needs `yarn update-translations` run locally).

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* ci: run Check Code + Test on push to main (#643)

Add main to the push trigger for the core check-code and test workflows
so main's own tip is continuously validated, not only PRs into it. Lands
on main via #621 (feat/fygaro -> main).

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(receive): convert USDT settlement amount from micros to cents for lnUsdInvoiceCreate

USDT money amounts are denominated in smallest units (micros) by the price
conversion layer, which are USDT_MICROS_PER_USD_CENT (10,000x) smaller than a
cent. The lnUsdInvoiceCreate mutation expects USD cents, but the receive flow
forwarded the raw USDT settlement amount, so a $5.00 request (500 cents) was
sent as 5,000,000 and the backend minted a ~$50,000 invoice.

Convert USDT micros back to cents at the mutation boundary (USD amounts are
already in cents and pass through unchanged). Add a regression test asserting a
$5.00 USDT request calls the mutation with amount=500.

Manually verified on device: scanning a $5.00 USDT invoice now displays $5.00.

* fix(cashout): prefer JMD bank account for local cashout

* test(payment-request): run spec without ttypescript

* feat(ui): display stablecoins as fiat currency in all screens (#648)

Stablecoin wallets (USDT today; USDC, USDB, PYUSD, EURC, etc. in the
future) should display as their underlying fiat currency — not the
stablecoin ticker. Users see "USD" whether their cash wallet is USDT,
USDC, or any future USD-pegged stablecoin.

This is a UI-only change. Underlying wallet routing, currency enums,
amount conversions, and settlement logic remain unchanged.

Changes:
- Add currency-display.ts with extensible STABLECOIN_DISPLAY_MAP
  (USDT → "USD" today; add new stablecoins with one line)
- CurrencyTag: render displayCurrencyCode() instead of raw enum
- use-display-currency: set currencyCode "USD" for USDT, suppress
  suffix so USDT amounts render identically to USD amounts
- amounts.ts: set currencyCode "USD" in USDT amount objects
- Balance.tsx: map currency code through displayCurrencyCode()
- CashoutWithdrawTo: map bank account currency through displayCurrencyCode()
- Update currency-tag test to assert display mapping

Adding a future stablecoin = one line in STABLECOIN_DISPLAY_MAP.
No other display code needs to change.

Co-authored-by: Vandana <forge@getflash.io>

* fix(tx-history): map USDT to USD in transaction list items

Two issues in TxItem.tsx:

1. 'Received USDT' / 'Sent USDT' label used raw settlementCurrency
   — now passes through displayCurrencyCode() to show 'USD'

2. Secondary amount for USDT transactions was rendering the raw USDT
   settlement amount with no currency indicator (after removing the
   suffix). USDT transactions now follow the same path as USD —
   showing the BTC equivalent as the secondary amount.

* fix(tx-history): USDT secondary amount shows $1.00 not ₿1,587

Two fixes:

1. TxItem.tsx: revert USDT secondary amount to raw settlement amount
   (not BTC conversion). USD transactions show BTC as secondary because
   their wallet settlement is USD. USDT transactions should show the
   settlement amount formatted as USD.

2. use-display-currency.ts: set USDT minorUnitToMajorUnitOffset to
   usdDisplayCurrency.fractionDigits (2) instead of hardcoded 6.
   The raw→major conversion uses /USDT_MICROS_PER_USDT (1M) which is
   correct. The offset is only used for display fraction digits, so
   it should match USD (2 digits) to show '$1.00' not '$1.0000000'.

* fix(display): show '.0000000 USD' for USDT amounts

Restore 6 decimal places (minorUnitToMajorUnitOffset: 6) and re-add
currencyCode suffix for USDT so users can see exact stablecoin amounts
and rounding. Suffix now reads 'USD' (from usdDisplayCurrency.id)
instead of 'USDT'.

* chore(bridge): drop KYC-response console.logs (#649)

Removes two `console.log("BRIDGE INITIATE KYC RESPONSE: ", res)` statements in
the Bridge KYC-initiation flow (AccountType.tsx, TopupCashout.tsx). Logging the
KYC response writes sensitive identity data to the console; it serves no
purpose in production. `res` is still used for error handling, so behavior is
unchanged.

Surfaced during review of #621. The transaction-details TODO stub on the
cashout success screen is intentionally left as-is.

Co-authored-by: Dread <dread@example.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* chore: remove unused displayCurrencyCode import from use-display-currency

Leftover from iteration — the hook uses usdDisplayCurrency.id directly
instead of the helper, so the import was unused.

* fix(receive): remove duplicate 'Cash' wallet option in wallet picker

The WalletBottomSheet showed two 'Cash' entries (USD + USDT) in
advanced mode. Since setReceivingWallet routes both USD and USDT to
the same cash wallet (first USD or USDT wallet from the query), the
duplicate was redundant and confusing.

Now shows just 'Cash' and 'Bitcoin'.

* fix(tx-history): cash transactions respect user's display currency

Cash wallet transactions were using the backend-provided
settlementDisplayAmount/settlementDisplayCurrency for the primary
line, which always showed USD regardless of the user's configured
display currency. Now uses moneyAmountToDisplayCurrencyString (same
as BTC transactions) to convert through the price feed to the user's
actual display currency.

* test: update USDT display assertion for stablecoin→USD mapping

The formatMoneyAmount output for USDT changed from '$0.179554 USDT'
to '$0.179554 USD' after mapping USDT currencyCode to usdDisplayCurrency.id.

* fix: display the overlay during the selfie step of the Bridge KYC flow (#650)

* fix selfie step on the bridge kyc flow not showing the overlay with informative texts

* fix term of services and privacy policy links not opening on the external browser

* fix(bridge-kyc): scope ToS legal-link external-open to bridge.xyz host

The ToS-step handler opened any URL containing '/legal' in the external
browser. Restrict it to bridge.xyz (and subdomains) + a /legal path so an
unrelated host can't be shelled out via Linking.openURL.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Dread <dread@example.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* test: repair frontend CI checks

* test: stabilize frontend CI checks

* fix: bridge manual external account fallback (#652)

* fix(bridge): prevent back-stack trap after manual bank link

Use navigation.replace instead of navigate after successful external
account creation so the manual form is removed from the stack —
pressing back from CashoutDetails returns to TopupCashout, not the
form. Also handle the duplicate-submission case: if the user re-enters
the same account details and Bridge returns an error, treat it as
'already linked' and proceed to cashout instead of showing a 400.

* fix(topup): replace hardcoded bank details with WhatsApp support CTA

The local bank transfer screen had hardcoded Flash banking details
(BBW/Banco Hipotecario). These should come from ERPNext dynamically
with a per-user reference code — tracked as part of TopUp V1 in
Linear. Until that's built, show a message directing users to
contact support via WhatsApp for banking information.

* fix(topup): skip amount entry for disabled bank transfer

* fix(topup): polish WhatsApp support CTA

* test(send-confirmation): fix flaky fee-display assertion

The realtimePrice mock typed me.defaultAccount as the "Account"
interface while every other mock uses the concrete "ConsumerAccount".
This normalized to a separate Apollo cache entity, so whichever query
wrote me.defaultAccount last won; when a ConsumerAccount query (no
realtimePrice field) resolved after realtimePrice, the display currency
reverted to USD and the fee's display-currency amount rendered empty.
Aligning the __typename makes realtimePrice persist regardless of
resolution order, so SendScreen Confirmation settles deterministically.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix: block self-send conversion in cash mode (#660)

* fix(usdt): remove micros conversion - backend now returns cents (#663)

* fix(usdt): format cash display amounts as cents

* fix(cash): address transfer flow review blockers

* feat(topup): gate local topup options by backend flag

* feat(bridge): gate bridge transfer entry points

* fix(send): support Spark LNURL-pay inputs (#662)

Co-authored-by: Vandana <forge@getflash.io>

* feat(bridge): Bridge banking in settings (#653)

* feat(bridge): show virtual account details in settings

* feat(bridge): scaffold unified Bank accounts hub (money-in + money-out)

Introduce a single "Bank accounts" settings surface that unifies the Bridge
virtual (receive) account and the user's withdrawal banks behind one
rail-agnostic view model, replacing the standalone routing-number row.

- add normalized BankAccountVM + useBankAccounts() hook merging
  bridgeVirtualAccount (receive), bridgeExternalAccounts (USD withdraw) and
  me.bankAccounts (local withdraw), with per-currency default resolution
- add BankAccountsScreen: Receive card (reveal/copy, KYC-gated) + currency
  grouped Withdraw list with default selection, status pills and add flow
- interim client-side default store (AsyncStorage) until backend adds
  isDefault + bridgeSetDefaultExternalAccount (kept isolated from the
  persistentState migration chain for a one-file swap later)
- wire BankAccounts route + settings row; hub handles KYC state internally

Scaffolding only: set-default persists client-side; remove is gated on a
future bridgeDeleteExternalAccount mutation. tsc + eslint clean.

* feat(bridge): cancel button on add-account + WhatsApp support link

- Add Cancel button in the Add Bank Account header (navigation.goBack)
- Add Contact Support button at the bottom of the Bank accounts hub
  that opens WhatsApp support (wa.flashapp.me) via openWhatsAppUrl

* feat(bridge): add Upgrade button to locked Bank accounts card

 KYC-not-approved state now shows an Upgrade your account button that
 navigates to the AccountType upgrade flow — same destination as the
 QuickStart upgrade card on the Home screen.

* feat(bridge): finish Bank accounts follow-ups

* fix(bridge): allow Bank accounts screen to scroll

* polish(bank-accounts): i18n already-linked alert + small UX fixes

- extract the #652 already-linked alert strings to i18n
  (BridgeAddExternalAccount.alreadyLinkedTitle/Message)
- toast confirmation when a default withdrawal account is set in the
  hub (no feedback before beyond the radio moving)
- CashoutWithdrawTo: reload the stored default on screen focus instead
  of mount only, so a default changed in Settings is reflected when
  returning to an already-mounted cash-out screen
- promote serverDefault to a proper optional field on BankAccountVM
  instead of smuggling it through type casts

Verified: tsc:check clean, eslint clean on touched files, jest
36 suites / 159 tests green on the merged tree.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* chore(i18n): backfill Bank accounts keys into all locale files

The translation-drift check (added in #604) requires every locale to
contain all en.json keys. Backfill the 72 new Bank accounts / add-bank
keys into all 23 locale files with English placeholder values — same
interim convention as existing untranslated keys (e.g. Cashout section).
Proper translations can follow the ENG-242 machine-translate process.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Patoo <262265744+patoo0x@users.noreply.github.com>
Co-authored-by: Dread <dread@example.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(bank-accounts): gate settings hub behind bridgeTopupEnabled kill switch

The Bank accounts hub (#653) landed after the ENG-465 remote-config gate
was folded into feat/fygaro and missed it: the settings row rendered for
everyone and the hub fired Bridge KYC/virtual-account/external-account
queries with the kill switch off — violating the gate's contract (hide
Bridge entry points, skip Bridge queries) and dead-ending users at an
Upgrade CTA whose Bridge path is hidden.

- hide BankAccountsSetting when !bridgeTopupEnabled (same contract as
  AccountType / TopupCashout)
- skip useBridgeKycStatusQuery in useBankAccounts when the flag is off;
  the virtual/external account queries already skip on !kycApproved and
  are now transitively gated
- split the AsyncStorage default-account helpers into
  default-account-store.ts: CashoutWithdrawTo/CashoutDetails only need
  the stored id, and importing the hook module would have pulled
  feature-flags-context (and Firebase Remote Config) into their module
  graph — breaking home.spec and coupling cash-out to settings

Verified: tsc:check clean, eslint clean, jest 36 suites / 159 tests
green (home.spec included).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Dread <34528298+islandbitcoin@users.noreply.github.com>
Co-authored-by: Patoo <patoo@getflash.io>
Co-authored-by: Vandana <forge@getflash.io>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Dread <dread@example.com>
Co-authored-by: Patoo <262265744+patoo0x@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants