Frontend (web app)
apps/web is a Next.js 14 (App Router) app, port 4300. It reuses screens and hooks from apps/mobile-version so the core flows have one implementation across desktop and mobile.
Route groups
app/
├─ (auth)/ sign-in, sign-up (Privy Google/email)
├─ (reused)/ agreements, send, receive, activity, market,
│ withdraw, account, notifications, onboarding (from mobile-version)
└─ (web)/ overview, custody, distributions, settlements, reports (bespoke desktop)
Auth wiring
flowchart TB
P["PrivyProvider"] --> WG["WagmiProvider (@privy-io/wagmi)"]
WG --> AG["AuthGate"]
AG --> SYNC["usePrivyWagmiSync()"]
AG --> APP["App / pages"]
components/auth/Providers.tsxwraps the app inPrivyProvider→WagmiProvider.NEXT_PUBLIC_PRIVY_APP_IDis required at build time — without it the provider stack is dropped and every wagmi hook throws.AuthGate.tsxmountsusePrivyWagmiSync()and loads the user's profiles.WebAuthGuard.tsxredirects unauthenticated users (mounted inside the(web)providers).hooks/usePrivyWagmiSync.tsbridges the active Privy embedded wallet (for the current mode) into wagmi as the connected connector, and self‑heals the connection after disconnects (network blips) with bounded retries.- SIWE login (
useSiweLogin): get nonce → sign →POST /auth/login→ store JWT.
Dual‑mode (personal / business)
- The mode + profiles live in a Zustand store (
store/auth.store.tsfor the JWT/user, a mode/profile store for the active context). useActiveWallet()(reused from mobile) is the single source of truth for "which wallet am I acting as" — it returns{ address, privyWallet, canSign, mode, profileId, … }by combining the mode store, the user, and Privy's connected wallets, matched by address (HD index 0 personal / 1 business), never by array position.- Switching mode retargets balances, activity, and signing to the right wallet, and re‑runs the wagmi sync.
State management
Zustand stores under store/: auth.store (JWT + user), the mode/profile store, plus UI stores (theme, ops modals). Server data uses TanStack Query.
Reused mobile hooks (important ones)
| Hook | Role |
|---|---|
useActiveWallet |
Active wallet + canSign + mode + profileId |
useCustodyAction |
The escrow action primitive (approve + contract call + verify + confirm) used by every agreement button |
useAuthGate |
Route guard for authenticated screens |
useDashboardData |
Aggregated dashboard reads |
API clients
Thin wrappers in lib/ call the gateway with the JWT + X-AXON-Profile-Id (via lib/api.ts / apiFetch): funding-api.ts, settlements-api.ts, agreements client (from mobile), etc. Errors surface response.data.message; the custody hook maps low‑level wagmi errors (e.g. "Connector not connected") to friendly, retryable messages.
Styling
Tailwind CSS, AXON brand (mint #00d999 accent on dark/ink). Shared icons/primitives come from @axon/shared-ui.