diff --git a/.claude/settings.json b/.claude/settings.json new file mode 100644 index 00000000..170d2ef8 --- /dev/null +++ b/.claude/settings.json @@ -0,0 +1,8 @@ +{ + "permissions": { + "allow": [ + "WebFetch(domain:docs.dappbooster.dev)", + "WebFetch(domain:components.dappbooster.dev)" + ] + } +} diff --git a/.gitignore b/.gitignore index 246a75ca..be85071d 100644 --- a/.gitignore +++ b/.gitignore @@ -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 diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 00000000..eceea487 --- /dev/null +++ b/AGENTS.md @@ -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//` +- 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[]` 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` 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` diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 00000000..cb3ccf5c --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,231 @@ +# dAppBooster + +> This file is mirrored as `AGENTS.md` for non-Claude AI agents. 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//` +- 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[]` 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` 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`