Skip to content
Open
Show file tree
Hide file tree
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
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
231 changes: 231 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,231 @@
# dAppBooster

> This file is mirrored as `CLAUDE.md` for Claude. Keep both files in sync when making changes.

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
Loading