> ## Documentation Index
> Fetch the complete documentation index at: https://docs.windrose.market/llms.txt
> Use this file to discover all available pages before exploring further.

# Web app

> Build and run the Next.js app, wire it to an indexer per chain, and understand what gates the chain switcher.

The app in `apps/web` is a Next.js App Router project using wagmi, viem, TanStack Query and RainbowKit. It serves every chain that has a deployment file at once; the top-bar switcher picks one and every hook and component reads that chain's RPC, addresses and indexer through `useChain()`.

## Environment

`apps/web/.env.example` documents every variable. `NEXT_PUBLIC_*` values are inlined at build time, so rebuild after changing any of them.

| Variable                                                                                                                                     | Default                                                                                     | Meaning                                                                                                                                                                                                 |
| -------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `NEXT_PUBLIC_CHAIN_ID`                                                                                                                       | `46630`                                                                                     | The *default* chain: what a first visit shows when nothing is remembered and no `?chain=` is in the URL, and the only chain the unsuffixed variables below apply to.                                    |
| `NEXT_PUBLIC_RPC_URL_<chainId>`                                                                                                              | the registry `rpc`                                                                          | RPC per chain. Point one at a local anvil that mimics the chain, for instance `NEXT_PUBLIC_RPC_URL_46630=http://127.0.0.1:8545`.                                                                        |
| `NEXT_PUBLIC_RPC_URL`                                                                                                                        |                                                                                             | RPC override for the default chain only.                                                                                                                                                                |
| `NEXT_PUBLIC_INDEXER_URL_<chainId>`                                                                                                          | none                                                                                        | That chain's Ponder GraphQL endpoint. The browser calls it, so it must be the public URL. A chain without its own variable shows on-chain data and "indexer offline" rather than another chain's index. |
| `NEXT_PUBLIC_INDEXER_URL`                                                                                                                    | `http://localhost:42069` in development                                                     | Indexer for the default chain only.                                                                                                                                                                     |
| `NEXT_PUBLIC_MULTICALL3`                                                                                                                     | unset (probe at runtime)                                                                    | Force a Multicall3 address for batched reads, or `none` to skip the probe. Applies to every chain.                                                                                                      |
| `NEXT_PUBLIC_FACTORY`, `NEXT_PUBLIC_ROUTER`, `NEXT_PUBLIC_REGISTRY`, `NEXT_PUBLIC_STATE_VIEW`, `NEXT_PUBLIC_USDC`, `NEXT_PUBLIC_USDC_SYMBOL` | unset                                                                                       | Fallback addresses for the default chain when its deployment file is absent.                                                                                                                            |
| `DEPLOYMENTS_DIR`                                                                                                                            | `packages/abis/deployments` (resolved through the workspace symlink or relative to the app) | Directory containing `<chainId>.json` when it lives elsewhere. Server-side only.                                                                                                                        |
| `NEXT_PUBLIC_SHOW_TESTNETS`                                                                                                                  | testnets shown when the default chain is one                                                | `1` lists the testnets in the switcher of a mainnet build.                                                                                                                                              |
| `NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID`                                                                                                       | unset                                                                                       | WalletConnect Cloud project id. Without it only injected (browser extension) wallets are offered.                                                                                                       |
| `NEXT_PUBLIC_DOCS_URL`                                                                                                                       | `https://docs.windrose.market`                                                              | Where the Docs links in the rail and footer, the "How it works" tile and the `/guide` redirect point.                                                                                                   |

## Build and start

```bash theme={"system"}
pnpm --filter @launchpad/web build      # runs scripts/gen-abis.mjs, eslint, next build
pnpm --filter @launchpad/web start      # next start (port 3000)
```

`pnpm dev` and `pnpm build` both start with `scripts/gen-abis.mjs`, which re-emits the JSON ABIs from `@launchpad/abis` as `as const` TypeScript (`src/lib/abis.generated.ts`, plus a union of every custom error so nested reverts decode) and writes `src/lib/chains.generated.ts` with the ids of the chains that have a deployment file. Run `pnpm --filter @launchpad/web gen` on its own after `pnpm abis` changes the ABIs.

On the production box pm2 runs the JS entry directly, because pnpm's `.bin` shims are shell scripts:

```js theme={"system"}
{
  name: "windrose-web",
  cwd: "apps/web",
  script: "node_modules/next/dist/bin/next",
  args: "start -p 3000",
  env: { NODE_ENV: "production" },
  max_memory_restart: "500M",
}
```

After changing `apps/web/.env.local`: rebuild, then `pm2 restart windrose-web`. If the build gets killed for lack of memory while three indexers backfill, stop one (`pm2 stop windrose-indexer-arc`), build, start it again.

## Which chains appear

Three things decide what the switcher offers:

1. **A deployment file at build time.** `scripts/gen-abis.mjs` scans `packages/abis/deployments/*.json` and bakes the chain ids into `src/lib/chains.generated.ts`; the wagmi config registers those chains when the module loads. Add a file, rebuild.
2. **Testnet visibility.** A mainnet default hides testnets unless `NEXT_PUBLIC_SHOW_TESTNETS=1`. A testnet default lists everything.
3. **Coming soon.** In a mainnet build, registry mainnets without a deployment (Arc, 5042, today) are listed as "Coming soon" and disabled.

`GET /api/chains` re-checks the files on every request so the UI can annotate a chain whose file appeared after the server started, and `GET /api/deployment?chainId=<id>` reads the addresses lazily (falling back to the `NEXT_PUBLIC_*` addresses for the default chain). Both are documented under [HTTP endpoints](/api).

<Warning>
  Next.js only inlines literal `process.env.NEXT_PUBLIC_*` reads, so a chain id needs a row in both `RPC_ENV` and `INDEXER_ENV` of `apps/web/src/lib/chain.ts` before `NEXT_PUBLIC_RPC_URL_<id>` and `NEXT_PUBLIC_INDEXER_URL_<id>` work in the browser. Add the rows when you [add a chain](/operate/add-a-chain).
</Warning>

## Copy driven by the registry

Everything that names the dollar, the chain, the gas token or the faucet comes from `packages/abis/chains.json` through `useChain()`: "Gas is paid in ETH", "Get test USDG", the faucet link, explorer links. Brand strings ("Windrose") are not. The dollar's logo is `dollar.logo` from the registry or `/tokens/<symbol>.svg`; the `CoinLogo` component falls back to initials when the file is missing.

## Token logos and token lists

```bash theme={"system"}
CHAIN_ID=4663 pnpm abis:logos                                   # one SVG per synthetic currency in apps/web/public/tokens/
TOKEN_LOGO_BASE=https://windrose.market/tokens/ CHAIN_ID=4663 pnpm abis:tokenlist   # Uniswap Token List in packages/abis/tokenlist/
```

`abis:logos` never overwrites an existing `USDC.svg` or `EURC.svg` unless `--force` is passed. `abis:tokenlist` reads names, symbols and decimals from the chain (falling back to the deployment file and `Currencies.sol` with `--offline`), names the file after the registry key (`robinhood.tokenlist.json`) and bumps the version per the token-list rules, so re-running is a no-op when nothing changed. The naming prefixes of the coins are read back from the deployment file (`synthNamePrefix`, `synthSymbolPrefix`).

## SEO files

* `/og.png` is the Open Graph image referenced by the root metadata.
* `robots.ts` allows everything except `/api/` and points at the sitemap.
* `sitemap.ts` lists the static pages plus one currency page (`/c/<CODE>`) per registered currency of the default chain, read from its deployment file, and revalidates hourly.

## Docs links

The Docs entry in the rail, the footer link and the "How it works" tile open `NEXT_PUBLIC_DOCS_URL` in a new tab, and `/guide` (the old in-app docs page) redirects there, so bookmarks keep working. The docs are hosted by Mintlify at `docs.windrose.market` and live in `apps/docs` of this repository; see [Self-hosting](/operate/self-hosting) for the DNS shape.
