Skip to content

fix: link card deposit Explorer row to the real USDC transfer tx - #2157

Open
MusabShakeel576 wants to merge 349 commits into
masterfrom
claude/vigilant-einstein-qwqz0j
Open

fix: link card deposit Explorer row to the real USDC transfer tx#2157
MusabShakeel576 wants to merge 349 commits into
masterfrom
claude/vigilant-einstein-qwqz0j

Conversation

@MusabShakeel576

Copy link
Copy Markdown
Contributor

No description provided.

MayankMittal1 and others added 30 commits April 16, 2026 22:41
The card purchase activity header used justify-between, which offset
the merchant title based on the ChevronLeft width, and the location
was pinned with ml-10. Absolutely position the back button so the
title truly centers, and drop the spacer. Also center the location
text and trim merchant_name/description so stray whitespace from the
upstream data doesn't misalign the header.
…us-W0JQd

Add escrow status and countdown timer for cashback releases
…splay-xL5yx

Improve transaction detail header layout and merchant name handling
…rkflows

Rename useDepositFromEOA -> useDepositFromWallet and add a category prop
('SAVINGS' | 'CARD'). The CARD branch is sponsor-only, uses TransactionType.CARD_DEPOSIT,
and calls createDeposit/bridgeDeposit with category='CARD' so the backend
Connect-Wallet workflows route funds to the Rain Base funding address instead
of the Ethereum vault.

Wire the new hook into CardDepositInternalForm for the production WALLET source
so 'Deposit to card from wallet' works from all 5 supported chains.

https://claude.ai/code/session_01TDpHy9uTrVX3PTRp9xS9S5
…Amount

Replace the hardcoded '100' minimum amount passed to useDepositFromWallet
in CardDepositInternalForm with EXPO_PUBLIC_MINIMUM_SPONSOR_AMOUNT from
lib/config, matching how the Savings flow sources the same threshold.

https://claude.ai/code/session_01TDpHy9uTrVX3PTRp9xS9S5
Move the CARD category support off the EOA-pull hook and onto
useDepositFromSolidUsdc so card deposits pull from the user's Solid
Turnkey Safe AA (the account is the approver and allowance-granter),
matching the rest of the new deposit flow.

- Revert the useDepositFromEOA rename; restore the original hook as
  unused dead code and revert DepositToVaultForm to import it again.
- useDepositFromSolidUsdc: add category: 'SAVINGS' | 'CARD' prop. When
  category=CARD: target chain is Base, activity type is CARD_DEPOSIT,
  and createDeposit / bridgeDeposit are called with category='CARD'
  so the backend routes funds to the Rain Base funding address.
- CardDepositInternalForm: production WALLET path now uses
  useDepositFromSolidUsdc(USDC, EXPO_PUBLIC_MINIMUM_SPONSOR_AMOUNT, 'CARD').

https://claude.ai/code/session_01TDpHy9uTrVX3PTRp9xS9S5
…Category enum

Add DepositCategory enum to lib/types.ts mirroring the backend enum, and
use it in place of the string-literal union in:
- Deposit / BridgeDeposit request types (lib/types.ts)
- useDepositFromSolidUsdc category prop + comparisons + API passes
- CardDepositInternalForm's category argument

https://claude.ai/code/session_01TDpHy9uTrVX3PTRp9xS9S5
…ins-earUz

Add card deposit support to wallet deposit hook
Addresses two issues in the card deposit from Solid wallet flow:

1. When useDepositStore.srcChainId is 0/unset (e.g. after resetDepositFlow
   persisted an unset value), useDepositFromSolidUsdc silently returned
   undefined from deposit(), so the form opened the transaction-status
   screen with no approve UserOp ever submitted and no Turnkey passkey
   prompt. The hook now throws a clear error when srcChainId is falsy,
   and CardDepositInternalForm defaults srcChainId to Base (the card
   funding chain) whenever the stored value is missing or unsupported.

2. Card deposit form now enforces EXPO_PUBLIC_MINIMUM_SPONSOR_AMOUNT via
   a zod refine on the WALLET source (disables the submit button and
   surfaces a validation message) and shows a gasless deposit hint row
   under the destination mirroring DepositToVaultForm's Fuel-icon line.

https://claude.ai/code/session_01TDpHy9uTrVX3PTRp9xS9S5
…ins-earUz

fix(card-deposit): prevent silent no-op and add minimum-amount UI
Extract the reusable wallet-token picker from SavingsDepositTokenSelector
into a shared WalletTokenSelectorScreen component that accepts filter
props (supportedChainIds, supportedTokenSymbols, onSelect, empty text)
so it can be reused unchanged by the card deposit flow.

- New components/WalletTokenSelector/WalletTokenSelectorScreen.tsx hosts
  the aggregation + filter + render logic previously inlined in the
  savings selector.
- SavingsDepositTokenSelector becomes a thin wrapper that passes the
  active vault's supportedChains / supportedTokens and navigates back to
  DEPOSIT_MODAL.OPEN_FORM on select. No behavioural change.
- New components/Card/CardDepositTokenSelector.tsx wraps the same screen
  with a USDC + 5-chains filter and navigates back to
  CARD_DEPOSIT_MODAL.OPEN_INTERNAL_FORM.
- constants/modals.ts adds CARD_DEPOSIT_MODAL.OPEN_TOKEN_SELECTOR.
- CardDepositModalProvider renders CardDepositTokenSelector when the new
  state is active and wires up the Select-token title + back-button
  routing to the internal form.
- CardDepositInternalForm replaces the earlier "default to Base" fallback
  with a WalletTokenButton that opens the selector. The deposit button is
  disabled until the user picks a token, and the selected token is shown
  in the button label (symbol + chain name).

https://claude.ai/code/session_01TDpHy9uTrVX3PTRp9xS9S5
…ins-earUz

refactor(card-deposit): reuse savings wallet token selector screen
Drop the separate "Token" row and surface the wallet token selector in
the same flex-row as the amount input, matching the savings deposit
modal layout.

AmountInput now accepts an optional rightSlot prop; CardDepositInternalForm
passes WalletTokenButton as the slot when the WALLET source is active on
production, replacing the static USDC/USDC.e image + label.

https://claude.ai/code/session_01TDpHy9uTrVX3PTRp9xS9S5
… WALLET source

- CardDepositTokenSelector was calling useDepositStore.setModal (the savings
  modal store) when a token was selected, which routed the user to the
  savings-flow's Add Funds screen instead of the card deposit internal form.
  Switch to useCardDepositStore.setModal so the card modal transitions back
  to OPEN_INTERNAL_FORM, and set CardDepositSource.WALLET on select so the
  form re-mounts on the Wallet option.
- CardDepositModalProvider's back button from the token selector also now
  explicitly sets source=WALLET before navigating back, so the internal
  form's defaultValues.from reads WALLET (not the BORROW default).

https://claude.ai/code/session_01TDpHy9uTrVX3PTRp9xS9S5
…ins-earUz

fix(card-deposit): render WalletTokenButton inline inside amount input
Adds "External Wallet" as a fourth option in the card deposit dropdown
(after Borrow against Savings, Savings, and Wallet). When selected, the
card funding address is rendered via the reusable DepositPublicAddress
screen from the Add funds modal with a Base chain USDC description that
links to the token contract on Basescan.

- Extend DepositPublicAddress with optional `address` and `description`
  props so the "Your Solid address" screen can be reused for arbitrary
  deposit addresses.
- Render the address screen inline in CardDepositInternalForm when the
  EXTERNAL source is chosen, hiding amount input, destination, error
  and submit UI for that mode.
…deposit-AtDYW

Add external wallet deposit option to card funding
Production "From Wallet" card deposit previously:
- Read USDC balance from the Fuse-hardcoded USDC_STARGATE address, so
  users who picked Ethereum/Polygon/Base/Arbitrum USDC in the token
  selector always saw 0 balance and the Max button pasted 0.
- Displayed "USDC.e" as the symbol on every chain, but the ".e" suffix
  only applies to Fuse's Stargate-bridged USDC; the other four chains
  use canonical USDC.

Read the balance from the Solid Safe AA on the chain picked in the
selector (falling back to the legacy Fuse USDC_STARGATE read when the
user hasn't picked a token yet), and show "USDC.e" only when the
selected chain is Fuse, otherwise "USDC". Consolidate the duplicate
cardDepositSrcChainId/tokenAddress derivations onto the single
balance-read source of truth.

https://claude.ai/code/session_01TDpHy9uTrVX3PTRp9xS9S5
…ins-earUz

fix(card-deposit): balance + symbol track the selected wallet chain
The unconditional <View className="flex-1" /> spacer was rendered as a
sibling of the EXTERNAL branch's own flex-1 content wrapper, creating
two stacked flex-1 children in the column. The empty sibling overlaid
the address card and intercepted pointer events, making the "See token
address" link unclickable. Restrict the spacer to non-external modes.
…deposit-AtDYW

fix(card-deposit): hide flex spacer when external wallet is selected
Wrap ResponsiveModal's ScrollView in a KeyboardAvoidingView so the
keyboard pushes modal content up instead of obscuring the focused input
(e.g. the Two-Factor Authentication code entry).

https://claude.ai/code/session_01MGVdxsdT6kXH5nPgJJFLJo
Replace the inline chevron-left Pressable in the Settings mobile header
with the shared BackButton component, and switch BackButton's icon from
ArrowLeft to ChevronLeft so it matches the existing chevron style while
gaining the rounded-circle background.

https://claude.ai/code/session_015abE3yneWZwkkzqAsvp7su
Settings mobile header still uses BackButton, which now renders its
original ArrowLeft icon with the rounded-circle background.

https://claude.ai/code/session_015abE3yneWZwkkzqAsvp7su
…omponent-nmVLt

Replace custom back button with BackButton component
Restores the single-user fallback in selectSelectedUser that was removed in
f8e23ec. Without it, the TurnkeyProvider has no allowCredentials on the
welcome page after logout (no user is marked selected), so the authenticator
prompt shows every registered passkey instead of just the one matching the
button the user pressed.

With the fallback, the lone stored user is treated as selected for the
purpose of passkey filtering, and its credentialId flows into
TurnkeyProviderKit's passkeyConfig.allowCredentials so only that passkey is
offered in the system prompt.

Regression originally fixed in 5584266 / 63564d5 and reintroduced by
f8e23ec.

https://claude.ai/code/session_017pCtNrFfjv1ge1t1LiVBSA
…ut-rWhty

Wrap modal content with KeyboardAvoidingView for better keyboard handling
claude and others added 27 commits June 7, 2026 09:43
…nder review

For a Didit under_review user (e.g. held due to a duplicate device/IP), step 1
of /card/activate now lists the attached warning reasons instead of a generic
"being reviewed" message.

- The under_review step-1 description surfaces the attached warnings
  ("Your application is under additional review: ...") with a fallback to the
  generic "few minutes" copy when there are none. (qa lacked this branch.)
- REJECTED / INCOMPLETE / under_review branches key off the formatted result so
  a text-less warning set no longer renders an empty bullet list.
- Add DUPLICATED_DEVICE_FINGERPRINT (+ duplicate IP/device) to the friendly map.

https://claude.ai/code/session_01S2muieprRUo3BGSh8yKBtR
…nings

Surface Didit KYC warnings on step 1, including under-review
Web never sent any Amplitude events because initAnalytics is gated behind
splashScreenHidden, which is only flipped by onLayoutRootView. That
onLayout was attached to SafeAreaProvider, which does not forward an
arbitrary onLayout prop to its underlying view on web (it only wires its
own inset measurement), so the callback never ran and analytics was
never initialized. Native works because the native SafeAreaProvider does
forward onLayout. Move onLayout to GestureHandlerRootView, which passes
it through to a react-native-web View (fires via ResizeObserver) on web
and lays out normally on native.

Also gate trackScreen behind a new analyticsReady flag set once
initAnalytics resolves, so the first (landing) Page Viewed fires after
init instead of being queued before the Amplitude proxy serverUrl is
configured and flushed to the wrong endpoint.

Enrich the Amplitude Page Viewed event the same way track() does:
extract a shared enrichEventParams() helper and use it for screen
tracking so UTM/attribution fields land as top-level, queryable
properties (utm_source, utm_campaign, ...) and route params are
flattened instead of nested under a `params` object. Kept screen
tracking on the Amplitude-only path to avoid double-counting in
Firebase/GTM.

https://claude.ai/code/session_01KR9AG8hkYVigCMsQmDKGS2
Refactor analytics enrichment and fix screen tracking initialization
Removes the "Get X% bonus for deposit" borrow-deposit banner from both
the desktop and mobile card details layouts, along with its now-unused
imports and styles. The desktop borrow position card now spans the full
row width.

https://claude.ai/code/session_01V7s9vkb32mh8HHBravEB5G
Remove DepositBonusBanner component from card details
The savings rate chart (savings page) and token price chart (coins/[id]
page) tooltips showed a period-over-period percentage change next to the
date. Remove that change indicator from both the web (recharts
ChartTooltip) and native (AreaChart) tooltips so they only display the
value and the date.

The selectedPriceChange store update is kept so the coin detail header's
price-change indicator continues to work.

https://claude.ai/code/session_01KE3gjUzyjkEN8ntFCkDTNC
Remove price change percentage display from chart tooltips
- expo-insights (~55.0.15): zero-config launch/usage analytics for the
  EAS dashboard; events are sent automatically by the native module.
- expo-observe (~0.2.2, the SDK 55-compatible release): startup and
  performance metrics (time to first render, time to interactive,
  launch times) dispatched to EAS Observe.

Integration details:
- lib/observe.ts guards the expo-observe import so OTA updates reaching
  binaries built before these native modules were added fall back to
  no-ops instead of crashing at startup (runtimeVersion policy is
  appVersion).
- Root layout is wrapped with AppMetricsRoot (SDK 55 equivalent of
  ObserveRoot) to record first render, configure() runs at module load
  with the app environment, and markInteractive() fires once the splash
  screen is hidden.

No Expo SDK upgrade required: both packages support SDK 55, so expo
stays at ~55.0.12.

https://claude.ai/code/session_01TiJbAYJFJyaHTe9D7A1JmK
- lib/__tests__/observe.test.ts locks in the contract that the EAS
  Observe wrapper never throws when the native module is unavailable.
- Inline eslint disable for the intentional require() in lib/observe.ts,
  matching the existing convention in lib/assets.ts and
  lib/utils/cardDetailsReveal.ts.

https://claude.ai/code/session_01TiJbAYJFJyaHTe9D7A1JmK
Add EAS Observe integration for startup performance metrics
Replace the gray placeholder box on the unprovisioned agent onboarding
state with the agent-wallet product image. Adds the asset to the registry
in lib/assets.ts and renders it via expo-image with contentFit="contain".

https://claude.ai/code/session_01AZ7i9i1YACRWSgDposkNPF
Surface a Passkey FAQ popup from the onboarding login-help prompt. The
prompt now reads "Have trouble logging in? See our Passkey FAQs or Recover
your account >", where "Passkey FAQs" opens the popup.

- Add constants/passkey-faqs.tsx with the passkey FAQ content
- Add PasskeyFaqModal built on the existing ResponsiveModal + FAQ components,
  reusing the existing vault Lottie asset for the header graphic
- Wire the modal into both the mobile and desktop onboarding layouts

https://claude.ai/code/session_01C4Jn2RkCUkojpkS83nynmg
Add agent wallet image to agent setup screen
The desktop Manage dropdown rendered its menu as an absolutely-positioned
view inside the sticky header. Trapped in the header's stacking context,
the menu was painted beneath the card art, hiding the dropdown items.

Replace the hand-rolled dropdown with the portal-based DropdownMenu
(rendered at the root PortalHost), matching the existing TransactionDropdown
pattern. Portal content paints above all page content, so the menu now
appears over the card art. This also drops the manual outside-click
handler in favor of the primitive's built-in dismiss behavior.

https://claude.ai/code/session_01EQ1VUeY2NyNkPANuzfn5Sc
Refactor card details dropdown to use DropdownMenu component
Swap in the new gradient agent-wallet.png and render it as a background
layer behind the onboarding card instead of an inline image (the exported
art had too much empty space above/below when shown inline).

- Container is now relative + overflow-hidden so the background respects
  the rounded corners.
- Image is absolutely positioned, top-aligned, full-width at h-[400px]
  with contentFit="contain" + contentPosition="top" (top-center).
- A 300px spacer View reserves the image area; title/description/button
  render on top.

https://claude.ai/code/session_01AZ7i9i1YACRWSgDposkNPF
- Widen modal to 40rem and reduce desktop padding from 2rem to 1rem
- Drop the gaps above the title (close->vault, vault->title) since the
  vault Lottie already includes top/bottom spacing; keep the gap between
  the title and the FAQ list

https://claude.ai/code/session_01C4Jn2RkCUkojpkS83nynmg
Use gradient agent-wallet image as onboarding card background
Add USDT to the Base and BNB Chain bridge token lists so it appears in the
Add Funds and Deposit to Savings token selectors (which render from
BRIDGE_TOKENS), matching the backend bridge-deposit support:
- Base: Tether USD 0xfde4C96c8593536E31F229EA8f37b2ADa2699bb2 (6 dp)
- BNB Chain: Binance-Peg BSC-USD 0x55d398326f99059fF775485246999027B3197955 (18 dp)

Derive token decimals from the bridge config in the Add Funds wallet-transfer
flow (form + useTransferToWallet) instead of hardcoding stablecoins to 6 dp, so
the 18-decimal BNB Chain stablecoins (USDC/USDT) transfer and display correctly.

https://claude.ai/code/session_012jF7BVuvcndnadBoePaAWG
Support 18-decimal stablecoins on BNB Chain
The activity list re-derives every successful BRIDGE_DEPOSIT's displayed
status from a LayerZero delivery check plus a card-transaction lookup that
matches crypto_transaction_details.tx_hash. Only the deprecated Bridge
card API returns that field — Rain card transactions carry no crypto tx
details — so for Rain users the check could never pass and every deposit
to card rendered as "Pending" forever (and was then hidden as a stuck
transaction after 24h), even when the activity was already SUCCESS in the
store and on the activity detail page.

Gate the override to the Bridge provider: Rain rows now trust the activity
status, which the backend completes from Rain collateral webhooks. Also
skips the now-pointless layerzeroscan polling for Rain users.
Addresses four card-deposit activity issues, all Rain-era:

- Instant SUCCESS: useProcessingActivitiesPolling / useTransactionReceiptPolling
  marked any PROCESSING activity SUCCESS as soon as its SOURCE-chain receipt
  mined. Stargate card deposits (BRIDGE_DEPOSIT, BORROW_AND_DEPOSIT_TO_CARD)
  confirm on Fuse in seconds but take minutes to bridge, so they flipped to
  SUCCESS before the funds arrived. Exclude cross-chain card-deposit types from
  source-receipt promotion (new isSourceReceiptFinalizable); they now finalize
  via the Rain collateral webhook (destination confirmation).

- "External wallet transfer" mislabel: BRIDGE_DEPOSIT is dual-use (real bridges
  AND "Deposit … to Card"), but the static category map labels it
  EXTERNAL_WALLET_TRANSFER. Add getTransactionCategory to relabel the card
  variant (title contains "Card") as "Card deposit", used by both the list row
  and the detail page.

- Duplicate row: a connect-wallet card deposit creates two card_deposit
  activities — the frontend's trackingId and the backend workflow's
  `${trackingId}_card`. Collapse them in deduplicateTransactions (keeping the
  row with the on-chain hash), mirroring the savings two-step special-case.

- "U" placeholder icon: USDC.e (borrow-and-deposit-to-card symbol) missed the
  'USDC' case in getTokenIcon (toUpperCase → 'USDC.E') and fell back to the
  DefaultTokenIcon placeholder on the detail page. Map USDC.E to the USDC icon.
A connect-wallet (Wallet source) card deposit shows twice: the frontend
card_deposit activity plus a Blockscout/Alchemy-synced "Send USDC" row for
the on-chain USDC transfer. They carry different tx hashes (the transfer vs
the approve userOp), so hash dedup misses them, and the existing second-pass
SEND removal only matched BRIDGE_DEPOSIT / CARD_TRANSACTION — not CARD_DEPOSIT.

- Introduce CARD_DEPOSIT_ACTIVITY_TYPES (adds CARD_DEPOSIT and
  BORROW_AND_DEPOSIT_TO_CARD) and use it in isCardDeposit, the collision
  priority map, and the second-pass SEND removal, so the redundant Send is
  dropped for every card-deposit type.

- The surviving card_deposit had only a hash (no url), so the detail page's
  Explorer row (which required url) was hidden. Derive the explorer URL from
  hash + chainId when url is missing, so the deposit always links on-chain.
The card_deposit row's own hash is the approve userOp for connect-wallet
deposits, not the actual transfer, so the detail page's Explorer link pointed
at the approve. Add resolveCardDepositTransferTx to find the sibling on-chain
USDC transfer (the Blockscout-synced Send, removed from the list but still in
cachedActivities) by matching toAddress + chain within 5 minutes, and link the
Explorer row to that tx's hash + url instead. Falls back to the activity's own
hash (deriving the url from chain) when no sibling transfer exists.
@vercel

vercel Bot commented Jun 16, 2026

Copy link
Copy Markdown

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

2 Skipped Deployments
Project Deployment Actions Updated (UTC)
solid-app Ignored Ignored Jun 16, 2026 4:38pm
solid-app-staging Ignored Ignored Jun 16, 2026 4:38pm

Request Review

Base automatically changed from qa to master August 13, 2026 11:12
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