⬆️ major upgrade#631
Conversation
🦋 Changeset detectedLatest commit: 12563e6 The changes in this PR will be included in the next version bump. Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
Caution Review failedFailed to post review comments WalkthroughMonorepo-wide modernization: ESLint migrated to flat configs, major dependency upgrades (Expo/React/React Native/Wagmi), migration from Alchemy SDK to account-kit/aa-sdk variants, Expo Router replacing navigation hooks, TypeScript type widenings for byte arrays, CI/pnpm/tooling updates, and assorted test/infrastructure adjustments. Changes
Sequence Diagram(s)(omitted) Estimated code review effort🎯 4 (Complex) | ⏱️ ~75 minutes Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 2 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Summary of ChangesHello @cruzdanilo, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request represents a substantial infrastructure and dependency upgrade across the entire monorepo. The primary goal is to modernize the project by adopting the latest versions of key libraries and tools, particularly focusing on a new centralized ESLint setup and a significant shift in account abstraction libraries. These changes aim to enhance code quality, leverage new features, and improve the overall development experience and application performance. Highlights
Ignored Files
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
There was a problem hiding this comment.
Code Review
This pull request represents a significant and well-executed major upgrade across the repository. The migration to a centralized ESLint flat config, the upgrade of key dependencies like Expo, React, and Wagmi, and the consistent refactoring to adopt new APIs (like Expo Router and Account Kit) are all excellent improvements for the project's health and maintainability. My review focuses on a few specific areas to further enhance code quality and prevent potential issues.
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #631 +/- ##
===========================================
+ Coverage 53.61% 78.49% +24.88%
===========================================
Files 51 47 -4
Lines 8060 2041 -6019
Branches 643 461 -182
===========================================
- Hits 4321 1602 -2719
+ Misses 3724 282 -3442
- Partials 15 157 +142
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Actionable comments posted: 57
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (12)
server/api/auth/registration.ts (2)
340-348: Critical: Potential null reference error onregistrationInfo.Line 340 only checks
verified, but lines 341-348 unconditionally accessregistrationInfoproperties. IfverifyRegistrationResponsereturnsverified: falsewithregistrationInfo: undefined, or in any edge case whereregistrationInfois absent, line 341 will throw aTypeErrorwhen destructuring.🔒 Add null check for registrationInfo
- if (!verified) return c.json({ code: "bad registration", legacy: "bad registration" }, 400); + if (!verified || !registrationInfo) return c.json({ code: "bad registration", legacy: "bad registration" }, 400); const { credential, credentialDeviceType } = registrationInfo;
216-216: TODO: Implement excludeCredentials to prevent duplicate credential registration.The
excludeCredentialsoption would improve UX by preventing users from registering the same authenticator multiple times.Do you want me to generate an implementation that queries existing credentials and populates
excludeCredentials, or open a new issue to track this enhancement?src/components/loans/Review.tsx (1)
40-461: Architectural concern: Smart component logic in dumb component location.This component is located in
src/components/loans/(dumb components directory) but contains smart component responsibilities including data fetching (useQueryline 45), mutations (useMutationlines 98-138), and business logic.Based on learnings, dumb components should only receive data and callbacks via props, while smart components handling data fetching and state management should be in
src/app/**.Consider either:
- Moving this component to
src/app/loan/review.tsxas a screen component, or- Refactoring to extract the data fetching and business logic into a screen component, passing data and callbacks as props to a presentational Review component
src/components/card/CardPIN.tsx (1)
18-53: FixuseRef<ReturnType<typeof setInterval>>(undefined)typing and make interval clearing null-safe.The ref is initialized with
undefinedbut the generic type doesn't include it. Additionally,clearInterval()is called ontimerReference.currentat lines 27, 37, and 51 where the value may be unset.Proposed fix
- const timerReference = useRef<ReturnType<typeof setInterval>>(undefined); + const timerReference = useRef<ReturnType<typeof setInterval> | null>(null); + function clearTimer() { + if (timerReference.current === null) return; + clearInterval(timerReference.current); + timerReference.current = null; + } function startCountdown() { + clearTimer(); setDisplayPIN(true); setCountdown(5); timerReference.current = setInterval(() => { setCountdown((previous) => { if (previous <= 1) { - clearInterval(timerReference.current); + clearTimer(); setDisplayPIN(false); return 0; } return previous - 1; }); }, 1000); } function stopCountdown() { - clearInterval(timerReference.current); + clearTimer(); setDisplayPIN(false); setCountdown(0); } useEffect(() => { if (open && card?.details.pin) startCountdown(); else stopCountdown(); return () => { - clearInterval(timerReference.current); + clearTimer(); }; }, [open, card]);src/components/home/PortfolioSummary.tsx (1)
20-32: Refactor component to follow dumb component architecture.This component is located in
src/components/**which per guidelines should contain dumb components that receive all data and callbacks via props. However, it directly usesuseQueryhooks anduseRouterfor navigation, coupling it to application state and routing logic.Based on coding guidelines: "Dumb components (UI) should receive data and callbacks via props with no knowledge of the application's state. Location:
src/components/**."♻️ Recommended refactoring approach
Move data fetching and navigation logic to the parent screen/smart component, then pass down:
export default function PortfolioSummary({ averageRate, portfolio, + country, + processingBalance, + onPortfolioPress, + onActivityPress, }: { averageRate: bigint; portfolio: { usdBalance: bigint; depositMarkets: { market: string; symbol: string; usdValue: bigint }[] }; + country?: string; + processingBalance?: number; + onPortfolioPress: () => void; + onActivityPress: () => void; }) { const { usdBalance, depositMarkets } = portfolio; - const router = useRouter(); - const { data: country } = useQuery({ queryKey: ["user", "country"] }); - - const { data: processingBalance } = useQuery({ - queryKey: ["processing-balance"], - queryFn: () => getActivity(), - select: (activity) => - activity.reduce( - (total, item) => (item.type === "panda" && isProcessing(item.timestamp) ? total + item.usdAmount : total), - 0, - ), - enabled: country === "US", - }); return ( <YStack gap="$s5" alignItems="center" cursor="pointer" hitSlop={20} - onPress={() => { - router.push("/portfolio"); - }} + onPress={onPortfolioPress} > {/* ... */} {processingBalance ? ( <XStack {/* ... */} - onPress={() => { - router.push("/activity"); - }} + onPress={onActivityPress} >Then handle the queries and navigation in the parent screen component.
src/components/swaps/Failure.tsx (2)
53-57: Replace inline styles with Tamagui styled props.The inline object literal passed to
contentContainerStyleviolates the coding guideline that forbids inline styles. All styling must use Tamagui's styled system props.As per coding guidelines: "Do not use inline styles with the React Native
styleprop using object literals. All styling must go through Tamagui's styled system props."♻️ Suggested refactor using Tamagui
Consider extracting the styles into Tamagui's styled system or using Tamagui's layout components:
- <ScrollView - showsVerticalScrollIndicator={false} - stickyHeaderIndices={[0]} - // eslint-disable-next-line react-native/no-inline-styles - contentContainerStyle={{ - flexGrow: 1, - flexDirection: "column", - justifyContent: "space-between", - }} - stickyHeaderHiddenOnScroll - > + <ScrollView + showsVerticalScrollIndicator={false} + stickyHeaderIndices={[0]} + flexGrow={1} + flexDirection="column" + justifyContent="space-between" + stickyHeaderHiddenOnScroll + >
43-43: Consider using design tokens for numeric values.Hardcoded numeric values should use predefined design tokens from
tamagui.config.tsfor consistency and maintainability.As per coding guidelines: "All styling must use predefined design tokens from tamagui.config.ts. Never use hardcoded values for colors, spacing, fonts, etc."
Examples:
- Line 43:
height={220}→ considerheight="$someTokenValue"- Line 66:
size={80}→ considersize="$someTokenValue"Also applies to: 66-66
src/components/loans/Asset.tsx (1)
21-136: Consider relocating this component or extracting smart logic.This component exhibits "smart" component characteristics (uses hooks, fetches data, manages state, handles navigation), but it's located in
src/components/**which, per coding guidelines, should contain "dumb" UI components that only receive data and callbacks via props.Consider either:
- Moving this to
src/app/loan/asset.tsxas a screen component (preferred for Expo Router)- Extracting the business logic (hooks, state management) into a screen component and passing data/callbacks down to a pure UI component
This would align with the architecture guideline: "separate smart components (screens, perform fetching) from dumb components (ui only)."
Based on learnings about the mobile architecture separating smart and dumb components.
app.config.ts (1)
60-89: Remove bare"expo-asset"plugin entry unless building with embedded asset files.The
expo-assetplugin (line 87) is listed without configuration and serves no purpose here. The plugin only applies when you need to embed asset files into the native binary at build time (via anassetsarray config). Since the app uses the runtimeuseAssets()hook fromexpo-asset(insrc/app/_layout.tsx), the package is already installed and functional—the plugin entry is redundant.Regarding targetSdk: The app currently relies on Expo SDK defaults (no explicit
targetSdkVersionset). Verify your Expo SDK version against Google Play's August 31, 2025 requirement for API 35+. If using SDK 52, explicitly settargetSdkVersion: 35via theexpo-build-propertiesplugin to meet the deadline.src/components/auth/Auth.tsx (1)
237-249: Remove redundantsetSignUpModalOpen(false)in thewebauthnbranch. It’s already set at the top ofonClose.Proposed diff
if (!method) return; if (method === "webauthn") { - setSignUpModalOpen(false); router.push("/passkeys"); return; }server/test/mocks/keeper.ts (1)
20-26: Add explicit guard forchain.rpcUrls.alchemy.http[0]in test mock to match production code pattern.The production code in
server/utils/keeper.ts,server/utils/publicClient.ts, andserver/utils/traceClient.tsall validatechain.rpcUrls.alchemy.http[0]withif (!chain.rpcUrls.alchemy.http[0]) throw new Error("missing alchemy rpc url");before use. The test mock at line 22 lacks this guard and will fail opaquely if the URL is missing. Add the same validation check at module level (before the vi.mock call) to maintain consistency and provide clear error messages.server/test/hooks/block.test.ts (1)
442-443: Add timer reset to global afterEach cleanup.The
vi.setSystemTime()in the "with weth withdraw proposal" describe block (line 158) lacks a matchingvi.useRealTimers()cleanup, while other similar blocks have local cleanup. The global afterEach should includevi.useRealTimers()to ensure timers are reset consistently across all tests and prevent leaks.Proposed fix
-afterEach(() => vi.restoreAllMocks()); +afterEach(() => { + vi.useRealTimers(); + vi.restoreAllMocks(); +});
🤖 Fix all issues with AI agents
In @.changeset/polite-turtles-glow.md:
- Line 5: The changeset summary mixes a noun phrase with verbs; rewrite the
summary text "new eslint config, upgrade vitest, account-kit, and other
dependencies" into a consistent, lowercase imperative-present sentence such as
"add new eslint config, upgrade vitest, account-kit, and other dependencies" so
the message uses imperative mood and consistent casing.
In @.changeset/swift-waves-dance.md:
- Around line 1-15: Update the changeset summary line (currently "⬆️ major
upgrades:") to a gitmoji + lowercase imperative present-tense sentence such as
"⬆️ upgrade expo, react-native, wagmi, tamagui, sentry, and eslint" (keep the
existing bullet list unchanged as details).
In @app.config.ts:
- Around line 26-33: The config enables android.edgeToEdgeEnabled which alters
system insets; update UI to respect these inset changes by using safe-area
helpers (e.g., wrap top/bottom edge components in SafeAreaView or call
useSafeAreaInsets/useInsets and apply paddingTop/paddingBottom) and ensure
StatusBar and bottom nav/buttons adjust when edgeToEdgeEnabled is true; search
for affected components (root App container, header/footer, bottom-tab
components, any view using hardcoded margins) and replace hardcoded edge offsets
with inset-driven padding/margin so content no longer overlaps system bars.
In @babel.config.cjs:
- Around line 17-22: Remove the unused module-resolver aliases for the missing
packages in the Babel config: delete the entries for "^jose$":
"jose/dist/browser/index.js" and all "zustand" aliases ("^zustand$":
"zustand/index.js", "zustand/middleware", "zustand/vanilla", "zustand/shallow",
"zustand/traditional") from babel.config.cjs; if these packages are actually
required, instead add the missing dependencies to the root package.json and
install them so the aliases resolve correctly, but do not leave aliases pointing
to packages that are not installed.
In @common/eslint/base.mjs:
- Around line 56-77: The inline custom ESLint rule exa/prefer-string is embedded
in base.mjs which hurts maintainability and lacks a defensive node type check;
extract the rule into its own module (e.g., exa/prefer-string.js) and export it
as a plugin rule, then import/register it from base.mjs, and inside the rule's
create(CallExpression) handler add a defensive guard that ensures
node.callee.property exists and node.callee.property.type === "Identifier"
before checking property.name !== "toString" and before accessing
node.callee.optional/computed/arguments so the rule safely handles
non-Identifier properties.
In @common/wagmi.config.ts:
- Around line 166-167: The generated type assertion for rpcUrls.alchemy.http is
wrong: it uses readonly [string] (a single-element tuple) and also mutates the
readonly Chain; update the code that sets up the chain (the chain variable and
its rpcUrls.alchemy.http) to either remove the incorrect assertion and use
viem's Chain directly, or change the asserted type to readonly string[] (or
string[]) to match viem's shape, and avoid mutating the original readonly Chain
by creating a shallow copy/clone or building a new object (e.g., const chain = {
...anvil, rpcUrls: { ...anvil.rpcUrls, alchemy: { http:
[...anvil.rpcUrls.default] } } } ) so you preserve immutability while providing
the correct type for rpcUrls.alchemy.http.
In @eas.json:
- Line 7: Add a pnpm workspace config to package.json to restore lifecycle
script behavior: update package.json to include a "pnpm" object with
"onlyBuiltDependencies": [] (or list specific packages) so the prepare script
and other lifecycle hooks run during install; after updating, run pnpm install
--force to refresh the store for the new SHA256 format and verify handling for
changed behaviors (pnpm link/workspace overrides and pnpm deploy requiring
inject-workspace-packages=true) and adjust workspace/root overrides or workspace
config as needed.
In @metro.config.cjs:
- Around line 27-35: Your custom resolveRequest is clobbering Sentry's resolver
from getSentryExpoConfig; change the implementation of resolveRequest to
delegate first to upstream = config.resolver?.resolveRequest ??
context.resolveRequest (store that in a variable), then call upstream(context,
moduleName, platform) inside your try/catch so Sentry's hooks run, and only
apply your tslib and .js fallback logic when upstream throws or returns
undefined; ensure you still use the same symbol names (resolveRequest,
context.resolveRequest, config.resolver?.resolveRequest, moduleName checks) so
the behavior is preserved.
In @nx.json:
- Around line 5-15: In nx.json update the targetDefaults entry for "test:eslint"
to use the camelCase option key expected by the @nx/eslint executor: replace the
kebab-case "max-warnings" with "maxWarnings" (so the "test:eslint"
targetDefaults options object contains "maxWarnings": 0) to ensure the option is
picked up and enforces zero lint warnings.
In @package.json:
- Around line 30-33: The package.json currently pins "engines.node" to
"~24.12.0" and may not reflect Expo SDK 54’s tested Node range and known React
19 + Expo54 web bundling issues; update or loosen "engines.node" (e.g.,
>=20.19.4 or document CI verification) and explicitly verify/apply React 19
web-workarounds and renderer alignment: confirm react 19.1.x vs
react-native-renderer versions, ensure Expo SDK 54 is used with react-native
0.81.5 compatibility, and validate native modules against Expo’s
bundledNativeModules.json; run CI/builds for Node 24.12.0 if you keep that pin,
or change the engines entry and add checklist comments in package.json/docs
about web-build requirements and native module version alignment.
- Around line 99-105: Update the native module versions in package.json to match
Expo SDK 54's bundledNativeModules: change "react-native-reanimated" from
"~4.1.6" to "~4.1.1", change "react-native-safe-area-context" from "5.6.2" to
"~5.6.0", and change "react-native-svg" from "~15.12.1" to the exact "15.12.1";
leave "react-native-screens" as "~4.16.0". Also scan the block at lines ~192-233
for other native modules and align their versions to Expo SDK 54 equivalents.
In @server/package.json:
- Around line 15-18: The Nx "test" target references nonexistent targets
"test:ts" and "test:openapi"; add corresponding Nx target entries (or rename the
dependency) so Nx can resolve them: create Nx targets named "test:ts" that runs
the TypeScript compiler (tsc) and "test:openapi" that invokes the OpenAPI
generator (or change the dependency to the existing "generate:openapi" target),
update the project's targets block (e.g., in project.json) to include these
target names and their executor/commands so "nx test server" no longer fails.
In @server/test/api/activity.test.ts:
- Around line 294-297: The root-level afterEach hook calls vi.restoreAllMocks(),
which can cause race conditions with describe.concurrent tests; remove the
vi.restoreAllMocks() call and keep only vi.clearAllMocks() in the afterEach hook
so mocks' call history is cleared but implementations are not restored globally;
update the afterEach block that currently references vi.clearAllMocks() and
vi.restoreAllMocks() to only call vi.clearAllMocks(), noting that the
@sentry/node mock (spy: true) is used only in sequential blocks and does not
require global restore.
In @server/test/hooks/activity.test.ts:
- Around line 44-51: The publicKey assignment currently wraps
hexToBytes(owner.address) in a redundant new Uint8Array, creating an unnecessary
copy; update the database.insert call (the values object used when calling
database.insert(credentials).values([...])) to use hexToBytes(owner.address)
directly for the publicKey field (remove new Uint8Array(...))—ensure similar
occurrences (tests using publicKey: new Uint8Array(hexToBytes(...))) are updated
consistently.
In @server/test/hooks/panda.test.ts:
- Around line 1513-1516: The file has a global afterEach that calls
vi.clearAllMocks() and vi.restoreAllMocks(), so any nested or additional
afterEach(() => vi.restoreAllMocks()) is redundant and can confuse teardown
order; remove the nested/duplicate vi.restoreAllMocks() calls and keep a single
top-level afterEach using vi.clearAllMocks() and vi.restoreAllMocks(), or if
specific tests need isolated mock state use vi.resetAllMocks()/manual restores
within those tests instead; verify by ensuring only the top-level afterEach
contains vi.restoreAllMocks() and running the test suite to confirm no
teardown-related failures.
In @server/utils/createCredential.ts:
- Around line 27-29: The code creates a redundant copy by wrapping
hexToBytes(credentialId) in a new Uint8Array when computing publicKey; remove
the extra Uint8Array and set publicKey to webauthn?.publicKey ??
(isAddress(credentialId) ? hexToBytes(credentialId) : undefined) so the SIWE
20-byte address bytes are preserved as-is for decodePublicKey; keep the existing
null-check (if (!publicKey) throw new Error("bad credential")) unchanged.
In @server/utils/ensClient.ts:
- Line 1: The RPC URL construction in ensClient.ts currently allows
mainnet.rpcUrls.alchemy to be undefined, producing invalid URLs; update the
module initialization to explicitly resolve the Alchemy RPC URL (e.g., read
mainnet.rpcUrls.alchemy and the Alchemy API key) into a local rpcUrl variable
and validate it immediately—if missing or falsy, throw a clear Error (matching
the pattern used in keeper.ts/traceClient.ts/publicClient.ts) rather than
letting an "undefined/<key>" string propagate; locate the URL construction and
client-init code (references: mainnet, mainnet.rpcUrls.alchemy, rpcUrl or
similar variable and the ENS client initialization) and add the guard and error
thrown at module load time.
In @server/utils/publicClient.ts:
- Line 8: The current guard accesses chain.rpcUrls.alchemy.http[0] directly
which can throw a TypeError if chain.rpcUrls or chain.rpcUrls.alchemy is
undefined; replace that direct access with a safe existence check (e.g., use
optional chaining or first read chain.rpcUrls?.alchemy?.http into a local
variable and validate with Array.isArray/length) and then throw the same
Error("missing alchemy rpc url") when no valid URL is found; update the check
around the symbol chain.rpcUrls.alchemy.http (or use a local const like
alchemyUrl) to ensure no import-time TypeError occurs.
In @server/utils/traceClient.ts:
- Line 32: The current guard reads chain.rpcUrls.alchemy.http[0] directly and
can raise a TypeError if chain.rpcUrls or chain.rpcUrls.alchemy is undefined;
change the check in trace client initialization to safely test existence first
(e.g., use optional chaining like chain.rpcUrls?.alchemy?.http?.[0] or
explicitly verify chain.rpcUrls and chain.rpcUrls.alchemy before indexing) and
still throw the same Error("missing alchemy rpc url") when the URL is absent.
In @src/app/(auth)/_layout.tsx:
- Around line 18-22: The effect in useEffect references isLoading, isFetched,
isMiniApp, sdk.actions.ready, SplashScreen.hideAsync and reportError but not
credential, so remove credential from the dependency array to avoid unnecessary
re-runs; update the dependency array on the useEffect that contains
sdk.actions.ready() and SplashScreen.hideAsync() to only include the actual
dependencies (e.g., [isFetched, isLoading, isMiniApp]) so the effect only runs
when those values change.
In @src/components/activity/ActivityItem.tsx:
- Line 12: The component imports and uses useRouter and calls router.push with a
string literal (router.push("/activity-details") /
router.push("/(main)/activity-details")), which should be replaced with Expo
Router's generated typed routes; update ActivityItem to import the generated
route helpers/types (from the app's auto-generated routes) and call router.push
with the typed route constant/handler instead of raw strings wherever useRouter/
router.push is used (e.g., in the onPress or navigation handlers), ensuring you
reference the generated route symbol for "(main)/activity-details" rather than a
string literal.
In @src/components/activity/PendingProposals.tsx:
- Around line 126-130: Replace the string literal "/(main)/(home)" passed to
router.replace with Expo Router's typed route helper so route navigation is
type-safe; update the code around router.canGoBack(), router.back(), and
router.replace(...) to use the app's typed route (e.g., the generated Href/typed
route constant or helper for the home/main route) instead of constructing the
path string manually, importing the appropriate typed route symbol where needed
and passing that typed value into router.replace.
In @src/components/add-funds/Bridge.tsx:
- Line 107: The declaration previousSourceAddress uses an explicit undefined
initializer which is redundant; change the initialization to omit the argument
and rely on useRef's default by updating the variable declaration
(previousSourceAddress) to use useRef<string | undefined>() instead of
useRef<string | undefined>(undefined), leaving the type unchanged.
In @src/components/auth/Auth.tsx:
- Line 5: The navigation calls in Auth.tsx that use fragile string literals
(router.push("/passkeys") at lines noted and
router.push("/(auth)/(passkeys)/about") in Passkeys.tsx) should be replaced with
Expo Router's generated typed route helpers; import the app's typed route
identifiers or route-generator provided by expo-router and update the two
router.push(...) calls in Auth.tsx (the occurrences around lines 179 and 244) to
use those typed route constants/helpers instead of raw strings so navigation is
consistent and refactor-proof.
In @src/components/auth/Passkeys.tsx:
- Line 3: Replace the hard-coded route strings in Passkeys.tsx with typed route
constants: add or import typed Href constants (e.g., Href.AUTH and
Href.PASSKEYS_ABOUT) from your central routes module and use those instead of
"/(auth)" and "/(auth)/(passkeys)/about" wherever useRouter().push, Link href,
or navigation calls occur in the Passkeys component; update the import and all
occurrences to reference the constants (e.g., useRouter().push(Href.AUTH) or
<Link href={Href.PASSKEYS_ABOUT} />) so routes are centralized and type-safe.
In @src/components/auth/Success.tsx:
- Line 40: Replace the string literal passed to router.replace with the
generated typed route constant/function: instead of
router.replace("/(main)/(home)"), import and use the project's typed route
export (e.g., Routes.home or typedRoutes.home — whichever your app's route
generation produces) and call router.replace with that value; update the imports
in Success.tsx to pull in the typed routes and use the typed symbol in
router.replace to ensure type safety.
In @src/components/card/Card.tsx:
- Line 122: The router.push calls in the Card component use hardcoded strings
(e.g., "/(main)/getting-started"); replace those with Expo Router's typed route
helpers: import the generated Routes/typed routes helper and use router.push
with the corresponding typed route symbol (e.g., Routes.gettingStarted or the
generated GettingStarted route) for both occurrences (the router.push in the
Card component near the two spots currently using "/(main)/getting-started");
update imports accordingly so router.push uses the typed route symbol instead of
a raw string.
In @src/components/card/CardPIN.tsx:
- Line 2: The import currently includes the default React symbol but it’s
unused; remove the default import so only the named hooks remain (drop "React"
from the import that currently declares React, useState, useEffect, useRef) and
keep useState/useEffect/useRef so JSX still works under the new transform.
Ensure no other references to the React identifier exist in this module before
removing it.
- Around line 75-79: The map in CardPIN.tsx uses the PIN digit as the React key,
which can produce duplicates and leaks sensitive data; change the key to a
non-sensitive stable identifier such as the loop index (or a combination like
`${index}-${digit}`) in the Array.from(...).map callback so Text elements use
index (or index+digit) as the key and no PIN digit alone.
In @src/components/getting-started/GettingStarted.tsx:
- Line 152: The hardcoded navigation call router.push("/add-funds/add-crypto")
in GettingStarted.tsx should use Expo Router's generated typed route helper
instead of a string; import the generated Routes (or the typed navigation helper
provided by expo-router in your project) and replace the string push with the
typed route variant (e.g., router.push(Routes.<addFundsAddCrypto>() or the
project-specific Routes helper) so the compiler validates the route and
parameters at build time.
In @src/components/home/Portfolio.tsx:
- Around line 40-43: In Portfolio.tsx, avoid passing the literal
"/(main)/(home)" into router.replace; instead import and use the Expo Router
generated typed route identifier for the home/main screen and call
router.replace with that typed route (not a string). Locate the router.replace
call inside the router.canGoBack() conditional and swap the string for the
generated route constant/function from your Expo Router types (add the
corresponding import), ensuring router.replace receives the typed route
reference.
In @src/components/home/PortfolioSummary.tsx:
- Line 40: Enable Expo Router's TypeScript plugin in tsconfig.json
(expo-router/plugin) to generate typed route helpers, run the type generation
(build/tsc) and then replace the hardcoded router.push("/portfolio") and
router.push("/activity") calls with the generated typed route references (use
the exported route helpers/types from the Expo Router generated output instead
of string literals) so navigation uses compile-time route names rather than
"/portfolio" and "/activity".
In @src/components/loans/Asset.tsx:
- Line 113: Replace the hardcoded router.push("/loan/amount") call in Asset.tsx
with the project's typed-route helper (e.g., router.push(routes.loan.amount())
or router.push(AppRoutes.loan.amount()) depending on your typed-routes API) and
add the necessary import for that typed routes object; if the typed helper
requires parameters, pass them instead of using a string literal so the call is
compile-time checked and type-safe.
- Line 35: Replace the hardcoded string in router.replace("/loan") with the
app's typed route reference: locate the router.replace call in Asset.tsx and use
the generated typed route helper (the project's Expo Router typed routes or
route constant, e.g., the generated routes enum/const or the typed navigation
helper) instead of the literal string so navigation is compile-time checked;
import and use the project's typed routes API and call router.replace with that
typed route identifier rather than "/loan".
In @src/components/loans/Loans.tsx:
- Around line 54-58: router.replace("/defi") in the Loans.tsx back-navigation
branch uses the wrong path; update the call to
router.replace("/(main)/(home)/defi") so it matches the app's file-based route
(replace the string in the existing router.replace(...) invocation inside the
if/else block that also calls router.back()).
In @src/components/loans/Review.tsx:
- Around line 175-179: The router.replace calls use raw string literals (e.g.,
router.replace("/loan/receiver")) instead of typed Expo Router routes; replace
each router.replace invocation (references: router.replace("/loan/receiver"),
the calls around lines where router.replace("/loan") and
router.replace("/pending-proposals") occur) with a typed Href route or named
route constant: import type { Href } from "expo-router" (or define typed
constants) and pass that Href to router.replace to ensure compile-time route
safety for the calls in Review.tsx (the router.replace calls at the noted
locations).
In @src/components/roll-debt/RollDebt.tsx:
- Around line 69-73: Replace the hardcoded route string in RollDebt.tsx where
router.replace("/(main)/(home)") is called: use the Expo Router generated typed
route constant instead of the string (import the generated route types and pass
the corresponding route enum/constant to router.replace), keeping the existing
router.canGoBack()/router.back() logic unchanged; update the import to pull the
generated Routes/type and call router.replace with that typed route reference
(targeting the same (main)/(home) route) so route safety is enforced.
- Line 257: Multiple components (RollDebt, HomeActions, ProposalBanner,
ProfileHeader, Review, Amount, Success, Pay) call
router.replace("/pending-proposals") but that route file is missing; either add
a new route component that handles "/pending-proposals" (e.g., create the route
component that renders the pending proposals list and necessary server/client
logic) or update every navigation call to point to an existing route; search for
uses of router.replace("/pending-proposals") and replace them with the correct
route string or implement the missing route handler that those components
expect.
In @src/components/send-funds/Amount.tsx:
- Line 358: The code uses hardcoded route strings in Amount.tsx (calls to
router.replace("/(main)/(home)") and router.replace("/pending-proposals")),
which breaks typed routing; replace these with Expo Router's generated typed
route helpers instead: import and use the project's typed Routes symbols (e.g.,
Routes.Home or Routes.PendingProposals) when calling router.replace so
router.replace(Routes.Home) and router.replace(Routes.PendingProposals) are used
in place of the string literals; update both the router.replace call at Line 358
and the two router.replace calls referenced at Lines 453–455 to use the typed
route constants.
- Around line 181-185: Replace hardcoded route strings in Amount.tsx (e.g.,
router.replace("/send-funds/asset"), router.replace("/(main)/(home)"),
router.replace("/pending-proposals")) with the Expo Router generated typed-route
helpers (use the generated Routes.* helpers or typed navigation helpers), update
the import to pull in the typed routes/navigation helper, and call
router.replace(Routes.SendFundsAsset()) or the equivalent
Routes.MainHome()/Routes.PendingProposals() functions where those string
literals are used; also update the other router calls in this file that use
string paths to use the corresponding Routes.* helper for type safety.
In @src/components/send-funds/Receiver.tsx:
- Around line 58-62: The navigation calls use hardcoded route strings; replace
them with the Expo Router typed route helpers used elsewhere by importing/using
the generated typed routes and calling those instead of raw strings — change the
router.push that currently uses pathname "/send-funds/asset" to use the typed
route helper for the send-funds asset screen (e.g., the generated
send-funds.asset route) and change router.replace("/(main)/(home)") to the typed
main/home route helper (e.g., routes.main.home or the equivalent generated
function) so all navigation uses typed routes rather than manual string paths.
In @src/components/settings/Settings.tsx:
- Around line 33-37: The router.replace call uses a raw string route; replace it
with the Expo Router generated typed route (e.g., use the generated Routes
enum/object value for the home screen) by importing the typed routes and passing
that value to router.replace instead of the string literal; update the code that
calls router.replace("/(main)/(home)") (near router.canGoBack()/router.back())
to use the typed route identifier (for example Routes.Home) and ensure the
appropriate import from your generated routes file is added.
In @src/components/shared/ModalSheet.tsx:
- Around line 5-7: Convert the multi-line function expression
WebSheetPortalContainer into a function declaration: replace "const
WebSheetPortalContainer = function ({ children }: { children: React.ReactNode })
{ ... }" with "function WebSheetPortalContainer({ children }: { children:
React.ReactNode }) { ... }", preserving the prop type annotation and the
returned JSX (Stack with className "sheet-portal") and keep any existing
export/usage unchanged.
In @src/components/swaps/Failure.tsx:
- Around line 113-116: The Failure component currently calls
queryClient.invalidateQueries and router.replace directly in its onPress
handler, breaking the "dumb UI" rule; change Failure's props (e.g., add an
onClose or onRetry callback in the Failure component's prop interface) and
replace the inline onPress body to merely call that prop callback, removing any
direct imports/usage of queryClient or router from Failure; update the parent
component to implement the new callback to run queryClient.invalidateQueries({
queryKey: ["swap"] }).catch(reportError) and router.replace("/swaps") so state
invalidation and navigation occur in the parent.
- Line 115: Replace the hard-coded route string in Failure.tsx
(router.replace("/swaps")) with the project's typed route helper to ensure
compile-time safety: import and use the generated/typed route helper (e.g.,
Routes.Swaps() or TypedRoutes.Swaps or the project-specific named export) and
call router.replace(Routes.Swaps()) (or the equivalent typed helper) instead of
the string literal, updating imports as needed so all navigation uses the typed
route API rather than manual path strings.
In @src/utils/accountClient.ts:
- Around line 68-70: The SIWE branch reads getConnection(ownerConfig).address
directly which can throw if getConnection(...) returns undefined; update the
condition in the code that checks
queryClient.getQueryData<AuthMethod>(["method"]) === "siwe" to guard the
connection using optional chaining or an explicit null check (e.g.,
getConnection(ownerConfig)?.address or const conn = getConnection(ownerConfig);
if (conn && conn.address && ...)) before calling signMessage(ownerConfig, {
message: { raw: uoHash } }), ensuring wrapSignature(0, await signMessage(...))
only runs when a valid connection/address exists.
- Around line 15-16: Replace the fragile deep import of standardExecutor with
the package's public API: remove the "// @ts-expect-error deep import..." line
and the deep path import from
"@account-kit/smart-contracts/dist/esm/src/msca/account/standardExecutor" and
instead import standardExecutor directly from "@account-kit/smart-contracts"
(use the existing symbol standardExecutor to locate the import). Ensure no
@ts-expect-error remains and update any import references to use the new module
path.
In @src/utils/panda.ts:
- Line 96: The variable declaration uses an invalid generic on Uint8Array;
change the declaration for iv to use the plain Uint8Array type (remove the
<ArrayBuffer> generic) so it matches assignments from
window.crypto.getRandomValues(...) and crypto.randomBytes(...); update the
declaration of iv to "let iv: Uint8Array;" to fix the type error.
- Line 74: Remove the unnecessary and unsafe type cast on the line using
decipher.setAuthTag; replace "decipher.setAuthTag(secret.subarray(-16) as
never)" with a plain call that passes the Buffer/Uint8Array returned by
secret.subarray(-16) directly so TypeScript can validate the type (refer to
decipher.setAuthTag and secret.subarray usage). Also fix the invalid type
annotation near the later IV declaration by changing the incorrect
"Uint8Array<ArrayBuffer>" to a simple "Uint8Array" (refer to the iv variable) so
the declaration reads as a plain Uint8Array.
In @src/utils/server.ts:
- Around line 180-181: In createCredential, avoid directly accessing
getConnection(ownerConfig).address because getConnection can return undefined
and will throw before your subsequent null check; instead retrieve the
connection safely (e.g., use optional chaining or capture the result in a
variable like conn = getConnection(ownerConfig)) and then read conn?.address
into credentialId, preserving the existing check if (method === "siwe" &&
!credentialId) to throw a controlled error when missing; update the reference to
getConnection(ownerConfig).address accordingly.
- Line 27: The code accesses getConnection(ownerConfig).address without guarding
for undefined; update both occurrences (the getConnection(ownerConfig).address
uses at the current locations around lines 27 and 180) to safely handle
disconnected states by using optional chaining on the property (e.g.,
getConnection(ownerConfig).address?) or by checking
getConnection(ownerConfig).status === 'connected' before reading .address;
modify the logic in the functions that reference getConnection and ownerConfig
so they won't access .address when the connection may be undefined.
In @tamagui.config.ts:
- Around line 387-395: The theme object currently assigns canonical Tamagui keys
(color, background, backgroundColor, backgroundHover, backgroundPress,
borderColor, borderColorFocus, borderColorPress, outlineColor) to empty strings
which can produce invalid styles; remove those empty-string assignments and
either omit the keys entirely or map them to valid design tokens (e.g., existing
token names used elsewhere in this config) so core components read real values;
apply the same fix for the duplicate block around the other occurrence (lines
~531-539) so no canonical theme key is left set to "".
closes #582
Summary by CodeRabbit
New Features
Refactor
User-facing Changes
Dependencies
✏️ Tip: You can customize this high-level summary in your review settings.