Skip to content
Merged
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
108 changes: 108 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Repository Overview

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.

## Commands

```bash
# Development
pnpm dev # Start widget-playground-vite on port 3000
pnpm dev:next # Start Next.js playground

# Building
pnpm build # Build all packages (libs first, then playgrounds/embedded)

# 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
```

## Architecture

### Package Dependency Graph

```
@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 iframe bridge

`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 with `INIT` (config + ecosystem states)
- Host sends `CONFIG_UPDATE` when config changes after init
- Guest forwards `RPC_REQUEST` → Host routes to ecosystem handler → sends `RPC_RESPONSE`
- Host pushes wallet `EVENT`s to guest when wallet state changes
- Guest sends `WIDGET_EVENT` for subscribed events → Host dispatches to `WidgetLightEventBus`
- Host sends `WIDGET_EVENT_SUBSCRIBE`/`UNSUBSCRIBE` to 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 subscriptions
- `src/host/useWidgetLightEvents.ts` — Public hook returning `{ on, off }` emitter
- `src/guest/GuestBridge.ts` — Singleton managing guest-side communication
- `src/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

### widget internals

- **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 (`widgetEvents` singleton in `src/hooks/useWidgetEvents.ts`)
- **i18n**: i18next with 20 language translations in `src/i18n/`

### Provider layering (widget)

QueryClient → Settings → WidgetConfig → I18n → Theme → SDK → Wallet → Store

## Conventions

- **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:write` after making changes** so Biome can auto-fix formatting.
- **Biome sorts imports** — running `pnpm check:write` may reorder import/export statements. This is expected.
- **Conventional commits** enforced by commitlint (`feat:`, `fix:`, `chore:`, etc.).
- **`console.log` is an error** — only `console.warn` and `console.error` are allowed (except in `examples/`).
- **`useExhaustiveDependencies`** and **`useHookAtTopLevel`** are 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`). The `scripts/formatPackageJson.js` rewrites paths to `dist/esm/` at publish time.
- TypeScript target is ES2020, module resolution is Bundler.
- **PR template** at `.github/pull_request_template.md` — always use it when creating PRs via `gh pr create`.
- `packages/widget-embedded/README.md` — main integration guide for widget-light (not a typical package readme).

## Release

Independent versioning via Lerna. Release flow:
1. `pnpm release:version` — bump versions
2. `pnpm release:build` — build all packages
3. `standard-version` — generate changelog
4. Git tag triggers GitHub Actions publish (`alpha`, `beta`, or `latest` npm tags)

`scripts/version.js` generates `src/config/version.ts` per-package during build.
10 changes: 5 additions & 5 deletions examples/connectkit/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
"preview": "vite preview"
},
"dependencies": {
"@lifi/wallet-management": "^3.22.5",
"@lifi/widget": "^3.40.6",
"@lifi/wallet-management": "^3.22.8",
"@lifi/widget": "^3.40.12",
"@mui/icons-material": "^7.3.6",
"@mui/material": "^7.3.6",
"@solana/wallet-adapter-base": "^0.9.27",
Expand All @@ -22,15 +22,15 @@
"mitt": "^3.0.1",
"react": "^19.2.4",
"react-dom": "^19.2.4",
"viem": "^2.45.1",
"viem": "^2.47.4",
"wagmi": "^2.19.4"
},
"devDependencies": {
"@types/react": "^19.2.13",
"@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^5.1.3",
"@vitejs/plugin-react": "^6.0.1",
"globals": "^17.3.0",
"typescript": "~5.9.3",
"vite": "^7.3.0"
"vite": "^8.0.0"
}
}
12 changes: 6 additions & 6 deletions examples/deposit-flow/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,24 +10,24 @@
"preview": "vite preview"
},
"dependencies": {
"@lifi/sdk": "^3.15.5",
"@lifi/widget": "^3.40.6",
"@lifi/sdk": "^3.16.1",
"@lifi/widget": "^3.40.12",
"@mui/material": "^7.3.6",
"@tanstack/react-query": "^5.90.20",
"events": "^3.3.0",
"react": "^19.2.4",
"react-dom": "^19.2.4",
"viem": "^2.45.1",
"viem": "^2.47.4",
"wagmi": "^2.19.4"
},
"devDependencies": {
"@types/events": "^3.0.3",
"@types/node": "^25.2.1",
"@types/node": "^25.5.0",
"@types/react": "^19.2.13",
"@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^5.1.3",
"@vitejs/plugin-react": "^6.0.1",
"typescript": "^5.9.3",
"vite": "^7.3.0",
"vite": "^8.0.0",
"vite-plugin-node-polyfills": "^0.25.0"
}
}
2 changes: 1 addition & 1 deletion examples/deposit-flow/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { nodePolyfills } from 'vite-plugin-node-polyfills'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react(), nodePolyfills()],
esbuild: {
oxc: {
target: 'esnext',
},

Expand Down
32 changes: 16 additions & 16 deletions examples/dynamic/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,19 @@
"preview": "vite preview"
},
"dependencies": {
"@bigmi/client": "^0.7.0",
"@bigmi/core": "^0.7.0",
"@bigmi/react": "^0.7.0",
"@dynamic-labs/bitcoin": "^4.60.0",
"@dynamic-labs/ethereum": "^4.60.0",
"@dynamic-labs/ethereum-aa": "^4.60.0",
"@dynamic-labs/sdk-react-core": "^4.60.0",
"@dynamic-labs/solana": "^4.60.0",
"@dynamic-labs/solana-core": "^4.60.0",
"@dynamic-labs/wagmi-connector": "^4.60.0",
"@lifi/sdk": "^3.15.5",
"@lifi/wallet-management": "^3.22.5",
"@lifi/widget": "^3.40.6",
"@bigmi/client": "^0.7.1",
"@bigmi/core": "^0.7.1",
"@bigmi/react": "^0.7.1",
"@dynamic-labs/bitcoin": "^4.67.2",
"@dynamic-labs/ethereum": "^4.67.2",
"@dynamic-labs/ethereum-aa": "^4.67.2",
"@dynamic-labs/sdk-react-core": "^4.67.2",
"@dynamic-labs/solana": "^4.67.2",
"@dynamic-labs/solana-core": "^4.67.2",
"@dynamic-labs/wagmi-connector": "^4.67.2",
"@lifi/sdk": "^3.16.1",
"@lifi/wallet-management": "^3.22.8",
"@lifi/widget": "^3.40.12",
"@mui/material": "^7.3.6",
"@solana/wallet-adapter-base": "^0.9.27",
"@solana/wallet-adapter-react": "^0.15.39",
Expand All @@ -35,16 +35,16 @@
"mitt": "^3.0.1",
"react": "^19.2.4",
"react-dom": "^19.2.4",
"viem": "^2.45.1",
"viem": "^2.47.4",
"vite-plugin-env-compatible": "^2.0.1",
"wagmi": "^2.19.4"
},
"devDependencies": {
"@types/react": "^19.2.13",
"@types/react-dom": "^19.2.3",
"@vitejs/plugin-react-swc": "^4.2.3",
"@vitejs/plugin-react": "^6.0.1",
"globals": "^17.3.0",
"typescript": "^5.9.3",
"vite": "^7.3.0"
"vite": "^8.0.0"
}
}
2 changes: 1 addition & 1 deletion examples/dynamic/vite.config.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import react from '@vitejs/plugin-react-swc'
import react from '@vitejs/plugin-react'
import { defineConfig } from 'vite'
import EnvCompatible from 'vite-plugin-env-compatible'

Expand Down
6 changes: 3 additions & 3 deletions examples/nextjs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@
"node": ">=21.0.0"
},
"dependencies": {
"@lifi/sdk": "^3.15.5",
"@lifi/widget": "^3.40.6",
"@lifi/sdk": "^3.16.1",
"@lifi/widget": "^3.40.12",
"@mui/material-nextjs": "^7.3.6",
"next": "^16",
"react": "^19.2.4",
"react-dom": "^19.2.4"
},
"devDependencies": {
"@types/node": "^25.2.1",
"@types/node": "^25.5.0",
"@types/react": "^19.2.13",
"@types/react-dom": "^19.2.3",
"typescript": "^5.9.3"
Expand Down
4 changes: 2 additions & 2 deletions examples/nextjs14-page-router/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@
"lint": "next lint"
},
"dependencies": {
"@lifi/widget": "^3.40.6",
"@lifi/widget": "^3.40.12",
"next": "^14",
"react": "^19.2.4",
"react-dom": "^19.2.4"
},
"devDependencies": {
"@types/node": "^25.2.1",
"@types/node": "^25.5.0",
"@types/react": "^19.2.13",
"@types/react-dom": "^19.2.3",
"eslint": "^8",
Expand Down
6 changes: 3 additions & 3 deletions examples/nextjs14/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@
"lint": "next lint"
},
"dependencies": {
"@lifi/sdk": "^3.15.5",
"@lifi/widget": "^3.40.6",
"@lifi/sdk": "^3.16.1",
"@lifi/widget": "^3.40.12",
"@mui/material-nextjs": "^7.3.6",
"next": "^14",
"react": "^19.2.4",
"react-dom": "^19.2.4"
},
"devDependencies": {
"@types/node": "^25.2.1",
"@types/node": "^25.5.0",
"@types/react": "^19.2.13",
"@types/react-dom": "^19.2.3",
"eslint": "^8",
Expand Down
6 changes: 3 additions & 3 deletions examples/nextjs15/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@
"node": ">=21.0.0"
},
"dependencies": {
"@lifi/sdk": "^3.15.5",
"@lifi/widget": "^3.40.6",
"@lifi/sdk": "^3.16.1",
"@lifi/widget": "^3.40.12",
"@mui/material-nextjs": "^7.3.6",
"next": "^15",
"react": "^19.2.4",
"react-dom": "^19.2.4"
},
"devDependencies": {
"@types/node": "^25.2.1",
"@types/node": "^25.5.0",
"@types/react": "^19.2.13",
"@types/react-dom": "^19.2.3",
"typescript": "^5.9.3"
Expand Down
13 changes: 6 additions & 7 deletions examples/nft-checkout/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
},
"author": "Eugene Chybisov <eugene@li.finance>",
"dependencies": {
"@lifi/sdk": "^4.0.0-alpha.13",
"@lifi/sdk": "4.0.0-beta.0",
"@lifi/wallet-management": "workspace:*",
"@lifi/widget": "workspace:*",
"@lifi/widget-provider-bitcoin": "workspace:*",
Expand All @@ -23,23 +23,22 @@
"@mui/icons-material": "^7.3.6",
"@mui/material": "^7.3.6",
"@mui/system": "^7.3.6",
"@opensea/seaport-js": "4.0.6",
"@opensea/seaport-js": "4.0.7",
"@tanstack/react-query": "^5.90.20",
"bignumber.js": "^9.3.0",
"ethers": "^6.16.0",
"events": "^3.3.0",
"react": "^19.2.4",
"react-dom": "^19.2.4",
"react-router-dom": "^7.13.0",
"viem": "^2.45.1",
"wagmi": "^3.1.0"
"viem": "^2.47.4",
"wagmi": "^3.5.0"
},
"devDependencies": {
"@esbuild-plugins/node-globals-polyfill": "^0.2.3",
"@vitejs/plugin-react-swc": "^4.2.3",
"@vitejs/plugin-react": "^6.0.1",
"source-map-explorer": "^2.5.3",
"typescript": "^5.9.3",
"vite": "^7.3.0",
"vite": "^8.0.0",
"vite-plugin-node-polyfills": "0.25.0",
"web-vitals": "^5.1.0"
},
Expand Down
18 changes: 2 additions & 16 deletions examples/nft-checkout/vite.config.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,16 @@
import { NodeGlobalsPolyfillPlugin } from '@esbuild-plugins/node-globals-polyfill'
import react from '@vitejs/plugin-react-swc'
import react from '@vitejs/plugin-react'
import { defineConfig } from 'vite'
import { nodePolyfills } from 'vite-plugin-node-polyfills'

// https://vitejs.dev/config/
export default defineConfig({
plugins: [nodePolyfills(), react()],
esbuild: {
oxc: {
target: 'esnext',
},
build: {
sourcemap: true,
},
optimizeDeps: {
esbuildOptions: {
define: {
global: 'globalThis',
},
plugins: [
NodeGlobalsPolyfillPlugin({
process: true,
buffer: true,
}),
],
},
},
server: {
port: 3000,
open: true,
Expand Down
4 changes: 2 additions & 2 deletions examples/nuxt/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@
"postinstall": "nuxt prepare"
},
"dependencies": {
"@lifi/widget": "^3.40.6",
"@lifi/widget": "^3.40.12",
"nuxt": "3.17.7",
"veaury": "^2.6.3",
"vite-plugin-node-polyfills": "^0.25.0",
"vue": "^3.5.27",
"vue": "^3.5.30",
"vue-router": "^4.6.4"
}
}
Loading