Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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
8 changes: 8 additions & 0 deletions .claude/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"permissions": {
"allow": [
"WebFetch(domain:docs.dappbooster.dev)",
"WebFetch(domain:components.dappbooster.dev)"
]
}
}
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ src/subgraphs/gql
src/hooks/generated.ts
vite.config.ts.timestamp*

# Claude Code
.claude/settings.local.json
CLAUDE.local.md

# Project .env files
.env.local
.env
Expand Down
1 change: 1 addition & 0 deletions AGENTS.md
229 changes: 229 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
# dAppBooster

A repository template / starter-kit for building decentralized applications (dApps). Built by BootNode based on 5+ years of dApp development. Docs: https://docs.dappbooster.dev/ | Components: https://components.dappbooster.dev/

## Requirements

- Node 24+ (see `.nvmrc`)
- pnpm 10.30.2+ (enforced via `packageManager` in package.json; corepack will block npm/yarn)

## Setup

1. `pnpm install` (postinstall automatically runs `pnpm wagmi-generate`)
2. `cp .env.example .env.local`
3. Edit `.env.local`:
- `PUBLIC_APP_NAME` is mandatory
- `PUBLIC_WALLETCONNECT_PROJECT_ID` is needed for wallet connection to work
- RPC vars (`PUBLIC_RPC_*`) are optional -- wagmi falls back to default public RPCs
- Subgraph vars are all-or-nothing: if ANY `PUBLIC_SUBGRAPHS_*` var is missing or empty, codegen will skip and the app will crash at runtime. Either set them all or remove all subgraph-related code.
4. `pnpm subgraph-codegen` (only if subgraph vars are configured)
5. `pnpm dev`

## Git Hooks (Husky)

Three hooks run automatically and will block on failure:

- **pre-commit:** lint-staged runs Biome check + Vitest on related files for staged changes
- **commit-msg:** commitlint enforces conventional commit format. Valid types: `feat`, `fix`, `docs`, `test`, `ci`, `refactor`, `perf`, `chore`, `revert`. PR titles are also validated via CI.
- **pre-push:** full `tsc --noEmit` type check (pushes with type errors will be rejected)

## Quick Reference

- **Dev server:** `pnpm dev`
- **Build:** `pnpm build` (runs tsc + vite build)
- **Lint:** `pnpm lint` (Biome) / `pnpm lint:fix`
- **Test:** `pnpm test` / `pnpm test:watch` / `pnpm test:coverage`
- **Generate contract hooks:** `pnpm wagmi-generate`
- **Generate routes:** `pnpm routes:generate`
- **Generate subgraph types:** `pnpm subgraph-codegen`
- **Docs site:** `pnpm docs:dev`

## Tech Stack

- React 19 + TypeScript (strict mode, ESM -- `"type": "module"`, no `require()`)
- Vite + SWC (fast JSX transform)
- Wagmi 2 + viem 2 (Ethereum interaction)
- TanStack Router (file-based routing with auto code-splitting)
- TanStack Query (server state / data fetching)
- Chakra UI 3 (component library + theming)
- ConnectKit (default wallet connector; RainbowKit and Web3Modal also available)
- LI.FI SDK (token prices/balances)
- Zod + @t3-oss/env-core (environment variable validation)
- Biome (linting + formatting)
- Vitest + Testing Library (testing)

## Project Structure

```
src/
components/
pageComponents/ # Page-level components (home/, weth/, demos/)
sharedComponents/ # Reusable UI + business components
ui/ # Chakra provider + theme setup
hooks/ # Custom React hooks
generated.ts # Auto-generated by wagmi-cli (do not edit)
providers/ # Web3Provider, TransactionNotificationProvider
routes/ # TanStack Router file-based routes
__root.tsx # Root layout (provider stack + shell)
lib/
networks.config.ts # Chain + transport configuration
wagmi/ # Wagmi CLI config + custom Suspense plugin
wallets/ # ConnectKit, RainbowKit, Web3Modal configs
constants/
contracts/ # Contract ABIs + addresses per chain
tokenLists.ts # Token list URLs
common.ts # Shared constants (isDev, includeTestnets)
types/ # TypeScript type definitions
utils/ # Utility functions
subgraphs/ # GraphQL codegen config + queries
env.ts # Zod-validated environment variables
main.tsx # Entry point
routeTree.gen.ts # Auto-generated route tree (do not edit)
```

No top-level barrel exports. Import directly from each file/folder path.

## Provider Stack

The root layout (`src/routes/__root.tsx`) wraps the app in this order:

```
Chakra Provider (theme, color mode)
Web3Provider (WagmiProvider -> QueryClientProvider -> WalletProvider)
TransactionNotificationProvider (tx toast context)
Header / Outlet (page) / Footer
```

New providers should be inserted at the appropriate layer based on their dependencies.

## Code Style

- **No semicolons** in TypeScript/JavaScript
- **Single quotes**
- 2-space indentation, 100 char line width
- Import organization handled by Biome
- Path aliases: `@/src/*` and `@packageJSON`
- All env vars prefixed with `PUBLIC_` and validated in `src/env.ts`
- JSDoc comments on exported functions/components (follow existing patterns)

## Styling

- **Chakra UI props** are the primary styling method. No CSS modules, no Tailwind, no styled-components.
- Theme is defined in `src/components/ui/provider.tsx` using `createSystem` extending `defaultConfig`.
- Use **semantic tokens** for colors: `bg`, `primary`, `text`, `danger`, `ok`, `warning`. These support light/dark mode. Do not hardcode color values.
- Fonts: Manrope (body/heading), Roboto Mono (mono/code).
- Complex components export style objects from a `styles.ts` file, consumed via Chakra's `css` prop or spread into component props.

## Component Conventions

- **Simple components:** single file (e.g., `ExplorerLink.tsx`, `SwitchNetwork.tsx`)
- **Complex components:** folder with:
- `index.tsx` -- main component and public API
- `Components.tsx` -- sub-components (styled primitives, layout pieces)
- `styles.ts` -- style objects
- `useComponentName.ts` -- dedicated hook (optional)
- Page components go in `src/components/pageComponents/<pagename>/`
- Shared/reusable components go in `src/components/sharedComponents/`

## Key Patterns

### Adding a New Contract

1. Save ABI in `src/constants/contracts/abis/YourContract.ts` (export as const)
2. Register in `src/constants/contracts/contracts.ts` with name, ABI, and addresses per chain
3. Run `pnpm wagmi-generate` to auto-generate typed hooks in `src/hooks/generated.ts`

The contracts array uses `as const satisfies ContractConfig<Abi>[]` for full type inference. Follow this pattern.

Use `getContract(name, chainId)` to retrieve a typed contract config at runtime (e.g., `getContract('WETH', sepolia.id)` returns `{ abi, address }`).

### Generated Hook Naming Convention

`pnpm wagmi-generate` produces hooks in `src/hooks/generated.ts` following this naming pattern:

- `useReadContractNameFunctionName` -- standard read hook
- `useWriteContractNameFunctionName` -- write hook
- `useSuspenseReadContractNameFunctionName` -- Suspense-enabled read hook
- `useSimulateContractNameFunctionName` -- simulation hook

Example: contract named `WETH` with function `allowance` generates `useReadWethAllowance`, `useWriteWethApprove`, `useSuspenseReadWethAllowance`, etc.

### Adding a New Route/Page

1. Create route file in `src/routes/` using `.lazy.tsx` extension for code-split lazy loading (e.g., `yourpage.lazy.tsx`). Use plain `.tsx` only for routes that must be eagerly loaded.
2. Create page component in `src/components/pageComponents/yourpage/`
3. Route tree auto-generates via TanStack Router plugin

### Adding a New Network

1. Import chain from `viem/chains` in `src/lib/networks.config.ts`
2. Add to the appropriate chain array (devChains or prodChains)
3. Add transport entry with optional custom RPC from env
4. Add RPC env var to `src/env.ts` if needed

### Environment Variables

- Defined and validated with Zod in `src/env.ts`
- Access via `import { env } from '@/src/env'` (never `import.meta.env` directly)
- New variables MUST be added to both `.env.example` and `src/env.ts`

### Web3 Status

- Use `useWeb3Status()` for unified wallet/chain state
- Use `useWeb3StatusConnected()` when wallet connection is guaranteed (throws otherwise)
- Use `withWalletStatusVerifier()` HOC to gate components behind wallet connection

### Suspense and Error Boundaries

- Contract read hooks use React Suspense via custom `reactSuspenseRead` wagmi plugin
- `useTokenLists` is Suspense-based
- Wrap async components with `withSuspenseAndRetry()` from `src/utils/suspenseWrapper.tsx` -- this is the primary error boundary pattern. It integrates with TanStack Query's `QueryErrorResetBoundary` for automatic retry on query failures.
- `withSuspenseAndRetry` accepts: `suspenseFallback`, `errorFallback`, `defaultFallbackFormat` ('dialog' | 'default'), `spinnerSize`
- Simpler variant `withSuspense()` available when retry logic is not needed

### Transaction Handling

Two components for two different jobs -- both auto-wrap with `withWalletStatusVerifier` (do not double-wrap):

- **`TransactionButton`** -- for on-chain transactions. Takes `transaction: () => Promise<Hash>` prop. Attach a `methodId` string property to the function for notification context. Calls `onMined(receipt)` on confirmation.
- **`SignButton`** -- for message signing only (uses `useSignMessage`). Takes `message`, `onSign`, `onError` props.

`TransactionNotificationProvider` handles toast notifications and tx watching for both.

### Token Management

- Token lists configured in `src/constants/tokenLists.ts`
- Fetch with `useTokenLists()` hook (Suspense-based). Tokens are deduplicated by `chainId + address` (lowercased). Native tokens are auto-injected for each configured chain.
- Token prices/balances via `useTokens()` (LI.FI SDK)
- `TokenInput` + `useTokenInput()` for amount input with validation
- For raw bigint/decimal conversion in inputs, use `BigNumberInput` (wraps viem's `parseUnits`/`formatUnits`). Never use `parseFloat` or `Number()` for token amounts.

### Wallet Provider

- Default: ConnectKit (configured in `src/lib/wallets/connectkit.config.tsx`)
- To switch: modify `src/providers/Web3Provider.tsx` imports
- Alternatives available: RainbowKit (`rainbowkit.config.tsx`), Web3Modal (`web3modal.config.tsx`)

## Auto-Generated Files (Do Not Edit, Do Not Commit)

These files are gitignored and regenerated from source:

- `src/hooks/generated.ts` -- regenerate with `pnpm wagmi-generate`
- `src/routeTree.gen.ts` -- regenerate with `pnpm routes:generate`
- `src/subgraphs/gql/` -- regenerate with `pnpm subgraph-codegen`

## Utilities

- **Number formatting:** Use `formatNumber()` from `src/utils/numberFormat.ts` with the appropriate `NumberType` context (`TokenTx`, `FiatTokenPrice`, `SwapPrice`, `PortfolioBalance`, etc.). Never use raw `.toString()` or `.toFixed()` for user-facing numbers.
- **Explorer links:** `getExplorerLink()` from `src/utils/getExplorerLink.ts`
- **String truncation:** `truncateStringInTheMiddle()`, `getTruncatedHash()` from `src/utils/strings.ts`
- **Native token check:** `isNativeToken()` from `src/utils/address.ts`
- **Hash detection:** `detectHash()` from `src/utils/hash.ts` -- identifies ENS names, transaction hashes, contract addresses, and EOAs

## Testing

- Framework: Vitest with jsdom environment
- Test files: colocated as `*.test.ts` / `*.test.tsx`
- Mocking: `vi.mock()` for module mocks
- Component testing: Testing Library + jest-dom matchers
- Run: `pnpm test` (single run) or `pnpm test:watch`
Loading