This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
LI.FI Widget monorepo — a cross-chain DeFi swap/bridge widget supporting Ethereum, Solana, Bitcoin, and Sui ecosystems. Managed with pnpm workspaces, Lerna (independent versioning), and TypeScript composite builds.
# Development
pnpm dev # Start widget-playground-vite on port 3000
pnpm dev:next # Start Next.js playground
# Building
pnpm build # Build all packages in parallel
# Code Quality
pnpm check # Biome lint + format check
pnpm check:write # Auto-fix Biome issues
pnpm check:types # TypeScript check all packages in parallel
pnpm check:circular-deps # Detect circular deps via madge
# Single-package type check
pnpm --filter @lifi/widget check:types
# Testing (vitest)
pnpm --filter @lifi/widget test # Run widget tests
pnpm --filter @lifi/widget-light test # Run widget-light tests
# Unused code detection
pnpm knip:check- tsdown (powered by rolldown) builds all library packages. Config in each package's
tsdown.config.ts. - Vite builds app packages (
widget-embedded,widget-playground-vite). These are NOT built with tsdown. isolatedDeclarations: trueis enabled in roottsconfig.json. All exported declarations in library packages must have explicit type annotations:- Exported functions must have explicit return types (
: JSX.Element,: void, etc.) - Exported
createContext<T>()results needContext<T>annotation - Exported
styled(Component)(...)results needReact.FC<Props>annotation - Use proper
import type { Foo } from '...'— never inlineimport('...').Fooin type positions
- Exported functions must have explicit return types (
- App packages (
widget-embedded,widget-playground-vite,examples/) override withisolatedDeclarations: falsein their tsconfig. - If
check:typesshows phantom errors after tsconfig changes, delete.tsbuildinfofiles and retry.
@lifi/widget-provider ← base contexts (Ethereum/Solana/Bitcoin/Sui)
↑
@lifi/widget-provider-{ethereum,solana,bitcoin,sui} ← chain-specific implementations
↑
@lifi/wallet-management ← wallet UI + connection logic
↑
@lifi/widget ← full widget (MUI, Zustand, TanStack Router, i18next)
@lifi/widget-light ← lightweight iframe host/guest bridge (zero dependencies)
@lifi/widget-embedded ← Vite app that runs inside the iframe (private)
widget-light provides an iframe-based integration where the widget runs inside an iframe (widget-embedded) and communicates with the host page via postMessage.
Message flow (defined in packages/widget-light/src/shared/protocol.ts):
- Guest sends
READY→ Host responds withINIT(config + ecosystem states) - Host sends
CONFIG_UPDATEwhen config changes after init - Guest forwards
RPC_REQUEST→ Host routes to ecosystem handler → sendsRPC_RESPONSE - Host pushes wallet
EVENTs to guest when wallet state changes - Guest sends
WIDGET_EVENTfor subscribed events → Host dispatches toWidgetLightEventBus - Host sends
WIDGET_EVENT_SUBSCRIBE/UNSUBSCRIBEto control which events the guest forwards
Key modules:
src/host/useWidgetLightHost.ts— React hook managing the host side (handshake, RPC routing, config updates)src/host/WidgetLightEventBus.ts— Module-level singleton with ref-counted subscriptionssrc/host/useWidgetLightEvents.ts— Public hook returning{ on, off }emittersrc/guest/GuestBridge.ts— Singleton managing guest-side communicationsrc/shared/widgetConfig.ts— Zero-dependency serializable config types (no React nodes, no callbacks, no MUI types)src/shared/widgetLightEvents.ts— Serializable event types mirroring widget events
- State: Zustand stores in
packages/widget/src/stores/(form, routes, chains, settings) - Routing: TanStack Router with page components in
src/pages/ - Theming: MUI v7 + Emotion; custom themes in
src/themes/ - Events: mitt event bus (
widgetEventssingleton insrc/hooks/useWidgetEvents.ts) - i18n: i18next with 20 language translations in
src/i18n/
QueryClient → Settings → WidgetConfig → I18n → Theme → SDK → Wallet → Store
- ESM only — all packages output to
dist/esm/. No CJS. - Biome for linting and formatting (not ESLint/Prettier). Single quotes, no semicolons, 2-space indent, trailing commas (ES5). Always run
pnpm check:writeafter making changes so Biome can auto-fix formatting. - Biome sorts imports — running
pnpm check:writemay reorder import/export statements. This is expected. - Conventional commits enforced by commitlint (
feat:,fix:,chore:, etc.). console.logis an error — onlyconsole.warnandconsole.errorare allowed (except inexamples/).useExhaustiveDependenciesanduseHookAtTopLevelare errors.- No unused variables or imports — enforced as errors.
- widget-light must have zero
dependencies— all types are self-contained duplicates. Chain-specific integrations are optional peer deps exposed via subpath exports (@lifi/widget-light/ethereum, etc.). - Package entry points use TypeScript source (
src/index.ts). Thescripts/formatPackageJson.jsrewrites paths todist/esm/at publish time. - TypeScript target is ES2020, module resolution is Bundler.
- Library packages use
tsdownwithunbundle: truemode. The widget package needsneverBundle: [/\.json$/]for i18n JSON files. - PR template at
.github/pull_request_template.md— always use it when creating PRs viagh pr create. packages/widget-embedded/README.md— main integration guide for widget-light (not a typical package readme).
Independent versioning via Lerna. Release flow:
pnpm release:version— bump versionspnpm release:build— build all packagesstandard-version— generate changelog- Git tag triggers GitHub Actions publish (
alpha,beta, orlatestnpm tags)
scripts/version.js generates src/config/version.ts per-package during build.