Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
|
||
| // Visible tabs - these are the main navigation tabs | ||
| const VISIBLE_TAB_NAMES = ['index', 'savings', 'card', 'activity']; | ||
| const VISIBLE_TAB_NAMES = ['index', 'savings', Platform.OS === 'ios' ? null : 'card', 'activity']; |
There was a problem hiding this comment.
Array contains null value on iOS
Low Severity
The VISIBLE_TAB_NAMES array includes null when Platform.OS === 'ios', creating an array like ['index', 'savings', null, 'activity']. While this doesn't cause runtime errors (since route.name won't match null), it's semantically incorrect and adds unnecessary overhead. The array should either be constructed conditionally or filtered with .filter(Boolean) to remove falsy values.
| > | ||
| <Text className="text-base font-bold text-black">Done</Text> | ||
| </Button> | ||
| )} |
There was a problem hiding this comment.
Minimum withdrawal threshold traps user funds
High Severity
The withdraw button now only appears when parseFloat(availableBalance) >= 1, replacing the previous behavior where the button was always visible but disabled when balance was zero or less. Users with balances between $0 and $1 (e.g., $0.50) cannot withdraw their funds because the withdraw button is hidden and replaced with a "Done" button that only closes the modal, effectively trapping their money.
…2DeY Make welcome card image cover full upper section of popup
Throttled info-level captureMessage emits the raw vs filtered Arbitrum token counts, symbols, addresses, fetch status, and whitelist size each time tokenBalances runs. Goal: identify whether Arbitrum USDC fails to show on native because the fetch returns empty, the whitelist filter drops it, or something downstream. Always emits on fetch rejection; otherwise throttled to 1/min per session. Sentry only enables in production (lib/_layout.tsx Sentry.init), so this needs a prod build to surface events. TODO: revert once diagnosis is complete.
…nfo on native React Query's default focusManager only listens to `visibilitychange`, which never fires on React Native. Its onlineManager has no native transport at all. Consequences with the current useBalances config: - `refetchOnWindowFocus: true` is a no-op when the app returns from background, so reopening the app doesn't refresh balances — users wait for the next 5s `refetchInterval` tick, or longer if JS was paused while backgrounded. - `refetchOnReconnect: true` is a no-op after a network drop. Bridge both managers in the root layout: AppState → focusManager, NetInfo → onlineManager. NetInfo is already a direct dependency. Web behavior is unchanged.
…splay-ktdN0 Wire React Query to native app lifecycle and connectivity
…ass backend validation
The activity API's CreateActivityDto enforces /^[A-Z]{2,10}$/ on `symbol`
(after .toUpperCase()), so 'USDC.e' became 'USDC.E' and was rejected with
400. The frontend's createActivityEvent call swallows errors via console.error,
so the borrow-and-deposit-to-card, bridge-to-card, and repay-and-withdraw
flows never landed in Mongo. Card balance webhooks then fell through to the
fallback path that creates synthetic 'card_transaction' rows.
Token identity is still preserved via metadata.tokenAddress (USDC_STARGATE)
and chainId (fuse.id).
https://claude.ai/code/session_01DQgmuLpKSnWQTDRogWXHZM
…vities pass backend validation" This reverts commit 5ab3183.
The global axios request interceptor in lib/api.ts injects the Solid backend Bearer JWT on iOS/Android. That header was leaking into Alchemy JSON-RPC calls (same axios import) which made Alchemy 401 only on mobile even when the env key is identical to web. Switch lib/alchemy.ts to a dedicated axios.create() instance so it never picks up the interceptor.
Mobile is back on the alchemy source after the axios-isolation fix in the previous commit; the Arbitrum diagnostic Sentry.captureMessage was only needed to attribute empty balances during the investigation.
…splay-ktdN0-qa fix(balances): isolate Alchemy axios from global JWT interceptor and drop diagnostics
Users only receive a Rain virtual card and do not order a physical card, so the trailing clause about a physical card being on its way was misleading.
…up-lAial Update CardWelcomePopup message text
…T instead of CARD_TRANSACTION The borrow handler and the SAVINGS/bridge fallthrough were both creating generic CARD_TRANSACTION "Card Deposit" rows. The backend's card-balance webhook (findPendingCardDepositActivity) only looks for [BORROW_AND_DEPOSIT_TO_CARD, BRIDGE_DEPOSIT] to flip pending rows to SUCCESS, so these rows stayed pending forever and were hidden by the 24h stuck-tx filter in ActivityTransactions.tsx. Writing the correct type from the form lets the existing webhook lookup match, so the activity reaches SUCCESS and stays visible. https://claude.ai/code/session_01DQgmuLpKSnWQTDRogWXHZM
…-display-P5EGg Update USDC symbol references from USDC.e to USDC
…y persist updateActivityEvent was sending `txHash` (not a backend DTO field — silently dropped by mongoose's strict schema) and omitting `url` entirely. The backend's UpdateActivityDto declares `hash` and `url`; the schema persists both. So neither field ever made it into the DB, even though local Zustand held the values. Symptom: after a hard refresh / cache clear, the activity-detail page lost its LayerZero explorer-row link, because the backend's GET /activity returned an activity with no `hash` or `url` and the page no longer had the local fallback. Side effect: useCardDepositPoller, which keys off activity.hash, can now actually pick up pending card-deposit rows as a fallback when the webhook is delayed (separate but related fix to broaden its activity types). https://claude.ai/code/session_01DQgmuLpKSnWQTDRogWXHZM
…fields fix(activity): send hash and url to backend on update so they actuall…
PageLayout was unmounting the entire savings page whenever isBalanceLoading or isTransactionsLoading flipped true — which now fires on every vault tab switch since both queries are keyed by the current vault. On Android this remounts the reanimated vault cards and analytics chart and feels stuck. Only show the full-page loader on the very first load (tracked via a ref). Subsequent vault switches keep the page mounted and render skeletons in the Total Value / Interest Earned slots while the new vault's data loads. Also surface a loading state on the Vault Breakdown tab so it stops flashing "No vault breakdown data available" during the switch.
…loader-oX5Fh-qa fix(savings): keep page mounted on vault switch, show inline skeletons
…ysical card shipping with Rain's actual displayName rule Rain's only character restriction is on configuration.displayName for physical cards (alphanumeric, spaces, periods, hyphens), and is ignored for virtual cards entirely. The Rain KYC form was rejecting valid applicants with non-Latin names (Cyrillic, Arabic, CJK) for both virtual and physical cards. - RainKyc schema: removed Latin-only regex on firstName/lastName. - OrderPhysicalCardModal: relaxed the shipping name regex to match Rain's documented rule (also allow digits and periods) since these names are embossed as the physical card's displayName.
…tion-rain-AeJe0 Refactor name validation for Rain KYC and physical card forms
Fetch and display the safe's native ETH balance on Arbitrum alongside Ethereum, Fuse, and Base. Users who sent ETH to their Arbitrum safe address can now see and transfer those funds from the Send flow.
Add Arbitrum network support to balance fetching
@didit-protocol/sdk-react-native's Expo plugin injects the Podfile entry pointing at .../sdk-ios/main/DiditSDK.podspec. On 2026-05-25 main was bumped to 4.0.0, which no longer satisfies the SdkReactNative.podspec constraint of `DiditSDK ~> 3.2`, so pod install fails on every iOS EAS build with a CocoaPods version conflict. Patch the plugin to use the 3.2.13 tagged podspec — the latest 3.2.x release that still ships the flat DiditSDK.xcframework + OpenSSL.xcframework the react-native-quick-crypto patch relies on for OpenSSL.
…ments #2093 pinned the DiditSDK pod to 3.2.13, which gets pod install past the version constraint but introduces a new fault: VerificationError gained a .retryBlocked case starting in 3.2.10, and @didit-protocol/sdk-react-native@3.2.8's DiditSdkBridge.swift mapErrorType() switch only handles .sessionExpired / .networkError / .cameraAccessDenied / .unknown. Xcode errors out with "switch must be exhaustive" during the Fastlane step. 3.2.9 is the latest DiditSDK iOS release before the enum was extended, so the wrapper's 4-case switch is still exhaustive against it. Verified by diffing the swiftinterface across 3.2.8 / 3.2.9 / 3.2.10 / 3.2.13 — the .retryBlocked case lands in 3.2.10. The wrapper's other switches (VerificationStatus, VerificationResult) are unchanged across 3.2.x.
fix(ios): pin DiditSDK pod to 3.2.13 instead of tracking main
The send form pulled balance from wagmi's useBalance, which has a 5-minute default staleTime in the app's QueryClient and isn't invalidated by the safe-account send flow (useSend doesn't go through useTransactionAwait). After a previous send, the token picker — fed by useWalletTokens (5s polling + SSE-invalidated) — showed the fresh balance, but selecting a token brought the user back to a SendForm displaying wagmi's stale cache. Drop wagmi's useBalance and source the balance from useWalletTokens instead, looking up the live token by contractAddress + chainId. The picker and the send screen now read from the same query. Max button also lost precision: it did `balanceAmount.toString()` where balanceAmount was Number(formatUnits(wei, decimals)). For balances whose low-order wei round up when squeezed into a float64, parseUnits later produced a BigInt above the actual on-chain balance and the transfer reverted — while Send stayed enabled because the Zod check was a JS-Number compare. Build the max string from the wei BigInt via formatUnits so it round-trips through parseUnits exactly, and validate amount <= balance in wei so the button can't enable for amounts that exceed the balance after rounding.
…vZYP Replace wagmi useBalance with useWalletTokens for fresher balance data
The form uses react-hook-form's onChange mode, which only re-runs the
Zod schema when the Controller's TextInput fires onChange. handleMaxPress
just calls setValue('amount', maxAmount), which writes the value but
doesn't run validation, so isValid stays at its prior value (false if
the user hadn't typed first) and the Review button remains disabled.
Call trigger('amount') after setValue — matches the pattern already
used in CardRepayForm and RegularWithdrawForm for the same problem.
…DOC-qa fix(send): trigger validation on Max so Review enables immediately


Note
High Risk
Updates KYC and card funding/management flows across web and native (including new third-party SDKs and iOS extension targets), which are user-verification and payments-adjacent paths with higher regression risk.
Overview
Adds multi-provider card support (Bridge vs Rain) across the app, including provider-aware formatting, deposit address selection (contracts vs
funding_instructions), card status routing, and gating of actions like deposit/withdraw/freeze/PIN management based on provider and customer/KYC state.Reworks KYC: replaces the existing Persona-based
kycflow with Didit (newkyc.native.tsxfor mobile and updated web embedding), and adds a separatebridge-kycPersona page for Bridge-only KYC; card KYC entry now redirects to the correct provider flow.Introduces MeaWallet MPP integration and iOS Wallet Extensions (new extension entrypoints + docs), adds registry/config plumbing (
.npmrc,meawallet/mea_configignore, Expo plugins + EAS extension provisioning), and removes Fingerprint.com usage/envs and signup fingerprint collection. Also adds a hiddensavings-oldroute for side-by-side savings calculation comparison and minor home/coin chart UX/data-fetch adjustments (spin-and-win card, CoinGecko native token charting, savings summary backend hook).Written by Cursor Bugbot for commit 1f5b304. This will update automatically on new commits. Configure here.