Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@
| Category | Technology | Notes |
|----------|-----------|-------|
| Framework | React 19 | StrictMode enabled |
| Language | TypeScript 5 (strict, ESM) | Path aliases: `@/src/*`, `@packageJSON` |
| Language | TypeScript 6 (strict, ESM) | Path aliases: `@/src/*`, `@packageJSON` |
| Blockchain | wagmi 2 + viem 2 | Type-safe Ethereum interaction |
| Data fetching | TanStack Query 5, graphql-request | Suspense-based contract reads |
| Routing | TanStack Router | File-based with auto code-splitting |
| UI | Chakra UI 3 + Emotion | Semantic tokens, light/dark mode |
| Testing | Vitest + Testing Library | jsdom environment, colocated test files |
| Build | Vite 6 + SWC | Manual chunk splitting for vendors |
| Build | Vite 8 + React plugin | Manual chunk splitting for vendors |
| Linting | Biome | Format + lint in one tool |
| Env validation | Zod + @t3-oss/env-core | All vars `PUBLIC_` prefixed |

Expand Down Expand Up @@ -65,7 +65,6 @@ ComponentName/
**HOC patterns:**
- `withSuspenseAndRetry(Component)` -- primary pattern for async components; wraps in `QueryErrorResetBoundary` + `ErrorBoundary` + `Suspense` for automatic retry on query failures
- `withSuspense(Component)` -- simpler variant without retry
- `withWalletStatusVerifier(Component)` -- gates a component behind wallet connection + chain sync

**Contract hooks** (auto-generated by wagmi-cli from ABIs):
- `useRead{Contract}{Function}` -- standard read
Expand All @@ -92,7 +91,7 @@ Four external data paths. Components never call external services directly -- th

3. **Subgraph queries**: `graphql-request` with typed document nodes from `@graphql-codegen` -> TanStack Query cache. Queries live in `src/subgraphs/queries/`, generated types in `src/subgraphs/gql/`.

4. **Token data**: LI.FI SDK (`getChains` -> `getTokens` -> `getTokenBalances`) via `useTokens` hook. Token lists fetched in parallel via `useTokenLists` (Suspense-based, sources: 1INCH, CoinGecko, optional Uniswap default list). Tokens deduplicated by `chainId + address`, native tokens auto-injected per chain.
4. **Token data**: LI.FI SDK (`getChains` -> `getTokens` -> `getTokenBalances`) via `useTokens` hook. Token lists fetched in parallel via `useTokenLists` (Suspense-based, source: CoinGecko, optional Uniswap default list). Tokens deduplicated by `chainId + address`, native tokens auto-injected per chain.

## Routes

Expand All @@ -117,7 +116,7 @@ Token list URLs ───> fetch + Zod validate ───> Tan

Caching strategy:
- All async state lives in TanStack Query. Components do not hold async data in local state.
- Token lists: `staleTime: Infinity`, `gcTime: Infinity` (fetched once per session).
- Token lists: `staleTime: 1 hour`, `gcTime: 1 hour`.
- Token balances: refreshed every ~32s (`BALANCE_EXPIRATION_TIME`).
- Contract reads: Suspense-based -- component suspends until data arrives, then cached normally.

Expand Down Expand Up @@ -223,7 +222,6 @@ When adding a new provider: place it inside the outermost provider it depends on

**Web3 connection state** (`src/hooks/useWeb3Status.tsx`):
- `useWeb3Status()` -- returns `{ readOnlyClient, appChainId, address, isWalletConnected, isWalletSynced, switchChain, disconnect, ... }`
- `useWeb3StatusConnected()` -- same but throws if wallet is not connected; use inside components that are already gated by `withWalletStatusVerifier`

**Token hooks**:
- `useTokens()` -- token list + LI.FI prices + account balances, sorted by balance value
Expand Down Expand Up @@ -261,9 +259,11 @@ To add a new contract: save the ABI, add it to the contracts array, run `pnpm wa

**`WalletStatusVerifier`** component (`src/components/sharedComponents/WalletStatusVerifier.tsx`) -- renders a fallback cascade based on wallet state:
1. Not connected -> `ConnectWalletButton`
2. Connected but `walletChainId !== appChainId` -> "Switch to [Network]" button
2. Connected but `walletChainId !== targetChainId` (where `targetChainId` is the optional `chainId` prop if provided, otherwise `appChainId`) -> "Switch to [Network]" button
3. Connected + synced -> renders children

**`withWalletStatusVerifier(Component)`** HOC -- wraps the component with the above logic. Already applied to `TransactionButton` and `SignButton` -- do not double-wrap.
`useWeb3StatusConnected()` (same file) -- companion hook that provides typed connected-wallet status (`address`, `readOnlyClient`, etc.) inside the `<WalletStatusVerifier>` tree. Throws a `DeveloperError` if called outside it.

`TransactionButton` and `SignButton` use the `useWalletStatus()` hook directly for inline wallet state checks rather than wrapping with the `WalletStatusVerifier` component.
Comment thread
gabitoesmiapodo marked this conversation as resolved.

Sync check: `isWalletSynced = isWalletConnected && walletChainId === appChainId`.
Loading