---
title: Frontend Service — amn-frontend
tags: [service, frontend, nextjs, react, web3, telegram]
created: 2026-06-08
updated: 2026-06-08
---

# Frontend Service — amn-frontend

## 1. Overview

`amn-frontend` is the primary user-facing application for the Amanat (AMN) escrow marketplace. It serves buyers, sellers, and admins through a unified Next.js 16 App Router application with a Persian-first (RTL) UI.

| Field | Value |
|---|---|
| Package name | `amn-frontend` |
| Version | **2.10.5** |
| Status | Active — deployed on `dev.amn.gg` |
| Framework | Next.js 16 (App Router + Turbopack), React 19, TypeScript strict |
| Dev port | `8083` (both local and Docker) |
| Package manager | `yarn@1.22.22` |
| Node requirement | `>=20` (host runs v26.0.0) |
| Repo | `git@git.manko.yoga:222/nick/frontend.git` |

The app covers the full escrow lifecycle: request creation, multi-seller offer collection, negotiation, on-chain payment (BSC/ETH/Base), delivery confirmation, dispute handling, loyalty points, and a Telegram Mini App shell for mobile-native access.

---

## 2. Tech Stack

### Core

| Layer | Library / Version | Notes |
|---|---|---|
| Framework | `next@^16.1.1` | App Router, Turbopack dev, standalone output |
| UI runtime | `react@^19.1.0` + `react-dom@^19.1.0` | |
| Language | TypeScript `^6.0.3` strict | `noEmit` check required before push |
| Component library | `@mui/material@^9.0.1` | MUI v9 + `@mui/lab`, `@mui/x-data-grid`, `@mui/x-date-pickers`, `@mui/x-tree-view` |
| Styling | `@emotion/react` + `@emotion/styled` + `stylis-plugin-rtl` | RTL support via stylis |
| Animation | `framer-motion@^12.13.0` | |
| Icon system | `@iconify/react@^6.0.0` | |

### Data Fetching & State

| Layer | Library | Notes |
|---|---|---|
| Server-state cache | `@tanstack/react-query@^5.83.0` | Primary async state manager |
| Lightweight fetch | `swr@^2.3.3` | Used in some hooks alongside RQ |
| HTTP client | `axios@^1.11.0` | Centralized instance with interceptors in `src/lib/axios.ts` |
| Forms | `react-hook-form@^7.77.0` + `zod@^4.0.10` + `@hookform/resolvers@^5.0.1` | |
| Real-time | `socket.io-client@^4.8.1` | `src/contexts/` socket context; used for request/offer/chat events |

### Web3

| Layer | Library | Notes |
|---|---|---|
| Wallet connection | `wagmi@^2.19.5` | Primary Web3 state manager |
| EVM low-level | `viem@^2.31.7` | ABI encoding, RPC calls |
| Compat layer | `ethers@^6.15.0` | Legacy compatibility |
| Chain indexing | `alchemy-sdk@^3.6.1` | Mainnet / Sepolia / Polygon queries |
| TON wallet | `@tonconnect/ui-react@^2.4.4` + `@ton/core@^0.63.1` | TON Connect in Telegram Mini App |
| Hardware wallet | `@trezor/connect-web@^9.7.3` | Trezor signing flow |

### Internationalization & Localisation

| Layer | Library | Notes |
|---|---|---|
| i18n engine | `i18next@^26.3.0` + `react-i18next@^17.0.8` | |
| Language detection | `i18next-browser-languagedetector@^8.1.0` | |
| Lazy loading | `i18next-resources-to-backend@^1.2.1` | |
| Persian date | `date-fns-jalali@^4.1.0-0` | Jalali calendar date formatting |
| RTL styling | `stylis-plugin-rtl@^2.1.1` | Emotion cache flips properties for RTL |

### Observability & Testing

| Layer | Library | Notes |
|---|---|---|
| Error tracking | `@sentry/nextjs@^10.22.0` | Configured in `src/instrumentation.ts` |
| Unit tests | `jest@^30.4.2` + `@testing-library/react@^16.3.0` | |
| E2E tests | `@playwright/test@^1.56.1` | `e2e/` directory; performance spec included |
| Notifications | `notistack@^3.0.2` + `sonner@^2.0.3` | Toast system |

### Editor & Rich Content

| Layer | Library | Notes |
|---|---|---|
| Rich text | `@tiptap/react@^3.23.6` + extensions | Code blocks, links, images, alignment, underline |
| Markdown render | `react-markdown@10.1.0` + rehype plugins | With GFM, syntax highlight, sanitization |
| Maps | `mapbox-gl@^3.12.0` + `react-map-gl@^8.0.4` | Address / delivery location picker |
| Charts | `react-apexcharts@^2.1.0` | Dashboard KPI charts |
| Carousels | `embla-carousel-react@8.6.0` | Auto-scroll and autoplay plugins |

---

## 3. App Router Page Structure

The Next.js App Router root is `src/app/`. Pages are thin wrappers that import a View component from `src/sections/<feature>/view/`. No business logic lives in `page.tsx` files.

### Top-level routes

| Route segment | Type | Purpose |
|---|---|---|
| `/` | Public | Landing / marketing page |
| `/api/health` | API route | Health check endpoint |
| `/api/llm` | API route | LLM proxy (amanat-assist integration) |
| `/auth/jwt/*` | Auth | Sign-in, sign-up, verify email, reset password, update password |
| `/checkout/` | Protected | Checkout flow entry (redirects to payment) |
| `/dashboard/` | Protected | Main authenticated shell (see sub-routes below) |
| `/design-preview/` | Dev | Component / theme preview (non-production) |
| `/error/` | Public | Global error page |
| `/payment/` | Protected | Payment status / callback landing |
| `/post/[slug]` | Public | Blog / post reader |
| `/shop/[seller]/[id]` | Public | Public seller shop and product view |
| `/telegram/` | Mini App | Telegram Mini App shell (dedicated layout, see §7) |
| `not-found.tsx` | Public | 404 page |

### Dashboard sub-routes (`/dashboard/*`)

All dashboard routes are wrapped in `AuthGuard` + `EmailVerificationGuard`.

| Sub-route | Purpose |
|---|---|
| `account/` | Profile, avatar, address book, notification prefs, passkey, wallet linking |
| `admin/` | Admin control panel |
| `assist/` | AI assistant chat (amanat-assist integration) |
| `chat/` | Real-time escrow negotiation chat |
| `disputes/` | Dispute hub — raise, view, respond |
| `payment/` | Payment history and detail view |
| `points/` | Loyalty hub — transaction log, referral tracking, level tiers |
| `post/` | Admin blog editor (Tiptap) |
| `request/` | Buyer purchase request management (create, track, accept offer) |
| `request-template/` | Seller request templates management |
| `seller/` | Seller profile and analytics |
| `shop-settings/` | Seller shop configuration (name, policies, payment rails) |
| `shops/` | Browse shops / checkout within dashboard scope |
| `user/` | Admin user management |

---

## 4. Key Sections & Features

### Marketplace

- `src/sections/shop-settings/` — seller configures shop, accepted payment chains/tokens, delivery policy.
- `src/sections/request/` — core escrow lifecycle feature. Status flow:
  ```
  pending_payment → pending → active → received_offers → in_negotiation
  → payment → processing → delivery → delivered → confirming → seller_paid → completed
  (or cancelled at most stages)
  ```
- Shared status/urgency color and label maps live in `src/sections/request/constants.ts`. Do not redefine per-view; use `getStatusColor / getStatusLabel / getUrgencyColor / getUrgencyLabel`.
- Role-based views (buyer / seller / admin) dispatched from `role-based-<feature>-view.tsx` components.

### Escrow Flow

The escrow flow spans multiple sections:

1. **Buyer** creates a purchase request (`/dashboard/request/new`) — wizard in `src/sections/request/components/steps/`.
2. **Sellers** receive notifications via Socket.io and submit offers (`received_offers` state).
3. **Negotiation** phase: real-time chat (`/dashboard/chat/`) with offer counter-proposals.
4. **Payment**: buyer pays on-chain (BSC primary, ETH/Base/Polygon/Arbitrum supported). Funds held in escrow wallet (`NEXT_PUBLIC_ESCROW_WALLET_ADDRESS`). USDT is the primary escrow currency; BSC USDT uses 18 decimals (non-standard — handled in `src/utils/currencyUtils.ts`).
5. **Delivery & confirmation**: seller marks delivered, buyer confirms → `confirming → seller_paid → completed`.
6. **Disputes**: either party can raise at `src/sections/dispute/`.

### Dashboard & Admin

- Overview tiles with ApexCharts KPI cards.
- Admin panel: user management, shop review, dispute arbitration, blog post management.
- Points / loyalty system: transaction ledger, referral tracking, tier levels at `src/sections/points/`.
- AI assist panel: embedded `amanat-assist` chat at `/dashboard/assist/`.

### Telegram Mini App

See §7 for full detail.

---

## 5. State Management

The app uses a layered approach — no single global store:

| Layer | Tool | Scope |
|---|---|---|
| Server state & cache | `@tanstack/react-query` | All API calls — fetching, mutations, invalidation |
| Supplementary fetch | `swr` | Some lightweight hooks |
| Local component state | `React.useState` / `useReducer` | Component-local UI state |
| Cross-tree shared state | React Context | Socket connection (`src/contexts/`), Auth (`src/auth/context/`), Web3, Settings drawer, Localization |
| Form state | `react-hook-form` | All form instances, with `zod` schemas as resolvers |
| Settings (theme/locale) | Context + `localStorage` | Theme mode, layout direction, color preset, font — managed by `src/settings/` |

There is no Zustand or Redux in the dependency tree. Global state is passed via Context providers stacked in `src/app/layout.tsx`.

Key contexts:

- `SocketContext` (`src/contexts/`) — wraps `socket.io-client`, exposes live event subscriptions.
- `AuthContext` (`src/auth/context/`) — JWT session, user object, sign-in/out actions.
- `Web3Context` / wagmi `WagmiProvider` (`src/web3/context/`) — wallet connection, chain switching.
- `SettingsContext` (`src/settings/`) — UI preferences (RTL, color scheme, font).
- `LocalizationProvider` (`src/locales/`) — i18next + MUI date picker locale.

---

## 6. Internationalization

The app is RTL-first with Persian (Farsi) as the primary production language.

| Aspect | Implementation |
|---|---|
| Engine | `i18next` + `react-i18next` |
| Supported languages | `fa` (Persian), `ar` (Arabic), `en` (English), `fr` (French), `cn` (Chinese), `vi` (Vietnamese) |
| Translation files | `src/locales/langs/<lang>/*.json` — split by feature namespace |
| RTL flip | `stylis-plugin-rtl` applied to the Emotion cache — physical CSS properties (margin-left, padding-right, etc.) are automatically mirrored |
| LTR islands | Inline `dir="ltr"` on elements containing URLs, wallet addresses, token amounts, or other inherently LTR content |
| Persian calendar | `date-fns-jalali` for Jalali date formatting; MUI date pickers use the Jalali locale adapter |
| Direction state | Controlled via `SettingsContext` — users can toggle in the settings drawer |
| Config | `src/locales/locales-config.ts` + `src/locales/i18n-provider.tsx` |

Language detection priority: URL `?lng=` param → browser `Accept-Language` → localStorage fallback → `fa`.

---

## 7. Telegram Mini App Integration

The Telegram Mini App (TMA) is a first-class feature with a dedicated route segment, layout, and 40+ purpose-built components.

### Loading & Auth Flow

- The TMA loads via Telegram's `webApp.openWebApp()` into the `/telegram/` route.
- The root layout at `src/app/telegram/layout.tsx` provides a minimal provider stack:
  - `TonConnectUIProvider` (TON wallet)
  - No standard app shell (no top nav, no side drawer) — uses native Telegram chrome instead.
- User identity: `window.Telegram.WebApp.initData` is parsed by `src/utils/telegram-webapp.ts` (a custom wrapper around the `window.Telegram` global — **no `@twa-dev` or `@telegram-apps` SDK package** is used).
- Auth is linked to the existing JWT session: on first open the app prompts the user to connect their AMN account (onboarding sheet). Subsequent opens re-use the stored token.

### Key File Locations

| Path | Purpose |
|---|---|
| `src/app/telegram/layout.tsx` | TMA root layout — minimal providers |
| `src/app/telegram/page.tsx` | TMA entry point |
| `src/utils/telegram-webapp.ts` | Custom `window.Telegram.WebApp` wrapper / SDK util |
| `src/sections/telegram/` | All TMA feature code |
| `src/sections/telegram/view/` | ~18 view components (one per TMA screen) |
| `src/sections/telegram/components/` | ~28 TMA-specific UI primitives |
| `src/sections/telegram/hooks/` | TMA-scoped hooks including `use-telegram-live-context` |
| `src/sections/telegram/telegram-shell-css.ts` | Native Telegram shell CSS variables integration |

### TMA Views

| View file | Screen |
|---|---|
| `telegram-mini-app-view.tsx` | Main shell / router (23 KB — primary orchestrator) |
| `telegram-home-view.tsx` | Home tab |
| `telegram-shop-view.tsx` | Shop list |
| `telegram-seller-shop-view.tsx` | Individual seller shop products |
| `telegram-cart-view.tsx` | Cart |
| `telegram-checkout-view.tsx` | Checkout |
| `telegram-payment-view.tsx` | Payment status |
| `telegram-requests-view.tsx` | Buyer requests list |
| `telegram-request-detail-view.tsx` | Request detail + offer management (31 KB) |
| `telegram-new-request-view.tsx` | New request wizard |
| `telegram-template-detail-view.tsx` | Seller template detail |
| `telegram-chat-view.tsx` | In-app chat thread list |
| `telegram-chat-thread-view.tsx` | Single chat thread |
| `telegram-archived-chats-view.tsx` | Archived chats |
| `telegram-account-view.tsx` | Account settings (18 KB) |
| `telegram-addresses-view.tsx` | Address book (15 KB) |
| `telegram-points-view.tsx` | Loyalty points |
| `telegram-notifications-view.tsx` | Notification centre |
| `telegram-settings-view.tsx` | App settings (14 KB) |

### TMA-specific Components

Key primitives: `telegram-chat-row`, `telegram-request-stepper`, `telegram-onboarding-sheet`, `telegram-tab-bar`, `telegram-header`, `telegram-quick-actions`, `telegram-cart-fab`, `telegram-support-fab`, `telegram-welcome-banner`, `telegram-unlinked-state`, `telegram-unsupported-state`.

### TON Wallet in TMA

TON Connect (`@tonconnect/ui-react`, `@ton/core`) is active only inside the TMA layout. BSC payments via wagmi/viem are also available in-TMA but TON is the preferred rail for Telegram users.

---

## 8. Web3 Integration

All Web3 code lives under `src/web3/`.

### Architecture

```
src/web3/
├── config.ts                 # WEB3_CONFIG — chains, WalletConnect project ID
├── index.ts                  # Public barrel
├── types.ts                  # Shared Web3 types
├── utils.ts                  # Misc helpers
├── payment-rails.ts          # Chain+token routing logic
├── decentralizedPayment.ts   # Core payment execution (16 KB)
├── web3Service.ts            # Service layer (9 KB)
├── paymentBackendService.ts  # Backend sync after on-chain tx (12 KB)
├── tonconnect-provider.tsx   # TonConnect provider wrapper
├── context/                  # Web3Context provider
├── contracts/                # ABI definitions
├── hooks/
│   ├── use-web3-wagmi.ts     # wagmi-based wallet + tx hooks (5.5 KB)
│   ├── use-alchemy.ts        # Alchemy SDK hooks — balance, tx history (3.8 KB)
│   ├── use-chainlink.ts      # Chainlink price feed hooks (2.6 KB)
│   └── use-web3-context.ts   # Context consumer hook
├── services/                 # Additional service modules
├── trezor/                   # Trezor Connect integration
└── utils/                    # Chain-specific utilities
```

### Supported Chains

Declared in `WEB3_CONFIG.supportedChains`: **BSC** (default, lowest fees), Base, Polygon, Arbitrum, Ethereum.

Primary escrow payments run on BSC. BSC USDT is 18 decimals (non-standard; handled in `src/utils/currencyUtils.ts` — do not hardcode decimals).

### Wallet Support

| Wallet | Integration |
|---|---|
| MetaMask / injected | wagmi `injected()` connector |
| WalletConnect | wagmi WalletConnect connector (`NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID`) |
| Trezor | `@trezor/connect-web` in `src/web3/trezor/` |
| TON wallet | `@tonconnect/ui-react` (TMA only) |

### Oracle / Price Feeds

- Chainlink price feeds via `use-chainlink.ts` — used for USDT/USD peg monitoring.
- Alchemy SDK (`alchemy-sdk`) for on-chain data queries (balances, tx receipts).
- Depeg protection feature in development — see `nick-doc/` oracle depeg protection design doc.

---

## 9. CI/CD

### Pipeline

CI is managed by Woodpecker at `frontend/.woodpecker/production.yml`.

**Trigger:** push to `main` or `master` branch.

**Agent:** `platform: linux/arm64` (netcup agent on `89.58.32.32`).

**Steps:**

| Step | Image | Action |
|---|---|---|
| `get-version` | `node:22-alpine` | Reads `package.json` version → writes `dev-<version>` to `.tags` |
| `build-and-deploy` | `docker:27-cli` | `docker build -t git.tbs.amn.gg/escrow/frontend:dev .` then `docker compose up -d --no-deps --pull never frontend` against `/opt/escrow-dev/docker-compose.yml` |
| `notify` | `node:22-alpine` | Posts Telegram notification (success or failure) via `scripts/ci/tg-notify.cjs` using `TG_TOKEN` + `TG_USERS` secrets |

**Important CI notes:**

- The image is built locally on the host — it does **not** pull from a registry. `docker-compose.override.yml` sets `pull_policy: never`.
- Turbopack is dev-only. The production build uses standard `next build` (webpack).
- `next build` runs a strict TypeScript type-check. The build fails on type errors.
- **Always bump `package.json` version before pushing** to `main`/`master`. Docker tags use `dev-<version>`. Reusing the same version overwrites the previous image tag and breaks rollback.
- A CI green check does not guarantee the image was pushed to the registry. Verify the registry tag manually if deployment seems stale.

### Docker Build

- Output mode: `standalone` (set in `next.config.js`).
- Start command: `PORT=8083 node .next/standalone/server.js`.
- Post-build: `cp -r .next/static .next/standalone/.next/ && cp -r public .next/standalone/` (required for static assets with standalone output).
- Build cache: `--mount=type=cache` for apk, yarn, and `.next/cache` — incremental Next.js rebuilds on unchanged packages.

---

## 10. Local Development Quick-Start

```bash
# Prerequisites: Node >=20, yarn 1.22.22

cd frontend/

# Install dependencies
yarn install

# Copy env file and fill in values
cp .env.local.example .env.local
# Edit .env.local — see §11 for required vars

# Start dev server (Turbopack, port 8083)
yarn dev

# Alternative: webpack (slower, more compatible)
yarn dev:webpack

# Type check (must pass before push)
npx tsc --noEmit --ignoreDeprecations 6.0

# Lint
yarn lint
yarn lint:fix

# Unit tests
yarn test

# E2E tests (requires running app)
yarn playwright:install
yarn test:e2e

# Production build (validates types + builds)
yarn build

# Run standalone production build
yarn start
```

**Note:** The dev server binds to `http://localhost:8083`. When proxied via infra-caddy on the dev server, it maps to `https://dev.amn.gg`.

---

## 11. Environment Variables

All public vars are prefixed `NEXT_PUBLIC_` and baked into the client bundle at build time.

| Variable | Required | Description |
|---|---|---|
| `NEXT_PUBLIC_APP_NAME` | Yes | Application display name (e.g. `Amanat`) |
| `NEXT_PUBLIC_APP_VERSION` | Yes | App version string — should match `package.json` version |
| `NEXT_PUBLIC_BACKEND_URL` | Yes | Base URL for backend API (e.g. `https://api.dev.amn.gg`) |
| `NEXT_PUBLIC_API_URL` | Yes | API endpoint root (often same as `BACKEND_URL` + `/api`) |
| `NEXT_PUBLIC_SOCKET_URL` | No | Socket.IO server URL — falls back to `NEXT_PUBLIC_BACKEND_URL` |
| `NEXT_PUBLIC_ESCROW_WALLET_ADDRESS` | Yes | On-chain escrow holding wallet address |
| `NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID` | Yes | WalletConnect Cloud project ID |
| `NEXT_PUBLIC_ALCHEMY_API_KEY_MAINNET` | Yes | Alchemy API key for Ethereum mainnet |
| `NEXT_PUBLIC_ALCHEMY_API_KEY_SEPOLIA` | Yes | Alchemy API key for Sepolia testnet |
| `NEXT_PUBLIC_ALCHEMY_API_KEY_POLYGON` | Yes | Alchemy API key for Polygon |
| `NEXT_PUBLIC_MAPBOX_API_KEY` | No | Mapbox GL token for map components |
| `NEXT_PUBLIC_ASSETS_DIR` | No | Custom assets base URL — defaults to empty (local `/public`) |
| `BUILD_STATIC_EXPORT` | No | Set `true` to enable static export mode |
| `NODE_ENV` | Auto | Set by Next.js (`development` / `production`) |

Actual values for the dev deployment are stored in `~/.agentSecrets/escrow/CLAUDE.md` (not in the repo).

---

## 12. Known Issues / Open Items

| # | Issue | Status |
|---|---|---|
| 1 | **Socket room scoping** — global payment socket broadcasts previously wiped every user's cart. A provider gate was added in frontend `v2.8.4` to filter by provider. Backend-side room scoping is still an open follow-up. | Open |
| 2 | **Backend rate limiter on GET /payment/:id** — `paymentLimiter` (30 req/15 min) applies to the payment status poll endpoint. Results in 429 during rapid callback polling, leaving payments stuck in "processing". Fix is on backend side but frontend polling interval could be increased as a mitigation. | Open (backend fix pending) |
| 3 | **Offer rejection UI** — "all sellers stuck at step 4" was a UI-only bug; backend rejects and notifies correctly. Telegram seller step must use `mojtaba`'s `StepContext` (introduced in `fe v2.9.13`). | Resolved in v2.9.13 |
| 4 | **Cart wipe regression risk** — any new global socket event handler must be scoped by `provider:` to avoid touching RN or other payment records. | Ongoing convention |
| 5 | **Performance is network-bound** — Mongo API profiling shows 300–800 ms response times due to WAN RTT (~235 ms). Server-side processing is 3–12 ms. Frontend-side CDN / edge caching is the recommended fix; DB migration will not help. | Open |
| 6 | **Oracle depeg protection** — server-side oracle quoting for multi-currency pricing + stablecoin depeg protection is designed and approved. Build starts on a new dev branch. | In progress |
| 7 | **Multi-chain for amn.scanner** — the in-house scanner pay-in path is not yet multi-chain; verify scanner watches mainnet addresses before enabling multi-chain selection in the UI for scanner provider. | Open |
| 8 | **Parallel agent pushes** — a second agent (moojttaba) pushes to the same branches. Always `git fetch --rebase` before pushing. Version-bump conflicts are expected. | Ongoing |
| 9 | **Tiptap / rich text in TMA** — the Tiptap editor is desktop-optimised; its usability on mobile Telegram is untested at scale. | Not verified |
| 10 | **`design-preview/` route** — present in the app router but should be excluded or protected in production builds. | Low priority |
