Skip to content
Merged
Changes from 1 commit
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
13 changes: 6 additions & 7 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,7 @@ 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`
- `useWeb3StatusConnected()` -- same but throws if wallet is not connected; callers must ensure the wallet is connected before rendering
Comment thread
gabitoesmiapodo marked this conversation as resolved.
Outdated

**Token hooks**:
- `useTokens()` -- token list + LI.FI prices + account balances, sorted by balance value
Expand Down Expand Up @@ -264,6 +263,6 @@ To add a new contract: save the ABI, add it to the contracts array, run `pnpm wa
2. Connected but `walletChainId !== appChainId` -> "Switch to [Network]" button
3. Connected + synced -> renders children
Comment on lines 260 to 263

Copilot AI Apr 10, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the WalletStatusVerifier fallback cascade, step 2 currently documents the chain mismatch check as walletChainId !== appChainId, but the component uses useWalletStatus({ chainId }), which compares the wallet chain against targetChainId (the optional chainId prop if provided, otherwise appChainId). Please update the step-2 wording to reflect that the verifier gates on the requested/target chain, not strictly appChainId.

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 891f992. Updated the chain mismatch check description to reference targetChainId and documented the full fallback cascade (optional chainId prop -> appChainId -> chains[0].id).


**`withWalletStatusVerifier(Component)`** HOC -- wraps the component with the above logic. Already applied to `TransactionButton` and `SignButton` -- do not double-wrap.
`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