Skip to content

Qa - #2559

Merged
mul53 merged 113 commits into
masterfrom
qa
Sep 24, 2026
Merged

Qa#2559
mul53 merged 113 commits into
masterfrom
qa

Conversation

@mul53

@mul53 mul53 commented Sep 23, 2026 •

Copy link
Copy Markdown
Collaborator

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

mul53 and others added 30 commits September 8, 2026 13:41
The Earn page showed three full-width vault cards and nothing else. This
reworks it into two sections so tokenized stocks have somewhere to live.

Vaults become a 2x2 grid of compact tiles, which makes room for a BTC tile
marked "Soon" alongside USD, ETH and FUSE. EarnVaultCard is replaced by
EarnAssetCard and deleted; its banknote backgrounds are now unreferenced.

Beneath it, an "Earn more" catalog previews five tokenized assets per
category (Popular / Tech / ETFs / Metals) and links through to Stocks. Rows
are wired to the existing useXStocksTokens and useXStockPrices hooks — no
mock data. Curated display names and sector captions live in earnCatalog.ts
because the issuer names read badly in a list ("Vanguard" for VTI, "abrdn
Physical Palladium Shares" for PALL).

The section is gated behind isDevFeatureEnabled: /stocks still redirects to
home in production, so shipping the catalog there would be a dead end.

Two deviations from the design, both for want of data. Rows show price only
— CoW's /usdPrice returns spot, and nothing in the app tracks 24h change, so
there is no honest number to put there. And the headline pill keeps the
existing "+$X today" rather than the mock's amount-plus-percent, since we
don't compute a portfolio return percentage.

Tapping a row now opens that stock's buy flow directly. BuyStockModal takes
an optional initialToken and opens on the amount step instead of the picker;
/stocks reads a ?ticker= param, resolves it, and clears the param so closing
the modal doesn't leave a link that reopens it. This also fixes an existing
bug where handleStockPress discarded its token argument, so every path into
buying started from "which stock?".

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Reworks the Earn page to the latest mockup.

Vault cards become textured half-width tiles in a 2-column grid: title,
green APY pill, and a circular coin badge over the banknote art. The BTC
"Soon" tile is gone — it isn't in this design — along with the SVG coin mark
added for it. The section heading collapses to the single "Earn interest,
Withdraw anytime" line, and the daily-earnings figure loses its pill and
sits as plain green text under the balance.

The catalog is retitled "Invest" and gains search: a circular button swaps
the category chips for an inline field that scans the whole catalog rather
than the active category. searchTokens ranks ticker prefixes above name
prefixes above substring hits, so typing "sp" surfaces SPCXx before names
that merely contain those letters. It searches the curated display name, so
"total market" finds VTIx, which upstream is only ever called "Vanguard".

Backing out of a stock's buy flow now returns to Earn. BuyStockModal takes
an onExit for the case where initialToken skipped the picker: there is no
picker behind that first step, so the caller decides where back goes. The
Stocks screen tracks whether the flow was opened by deep link and, if so,
routes both back and dismiss through router.back() — otherwise dismissing
strands the user on a Stocks tab they never chose to open.

APY labels drop the percent sign ("4.8 APY") to match the mockup.

Two notes on fidelity. The tile backgrounds are centre crops of the existing
wide banknote art, standing in for the square exports until those land —
same composition, different pattern. And rows still show price only: CoW's
BFF exposes spot price and nothing else (/tokens/{addr} and /priceChange
both 404), so there is no source for the per-row change percentage in the
design.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The five stock rows sat directly on the page background, reading as loose
items rather than a list. Wrapping them in a rounded #1C1C1C panel groups
them into a single surface. The heading, chips and "Browse all" button stay
outside it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The APY pill and coin badge were flat white/10 fills, so they sat on the
tile art rather than in front of it. GlassSurface gives both a blurred
backdrop, a diagonal light gradient, and a 1dp rim.

Web takes the blur from CSS backdrop-filter and iOS from a light-tint
BlurView. Android gets the tint and rim only: its BlurView needs a
blurTarget view, which a chip nested inside a tile has no way to reach, and
the art behind these chips is near-uniform dark, so the blur would barely
register anyway.

Border is a fixed 1dp rather than hairlineWidth, which resolves to 0.33 on
iOS against 1 on web — the rim would read at noticeably different weights.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Opening a vault from Earn was slow the first time for every tile, not just
the first. Balances and APYs are already prefetched app-wide in
(protected)/_layout, but the vault detail screen also reads APY history and
the strategy breakdown, and both key on the vault — so warming USD leaves
ETH and FUSE exactly as cold.

Earn now prefetches both for all three vaults, deferred behind
runAfterInteractions so the six requests queue after its own balance reads
rather than competing with them.

Prefetching on a key the reader does not use is wasted work that fails
silently, so the two paths now share one query-options factory instead of
repeating the key. The chart's opening period moves to
DEFAULT_HISTORICAL_APY_DAYS for the same reason: it was a bare '7' inside an
array literal, which a prefetch could not reference without guessing.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The previous surfaces were a tint and a flat rim — glassmorphism, not liquid
glass. What separates the two is refraction, and backdrop-filter has no
function that displaces pixels, so the distortion has to come from an SVG
filter: fractal noise, softened, driving a displacement map over the
backdrop.

Split into GlassSurface.web/native rather than branching inside one file,
since the two now share only their props. Native additionally gains the lit
top-left rim that web gets from inset shadows.

Only Chromium currently parses an SVG filter inside backdrop-filter, and
this has to be a feature test rather than a fallback declaration: an engine
that rejects url() throws out the whole property and takes the blur with it,
so Firefox would have ended up worse off than before. Engines that fail the
test keep blur and saturation alone.

Worth knowing for tuning: over the near-black tile art there is very little
behind these chips to bend, so at 26-52px the specular rim does most of the
work. The refraction will show more if the tile backgrounds get busier.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
On device the APY pills and coin badges came out lighter than the card they
sit on — the opposite of the design, and enough to dull the green APY text.
A light BlurView tint over near-black art blurs to an opaque grey, and the
white gradient on top compounded it.

Switches to systemUltraThinMaterialDark and drops the gradient from
0.16/0.05 to 0.10/0.02, so the surface stays dark and the lit rim carries
the glass. Rim brightened slightly to compensate for the darker fill.

iOS only. The web path composites differently — a CSS backdrop blur over
dark art stays dark, so it never had the wash and is already tuned.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Text merges its own base `text-foreground` with whatever className it is
given, and that resolution has misfired in this codebase before: see the
`brand` note in ui/button.tsx, where the label intermittently came out white
on a light background. The active chip has a white background, so the same
failure renders it as a blank white pill.

An inline style is not part of that merge and wins outright.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Tiles were hidden below a positive APY, so a vault quoting 0% vanished from
Earn. A vault at 0% is still somewhere to deposit, and hiding it makes the
grid reflow — USD and ETH side by side one moment, ETH and FUSE the next —
which is harder to build muscle memory for than a layout that holds still.

Drops shouldShowEarnVaultCard and its tests; the per-tile APY skeleton
already covers the loading case the helper was also serving.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Replaces the glass treatment on the APY pill and coin badge with flat fills
and a hairline rim, matching the reference card. The APY pill picks up a
faint green tint from its own text colour; the badge stays neutral.

Deletes GlassSurface and its web variant — nothing else used them, and the
SVG refraction and BlurView work only existed to serve these two chips.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The Earn page listed assets to buy but never what the user already owns.
Positions now sit directly under the "Invest" heading, from the existing
useXStockHoldings hook, with the holding size as the caption and its worth
(shares x live price) on the right. Tapping one opens its buy flow, same as
a catalog row.

The panel is absent rather than empty when nothing is held, so the majority
case does not get a hole between the heading and the catalog. The subtitle
moves below it, since it introduces the catalog rather than the section.

EarnAssetRow's sector/price become caption/value: the two lists differ only
in what those two slots mean, which is not enough to justify a variant.
Holdings carry no logo of their own, so they borrow the catalog's by symbol,
and their tickers join the same price query rather than opening a second.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
rounded-2xl read as square next to the chips above it. rounded-full at 52px
tall gives a 26px radius, matching the chip language on the same screen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
"Browse all" left the page for the Stocks tab. It now expands the catalog
into a scrollable list and becomes "Show less". Expanding drops the curation
and lists the whole catalog; picking a category collapses back to its
curated view, since a chip is a request for that view rather than a filter
over everything.

Only the expanded list scrolls — wrapping the short preview in a nested
scroll view would swallow drags meant for the page. Rows page in 20 at a
time as it nears the bottom, so the initial expand mounts twenty rows rather
than a hundred and sixty.

useXStockPrices moves to one query per ticker. It was keyed on the joined
list, so a growing list changed the key each page and refetched every ticker
it had already fetched: reaching the end of the catalog would have cost
roughly 20+40+60+... requests instead of one per asset. Per-ticker keys make
each page cost only its own new tickers, and the shared 60s cache means rows
revisited across categories cost nothing. Signature is unchanged, so the two
other callers are unaffected.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Tapping a held position took the user to the buy flow — offering to buy more
of something they were most likely looking to sell. Positions now pass
`action=sell`, and the Stocks screen resolves the ticker to a holding and
opens the sell modal. Catalog rows are unchanged.

The sell branch waits for holdings rather than clearing the params
immediately: they come from an on-chain balanceOf and land after the screen
mounts, so consuming the link on the first render would drop it. If they
finish loading with no match — the position is gone — the params clear and
nothing opens. Leaving a deep-linked sale returns to Earn, matching the buy
flow.

Also stops the sell screen printing "+Infinity%". Est. return divides by
avgCost, which useXStockHoldings hardcodes to 0 because a balanceOf carries
no purchase price, so the line rendered "Avg cost $0.00 · Est. return
+$641.38 (+Infinity%)". The return is now computed and shown only where
there is a basis for it, leaving the shares-available line on its own. This
was reachable before, but only via the Stocks screen; routing Earn traffic
into it made it worth fixing now.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The Invest section is now absent entirely for anyone holding nothing, rather
than rendering with an empty positions slot. It stays hidden while the
holdings read is still in flight, since appearing and then vanishing reads
worse than arriving a beat late.

Adds "Your positions" under the heading, mirroring how "Buy, sell and use as
collateral" introduces the catalog below it, and drops the now-redundant
holdings guard around the panel.

Worth flagging: this removes the only route from Earn to buying a first
stock. Someone with no positions sees no Invest section at all, so discovery
depends entirely on the Stocks tab.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…-positions

Show Invest only to holders, and label the positions list
Gating the whole Invest section on holdings left anyone with a zero xStock
balance looking at an Earn page that ends after the vault tiles — no heading,
no catalog, no way in. The section that sells you your first stock was only
visible once you already owned one.

The escape hatch the gate assumed — "discovery depends entirely on the Stocks
tab" — does not exist on qa. The desktop sidebar's NAV_ITEMS is a hardcoded
Wallet/Earn/Rewards/Activity, and the redesigned bottom bar reads
WHITELIST_TAB_NAMES, which is index/earn/rewards. Neither lists Stocks, so
/stocks is reachable only by typing it.

So the gate moves off the section and onto the positions panel, which is the
part that actually has nothing to say when you hold nothing. The panel keeps
its isHoldingsLoading hold-back so it doesn't appear and then vanish; the
catalog is no longer behind that, because it has nothing to wait for and a
164-token mainnet multicall is a slow thing to block a page on.

One consequence worth naming: useXStockHoldings defaults data to [] on error,
so a failed multicall is indistinguishable from holding nothing. Before this
it silently hid the whole section from a genuine holder. Now it costs them
only the positions panel.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…alog

Show the Invest catalog to everyone, not just existing holders
Every savings entry point — the header and mobile Deposit buttons, the
empty state, the funded action row, Start earning and the home balances
dropdown — opened "Deposit to savings" first: a token list whose only
route to the amount form was the "Move from wallet" row buried under it.
Point them straight at the form with depositFromSolid set, so the first
screen is the one that takes an amount.

The form already knows how to say there is nothing to deposit, but it
read an unloaded balance as an empty one. As a step behind the token list
that flashed by; as the entry point it would greet most users with the
empty state before their balance arrived. Hold the decision until
balances have loaded.

The card's minimum-deposit step still opens the token list: it funds
savings by sending new money in, which the form does not do.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The Destination dropdown is the only thing that reads `vaultName`, so
"USDC / FUSE / ETH Savings" become "USD / FUSE / ETH Yield" to match the
design.

The deposit form had no back arrow because it had nowhere to go: as the
savings entry point, back and close were the same action. Point it at the
landing screen for the vault being deposited into instead, so it lands
somewhere whether the flow was opened from that vault's own screen or
from the home balances dropdown.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
"Add funds" opened the deposit-methods list; the screen it belongs on
says the wallet holds nothing to deposit, so the wallet-funding entry is
the step that follows.

Clear depositFromSolid on the way out. Left set, the steps behind that
screen still resolve to the vault deposit form rather than the
add-funds-to-wallet one, and skip the wallet provider an external deposit
needs.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…-redesign-qa

Claude/deposit vault screen redesign qa
"Add funds" used to open one long list of every funding method. It now
opens a short "Deposit with" fork — Crypto or Cash — and each branch owns
the methods underneath it:

- Crypto -> "Receive crypto": the deposit address, plus connect wallet on
  desktop (thirdweb's connect modal is desktop-only, so a phone is offered
  the address alone).
- Cash -> "Deposit with cash": USD through the virtual account, the local
  currencies through the onramp.

Being a fork and nothing else is what lets the first step be a drawer on a
phone: ResponsiveModal gains `mobilePresentation="drawer"`, backed by a new
`nativePresentation` on DialogContent so iOS and Android anchor to the
bottom the way web-mobile already could. Desktop is unchanged — both
presentations are the usual centred modal there. The later steps keep the
full-height sheet.

The deposit address screen is rebuilt around chain and currency pills. The
address is the user's Safe and is the same on every chain, so the pills do
not change it: they decide which currencies are on offer, what the minimum
transfer is, and which chain the QR is labelled for. Minimums live in one
table in WalletDepositAddress/constants.ts — Ethereum asks for more because
crediting a deposit costs gas on the source chain, and assets that are not
worth ~$1 a unit carry their own override.

Also narrows the disconnect reset in useDepositOption. It returned the
modal to step 1 whenever thirdweb reported `disconnected` — the default
state on desktop for anyone who has never connected a wallet — for every
step not on a hand-maintained exclusion list, which the new screens would
have been ejected from immediately. It now names the three steps that
actually read the connection, so bank transfer, the onramp and the deposit
address stay put.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Design QA on the deposit address screen:

- The QR is clipped to a 20px radius instead of ending in hard corners. It
  gets a quiet zone to make room for that: the library keeps the element at
  `size` and scales the modules down, so the radius bites into background
  rather than into a finder pattern, which a scanner has to be able to
  locate.
- A hairline divider separates the QR from the address. The card's inset
  moved from the card onto each half so the divider runs its full width.
- The block under the card gained the padding the design gives it: 24px
  between the card, the copy and the buttons, and "Learn about deposits"
  joined the copy it belongs to rather than floating between the two.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…el, and default deposits to Fuse"

This reverts the rewards files of commit a92006e.
The deposit-address default to Fuse/USDC stays: the wallet deposit flow now
builds on it.
…ng-f894ef

Claude/qa crypto stock hiding f894ef
mul53 and others added 2 commits September 23, 2026 16:56
…ddresses

A crypto deposit is free when it is sent on the chain its destination lives
on, and pays 0.03% from any other chain. Where that is depends on the card
program and on what the deposit funds:

- Wirex card: free from Fuse, where the Safe the card spends from lives.
- Wirex savings: free from every chain, for Phase 1.
- Rain or no card, card: free from Base, where the Rain card is funded.
- Rain or no card, savings: free from the vault's chain - Ethereum for
  soUSD and soETH, Fuse for soFUSE.

The rule lives in lib/utils/depositFee.ts, tested row by row against that
table. DepositFeeNotice shows the line under the "Fund your card" and savings
deposit addresses, and only on the chains that pay it.

The wallet deposit address is deliberately left alone: it sends to the user's
Safe on the chosen chain, which the table does not cover.

Display only - the client collects nothing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ss-2c9cb3

feat(deposit): warn about the 0.03% fee on card and savings deposit a…
mul53 and others added 6 commits September 23, 2026 19:04
…down

Four from review of the deposit address screen.

The currency pill leads and the network follows. The currency is what is
picked first and what the minimum is quoted in, so it is the one the screen
is about.

The network is a dropdown again, not a step. Sending it to a full screen
made changing a network feel like starting the flow over, when it is an
adjustment to the screen you are already on. It lists only the chains
carrying the chosen currency, so a change of network can never quietly
change the currency too. That leaves "Select chain" unreachable, so it is
deleted along with its modal state rather than left as a screen nothing
opens.

The dropdown is a plain View rendered as the screen's last child — the two
things that made it work last time. An `Animated.View` does not resolve
nativewind's `className`, so wrapping the card drops its background; and
paint order, not `zIndex`, is what keeps it over the QR.

"Select token" no longer arrives with a currency ticked. It is the first
step, so nothing has been chosen yet and the tick read as a decision already
made for the user. Coming back to change it still shows the current one. Its
order is now explicit — USDC, USDT, ETH, then the rest — where deriving it
from chain order had floated FUSE and WFUSE above USDT and ETH purely
because Fuse is the default chain.

Also raises the Fuse floor to 500 FUSE, matching the backend's fallback.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The redesigned deposit flows went out with Wirex, whose cardholders have
never seen anything else. Rain cardholders have years of habit in the older
screens, and their money lands somewhere different — a prefunded card rather
than a Safe — so moving them is a change of behaviour, not just appearance.
They stay where they are until that is a decision someone makes on purpose.

Which design a cardholder sees is one predicate, `usesNewDepositDesign`,
sitting beside `canDepositToCard` because it answers a neighbouring question
about the same issuer. An unresolved issuer reads as Rain, matching its
neighbour, so a slow query never flips someone onto the other design for a
frame.

Card deposits: every entry point that reached the redesigned funding flow —
the wallet action bar, the balance breakdown on both platforms, and the card
action row — now opens the older screens through CardDepositModalProvider.
Wirex is untouched; its modal was already the new design.

Vault deposits: the savings buttons pick their flow through
`useVaultDepositEntry`. The two are not skins of one screen — one sends new
money to a minted address, the other moves a balance already in Solid — so
the hook primes the state each needs rather than leaving callers to
remember.

Also fixes the address the card's external-wallet route hands out. It was
the card's funding contract, which takes a transfer but which nothing
watches: the pipeline resolves deposits against the addresses it mints, so
anything sent there is credited by hand, with no activity for the user to
follow. It now mints a direct-deposit session like the redesigned flow does,
and says so plainly when it cannot rather than falling back to the contract.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The previous commit had this the wrong way round, and the reasoning it gave
for it was wrong too. Which flow a cardholder gets follows from where their
money has to land, not from which design is newer:

- A Wirex card holds no balance. Settlement takes the stablecoin from the
  Safe, so funding the wallet IS funding the card, and a separate
  card-funding flow is the same destination under another name. They get
  "Deposit with" — the wallet flow — from the wallet action bar and from the
  card's own Add funds.
- A Rain card is prefunded and separate from the Safe. Its money has to land
  on the card itself, which is what "Fund your card" does and what the wallet
  flow would not, so they keep it.

That also settles the deposit address: the Rain flow already mints one
through `createDirectDepositSession` against the card destination, so the
address on its show-address step is the direct-deposit address with nothing
further to change. The previous commit's attempt at that sat in a branch of
the legacy form that nothing can reach — the only thing setting that source
navigates to a different component — so it went with the rest of the revert,
along with the hook written for it.

`usesNewDepositDesign` stays, now saying why rather than which is newer, and
the savings deposit keeps reading it: Wirex on the direct-deposit flow, Rain
on the amount form.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Rain now opens the direct-deposit flow — currency, chain, address — and
Wirex the amount form that moves a balance already in Solid.

Renamed with it, because "new design" was never what this decided. The two
flows put money in by different routes, so the choice is what the button can
do: a Rain cardholder's wallet, card and savings are three separate pots, and
the savings button is their only direct way to get new money into the vault.
A Wirex cardholder funds savings by funding their wallet — settlement draws
on the Safe — so the wallet flow already covers that, leaving this button for
the thing it does not do.

Users with no card, and anyone whose issuer has not resolved yet, fall on the
Rain side: the same default `canDepositToCard` takes.

Note this leaves a Wirex cardholder no route from the savings screens to fund
savings from outside. Their wallet deposit reaches the same balance, so the
money can still get there, but not from the button they are looking at.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The screen handed out the Safe address for every currency. Stablecoins now
get an address minted by `createDirectDepositSession`, because that is the
one the pipeline watches: a transfer to it is detected, credited and recorded
as activity, where a transfer to the Safe is none of those things.

ETH, WETH, FUSE and WFUSE keep the Safe. Nothing mints an address for them
and nothing would watch it — they land in the Safe and stay as the token that
was sent — so there the Safe is the right answer rather than a fallback.

The destination is `RAIN_CARD`, which is historical naming rather than a
misrouting: the backend resolves the issuer and delivers to a Rain card or to
the cardholder's Safe accordingly. Everyone who reaches this screen is on the
Safe side of that — a Wirex cardholder, or someone with no card — because
Rain cardholders are routed to "Fund your card" and never see it.

A failed mint now says so instead of falling back to the Safe, which would
hand out an address the deposit would be stranded on. And "Scanning for
deposits" is limited to the minted addresses, since that poll is what watches
them; over the Safe it was describing a request that could never come back
positive.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ns-bb5155

Claude/wallet deposit screens bb5155
mul53 and others added 2 commits September 24, 2026 08:04
…its issuer

Three from review of the 0.03% deposit fee line.

The wallet deposit screen now shows it. Wirex cardholders fund their card
through that screen now rather than "Fund your card", so it is where their
fee has to be quoted: on stablecoins, from every chain but Fuse. ETH and
FUSE are sent straight to the Safe and are never charged, and nor is anyone
without a card.

"Fund your card" always quotes the Rain rule - free on Base, charged
everywhere else - because every entry point to it is Rain's. The screen used
to ask the issuer query which rule to apply, so the Wirex rule (charged on
Base, free on Fuse) could show on a screen both issuers' modals render. Each
modal now names its issuer instead.

Savings deposits keep the vault's chain as the free one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
fix(deposit): quote the fee on the wallet flow, and tie each flow to …
mul53 and others added 2 commits September 24, 2026 11:37
The stablecoin rule was applied to everyone. It is a Wirex rule: the address
is minted against the card destination and the backend resolves that by
issuer, so for a Wirex cardholder it delivers to their Safe on Fuse — the
balance their card settles from — while for a Rain cardholder it would
deliver to the card, and for someone with no card there is no issuer to
resolve at all. Everyone but Wirex now sees the Safe address for every
currency, as they did before.

This also closes the gap noted when that change went in. The savings flow's
"Deposit from an external wallet" route reaches this screen without checking
the issuer, so a Rain cardholder could get there — and with the rule applied
to all, their stablecoins would have landed on their card instead of their
wallet. The check now sits on the address itself rather than on the routes
into it, so no entry point has to remember.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ns-bb5155

fix(deposit): mint the address for Wirex cardholders only
@mul53
mul53 merged commit bb007e5 into master Sep 24, 2026
16 checks passed

This branch was successfully deployed

1 active deployment
Production – solid-app-staging — b34b382b Deployed Sep 24, 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.

3 participants