Skip to content

Qa - #2576

Merged
MayankMittal1 merged 16 commits into
masterfrom
qa
Sep 25, 2026
Merged

Qa#2576
MayankMittal1 merged 16 commits into
masterfrom
qa

Conversation

@MusabShakeel576

@MusabShakeel576 MusabShakeel576 commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

creatix and others added 7 commits September 23, 2026 20:02
qa reverted the tier-upgrade feature in the 18 reverts merged with #2561.
This branch builds on that feature, so a plain merge would delete the code
it changes and leave it importing modules that no longer exist.

This merge brings in everything else qa has gained since the branch point
(the deposit rework, the 0.03% fee notice, issuer-based deposit routing)
and keeps the feature: every path the reverts removed is back in its
pre-revert state, with this branch's refinements on top.

Built as qa, plus the exact inverse of the reverts (a1e3912..03c17c2),
plus this branch. qa's post-revert changes apply unchanged over the
restored feature. The only file both touch is constants/modals.ts, where
TIER_UPGRADE_MODAL returns beside qa's removal of OPEN_DEPOSIT_CHAIN.
store/useDepositStore.ts takes qa's changes and this branch's
upgradeTopUp slice together.

Merging this PR therefore brings the tier-upgrade feature back to qa.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E6iamcwoqjwq77x9kDCPzR
qa now opens "Select token" first from Crypto and has deleted the
"Select chain" step (a1e3912, 6ee35e0), so DEPOSIT_MODAL.OPEN_DEPOSIT_CHAIN
no longer exists. The chooser test asserted that an ordinary Crypto press
opens it, which failed once qa was merged. The soFUSE top-up test's
negative check against it passed vacuously, since it compared against
undefined. Both now name the step Crypto opens on a phone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E6iamcwoqjwq77x9kDCPzR
…p-20260923

Refine rewards tier upgrade and top-up flows
The backend now reports `legacyFuseOnly` on the membership state: whether the
user's tier rests on FUSE sitting in Savings and nothing else. That route is
being retired, and when it is switched off everyone it was carrying drops to
Core in the same instant.

The type only, so the app can be given a migration prompt without another
backend round trip. False for anyone who has also locked or subscribed —
nothing about the change can reach them, so there is nothing to warn them
about.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XGzkz4QFpEZVg6GpNH311x
Carry the legacy skip-the-line flag through to the app
@vercel

vercel Bot commented Sep 24, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
solid-app-staging Ready Ready Preview Sep 25, 2026 2:05pm UTC
1 Skipped Deployment
Project Deployment Actions Updated
solid-app Ignored Ignored Preview Sep 25, 2026 2:05pm UTC

Request Review

Comment thread lib/tierUpgrade.ts
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VhnDQhJoWUKneB9KFcZgfV
…ase-2xrzv5

Rewards v3 prod release: merge master into qa
@claude

claude Bot commented Sep 24, 2026

Copy link
Copy Markdown

Code review

No issues found. Checked for bugs and CLAUDE.md compliance.


Review scope:

  • 86 files changed implementing rewards tier upgrade flow
  • Checked for: syntax/logic errors, debugging leftovers (console.log, debugger), commented-out code, test artifacts (TODO/FIXME, .only/.skip), hardcoded secrets/config, accidental edits
  • No CLAUDE.md files present in repository

🤖 Generated with Claude Code

claude and others added 2 commits September 24, 2026 17:29
Each tier's page said "Unlocks at 5M points" and opened a "how to earn
points" sheet whether or not points could still reach that tier. Once the
ladder is retired, or for anyone the grandfather list leaves nothing to,
that advertises a route the backend refuses.

The page now follows the membership state's pointsUnlockEnabled. The
backend reports it per user: can points still take this user higher? When
that is false, the line reads "Unlocks with a membership" if a lock or an
annual charge is on sale for the tier, and is hidden otherwise. The points
sheet goes in both cases. While the state is loading, the page keeps v2's
copy.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VhnDQhJoWUKneB9KFcZgfV
…ase-2xrzv5

Drop the points copy from the benefits screen once points can't unlock
mul53 and others added 5 commits September 25, 2026 10:33
Adds a USD deposit rail paid from Cash App. Selecting USD under "Deposit
with cash" now opens a chooser — "Wire transfer, ACH" (the virtual
account) or "Cash App" — instead of going straight to the bank rail.
Outside the US the chooser is skipped, because one option is a question
with one answer.

The flow is amount → invoice → status, with the invoice screen acting as
the review step: it shows the sats, fee and delivery Orchestra actually
quoted, and nothing is charged until it is paid. There is no live quote
on the amount screen by design — /estimate prices in sats only, with no
fiat parameter at any spelling, and the app has no spot source, so a
dollar figure there could only be quoted against a rate Orchestra never
agreed to.

Everything goes through our own backend at /accounts/v1/orchestra, which
holds the Orchestra server key. The app carries no Orchestra credential:
the recipient is the Safe the server resolves from the session, and
availability — region plus allowlist — is the server's verdict, which it
enforces again on order creation rather than trusting a hidden row.

Status is streamed over SSE where EventSource exists and polled at the
documented three seconds everywhere else, which is every native build.
The poll keeps running underneath a healthy stream: frames carry only the
status, and a reconnect does not replay what was missed.

Amounts are formatted from smallest units with BigInt and grouped by
hand, because Hermes and the React Native Web Intl shim throw on a BigInt
passed to Intl.NumberFormat — Node's does not, so the tests alone would
not have caught it. There is now a test that makes Intl behave the way
the app's runtime does.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The modal header is a three-part row: a 50px back button, the title, and
the close button. The title's container had no flex constraint and the
title itself no line limit, so a long one took its natural width and
shoved both controls out of the header. They were still mounted, just
off-screen — which on the Cash App amount step left a modal with no
visible way back.

The no-title branch already used `flex-1`; the title branch never did.
It now takes the space between the two controls and truncates to one
line rather than growing past them.

Also shortens that step's own title from "Deposit with Cash App" to
"Cash App". It is reached by tapping a row already labelled Cash App,
from a screen titled "Deposit US Dollars", so the longer version was
repeating context to earn a truncation.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The config query fired on mount with countryCode still undefined. The
server correctly answers "not available" to a request that names no
region — but the amount screen read that as a verdict on the user and
bounced them to the error screen, a beat before the real answer arrived.

The hook kept its detected country in local state rather than the country
store, so every mount started at undefined and each screen re-ran the
race independently.

The cause was that "we don't know yet" and "we looked and couldn't tell"
were the same value. They are different answers: the first must not be
acted on, the second is final. The hook now reports isResolving
separately from isResolved, and every /config call waits for it.

This only ever hit users who are not on the allowlist — an allowlisted
username short-circuits the region check server-side, which is why it
survived testing on an allowlisted account.

The invoice and status screens also now pass the country, so all four
call sites share one cache entry instead of firing a second request under
['orchestraConfig', undefined]. Their amounts were never wrong — decimals
come from /v2/routes and do not depend on the country — but that entry
cached isAvailable: false, which was a trap for anyone adding a gate to
those screens later.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Gating /config on the geo lookup fixed the false "not available" bounce,
but left each screen restarting the lookup from scratch: the hook kept
its result in component state, so arriving at the invoice screen held its
config call off for a frame and flashed empty amounts.

detectGeo already memoises the request; what was missing was the settled
result. Holding it at module scope lets a later mount start with the
answer in hand — its first render is already settled, which is what the
new test asserts rather than whatever state the mount eventually reaches.

Exports a reset for tests, because module state would otherwise leak
between cases.

Also replaces the reassign-an-outer-binding pattern in this spec with
collection into an array: the react-compiler rule counts the former as a
render side-effect, and the file had been failing that rule.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@MayankMittal1
MayankMittal1 merged commit 04c43fc into master Sep 25, 2026
16 checks passed

This branch was successfully deployed

1 active deployment
Production – solid-app-staging — 44d3eb26 Deployed Sep 25, 2026 by vercel[bot]
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.

5 participants