Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
67 commits
Select commit Hold shift + click to select a range
43ba82f
docs: add AGENTS.md with Cursor Cloud dev environment setup notes
cursoragent Jul 10, 2026
9d469e4
docs: document projected yield architecture
cursoragent Jul 24, 2026
d06facd
docs: clarify projected rate breakdown rows
cursoragent Jul 24, 2026
7806b25
fix: keep failed projected-rate reads scoped to their request
cursoragent Jul 27, 2026
ae35843
docs: describe the real projected-rate failure boundaries
cursoragent Jul 27, 2026
2b7a0cc
Revert "fix: keep failed projected-rate reads scoped to their request"
cursoragent Jul 27, 2026
5ec460b
docs: document projected-rate rejection paths as they stand
cursoragent Jul 27, 2026
4361300
chore(deps): bump valibot from 1.4.1 to 1.4.2
dependabot[bot] Jul 28, 2026
1534c44
docs: document V3-backed activity feed architecture
cursoragent Jul 31, 2026
c52b584
docs: document batch cart slot-hint and account prefetch
cursoragent Jul 31, 2026
77b1af5
Merge pull request #773 from euler-xyz/cursor/engineering-documentati…
Seranged Jul 31, 2026
c7d1ab5
fix: display oracle router activity events
Seranged Jul 31, 2026
0ad254b
fix: clarify oracle route activity
Seranged Jul 31, 2026
d546597
fix: show oracle route asset icons
Seranged Jul 31, 2026
cad7a54
docs: expand AGENTS.md with architecture, layout, server/proxy, and t…
cursoragent Jul 31, 2026
82f0c61
docs: address PR review feedback in AGENTS.md
cursoragent Jul 31, 2026
03ad429
fix: retain asset icons in activity details
Seranged Jul 31, 2026
a45b062
fix: align expanded activity assets
Seranged Jul 31, 2026
a3023da
fix: top-align expanded activity assets
Seranged Jul 31, 2026
33fa490
Merge pull request #759 from euler-xyz/cursor/engineering-documentati…
Seranged Jul 31, 2026
cfb5737
docs: rewrite golden-test section to match current development workflow
cursoragent Jul 31, 2026
cf120a1
fix: normalize activity event labels
Seranged Jul 31, 2026
2ec4d17
fix: stack expanded activity asset pairs
Seranged Jul 31, 2026
0c39b20
Merge pull request #721 from euler-xyz/cursor/setup-dev-environment-dd95
Seranged Jul 31, 2026
ba635a9
fixes
kanvgupta Jul 31, 2026
d560440
docs: document announcements and v3 proxy ops
cursoragent Jul 10, 2026
4bc1caa
docs: include activity and liquidations in V3 proxy allowlist
cursoragent Jul 31, 2026
c2594eb
docs: correct labels path-shape and V3 stale-fallback contracts
cursoragent Jul 31, 2026
72a036f
docs: consolidate labels cache notes with PR #739
cursoragent Jul 31, 2026
fc0dacc
Merge pull request #720 from euler-xyz/cursor/engineering-documentati…
Seranged Jul 31, 2026
351fabe
chore(deps-dev): bump brace-expansion from 1.1.14 to 1.1.18
dependabot[bot] Aug 4, 2026
e00aa79
feat: show uncovered losses on earn vault statistics
kasperpawlowski Aug 4, 2026
8bea11a
fix: withhold uncovered losses until the coverage read settles
kasperpawlowski Aug 4, 2026
791cbf8
fix: use normalized earn uncovered losses
Seranged Aug 5, 2026
883a730
Merge pull request #778 from euler-xyz/feat/earn-uncovered-losses
Seranged Aug 5, 2026
e42a4a2
fix: prevent unsafe REUL unlock batches
dglowinski Aug 5, 2026
4b7b2e7
test: cover Turtle rEUL batch claims
dglowinski Aug 6, 2026
9d08ea3
fix: preserve executable batch after rejected unlock
dglowinski Aug 6, 2026
e9296ea
fix: keep reward actions outside transaction batches
dglowinski Aug 6, 2026
ab9835e
fix: limit standalone reward flow to rEUL
dglowinski Aug 6, 2026
84f6202
fix: revalidate rEUL unlock reviews
dglowinski Aug 6, 2026
77d1710
test: isolate rEUL burn quote drift
dglowinski Aug 6, 2026
b02521c
fix: preserve rEUL polling and quote progress
dglowinski Aug 6, 2026
14081ce
fix: label rEUL unlock transaction review
dglowinski Aug 6, 2026
3150d5b
chore: bump Euler SDK to 1.2.4
dglowinski Aug 6, 2026
03b7f40
fix: reset rEUL locks after final consumer unmounts
dglowinski Aug 6, 2026
00533d1
fix: surface rEUL unlock preparation failures
dglowinski Aug 6, 2026
10a2031
Merge pull request #780 from euler-xyz/codex/apex-eule2-27
Seranged Aug 7, 2026
a0ce443
Merge pull request #777 from euler-xyz/dependabot/npm_and_yarn/brace-…
Seranged Aug 7, 2026
fad3ecc
chore(deps): bump the all-updates group across 1 directory with 16 up…
dependabot[bot] Aug 7, 2026
284aecd
fix: align @wagmi/connectors override with @wagmi/core 3.6.4
Seranged Aug 7, 2026
3b3be50
chore(deps): bump js-yaml from 4.3.0 to 4.3.1
dependabot[bot] Aug 7, 2026
da5bc95
fix: clarify resolved vault activity entries
Seranged Aug 7, 2026
eaa6886
Merge pull request #786 from euler-xyz/dependabot/npm_and_yarn/all-up…
Seranged Aug 7, 2026
81fefd6
fix: label unresolved activity vaults with token symbols
Seranged Aug 7, 2026
08db1da
chore(deps): bump @nuxt/devtools from 3.2.4 to 3.4.1
dependabot[bot] Aug 7, 2026
517c898
Merge pull request #787 from euler-xyz/dependabot/npm_and_yarn/js-yam…
Seranged Aug 7, 2026
d18ab86
Merge pull request #790 from euler-xyz/dependabot/npm_and_yarn/nuxt/d…
Seranged Aug 7, 2026
83792b2
chore(deps): bump nuxt in the nuxt-ecosystem group across 1 directory
dependabot[bot] Aug 7, 2026
1153f55
Merge pull request #775 from euler-xyz/security-hardening
Seranged Aug 7, 2026
6faa837
fix: drop unhead override so nuxt 4.5.1 can install
Seranged Aug 7, 2026
4d0f4ae
Merge pull request #764 from euler-xyz/dependabot/npm_and_yarn/valibo…
Seranged Aug 7, 2026
5ef6d45
Merge pull request #762 from euler-xyz/dependabot/npm_and_yarn/nuxt-e…
Seranged Aug 7, 2026
ce1017c
fix: gate activity vault resolution on factory membership
Seranged Aug 7, 2026
4441716
Merge remote-tracking branch 'origin/development' into claude/resolve…
Seranged Aug 7, 2026
9ef5887
chore: bump Euler SDK to 1.2.5
Seranged Aug 7, 2026
60cc27b
Merge pull request #774 from euler-xyz/feat/activity-oracle-router-ev…
Seranged Aug 7, 2026
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
10 changes: 10 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,16 @@ NUXT_PUBLIC_CONFIG_ENABLE_INCENTRA="true"
NUXT_PUBLIC_CONFIG_ENABLE_FUUL="true"
NUXT_PUBLIC_CONFIG_ENABLE_TURTLE="true"

# One-time announcement modal (optional). Populate any content field to show it
# after onboarding. Short CONFIG_ANNOUNCEMENT_* names are also accepted at
# runtime for Doppler-injected deployments.
NUXT_PUBLIC_CONFIG_ANNOUNCEMENT_TITLE=
NUXT_PUBLIC_CONFIG_ANNOUNCEMENT_BODY=
# Newline-delimited text or JSON array string, e.g. '["First","Second"]'.
NUXT_PUBLIC_CONFIG_ANNOUNCEMENT_ITEMS=
# Must be https://, http://, or a safe root-relative path such as /portfolio.
NUXT_PUBLIC_CONFIG_ANNOUNCEMENT_URL=

# External token lists for swap token selector (optional)
NUXT_PUBLIC_CONFIG_UNISWAP_TOKEN_LIST_URL="https://tokens.uniswap.org"
NUXT_PUBLIC_CONFIG_DEFILLAMA_TOKEN_LIST_URL="https://d3g10bzo9rdluh.cloudfront.net"
Expand Down
192 changes: 192 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
# AGENTS.md

Euler Lite is a single-service **Nuxt 4 / Vue 3 / TypeScript** frontend for the Euler DeFi
lending protocol (lend, borrow, earn, portfolio, rewards, swaps). There is no separate backend:
the Nitro server layer only serves the SPA shell and a set of `/api/internal/*` proxy routes.
All Ethereum interaction uses **viem** + the **`@eulerxyz/euler-v2-sdk`**; wallet connection uses
**wagmi + Reown AppKit**.

Deep-dive docs live in `docs/` (see "Reference docs" below) and setup/scripts are in `README.md`
and `package.json`. This file captures the non-obvious, durable context that isn't already
obvious from those sources.

## Cursor Cloud specific instructions

Euler Lite is the only service. Standard commands live in `README.md` ("Available Scripts") and
`package.json`.

### Node version
- The app requires **Node 24** (24.14.1 is installed via `nvm` and set as the default). The VM's
system `node` at `/exec-daemon/node` is Node 22, so `~/.bashrc` prepends the Node 24 bin to
`PATH`. New login shells already resolve `node -v` to 24.x — no action needed. If you spawn a
non-login/non-interactive shell and get Node 22, run `nvm use default` or prepend
`$HOME/.nvm/versions/node/v24.14.1/bin` to `PATH`. CI also pins Node 24 (`.github/workflows/ci.yaml`).

### Environment config (`.env`)
- The app reads runtime config from `.env` (gitignored). A working dev `.env` is created during
setup by copying `.env.example` and setting `NUXT_PUBLIC_APP_URL=http://localhost:3000` plus a
public RPC: `RPC_URL_1=https://ethereum-rpc.publicnode.com`. If `.env` is missing, recreate it
the same way — **at least one `RPC_URL_<chainId>` is required** or the chain selector shows no
chains. Subgraph URLs for many chains are already present in `.env.example`.
- `NUXT_PUBLIC_APP_KIT_PROJECT_ID` (Reown/WalletConnect) is left empty; browsing/read flows work
without it, but live wallet connection needs a real project ID.
- Enabled chains are derived from `RPC_URL_<chainId>` vars at server startup, so **restart
`npm run dev` after editing chain env vars** (they are not hot-reloaded).

### Running / testing
- Dev server: `npm run dev` → http://localhost:3000 (use a tmux session so it persists).
- Vault/market data (Explore page, vault details) loads from the upstream V3 API + the configured
RPC; some individual price/vault upstream calls may log `502`/timeout warnings without breaking
the page.
- Lint: `npm run lint` (a few pre-existing `no-explicit-any` warnings, 0 errors).
- Typecheck: `npm run typecheck`.
- Tests: `npm run test:run` (single pass; `npm run test` is watch mode). Test runs print lots of
`pino` warn/error JSON lines from exercised code paths — this is expected; check the final
vitest summary. See "Testing" below for the golden/parity suites and the CI build-before-test
gotcha.
- A `pre-commit` hook (simple-git-hooks + lint-staged) runs `eslint --fix` on staged files.

## Architecture at a glance

- **SPA, not SSR.** `nuxt.config.ts` sets `ssr: false`. Nitro still runs to serve the HTML shell
(with injected config scripts) and the `/api/internal/*` proxies. Details in `docs/architecture.md`.
- **No Pinia.** State lives in module-scoped `ref`s inside composables (e.g. `useVaults`,
`useEulerAccount`, `useTxBatch`, `useWallets`). `app.vue` eagerly instantiates the key
composables at the root so their watchers survive navigation. TanStack Vue Query
(`plugins/01.query.ts`) is used for some async caching, not as global app state.
- **Config flows env → server plugin → `window` global → composable:**
- `RPC_URL_*`, `DEPRECATED_CHAINS`, `ONCHAIN_SDK_CHAINS`, `EVAULT_FETCH_CHUNK_CHAINS`
→ `server/plugins/chain-config.ts` → `window.__CHAIN_CONFIG__` → `useChainConfig()`.
Note: `__CHAIN_CONFIG__` carries only chain IDs + validation metadata (`enabledChainIds`,
`deprecatedChainIds`, `onchainSdkChainIds`, `eVaultFetchChunkChainIds`, `unsupportedChainIds`,
`chainEnvIssues`) — the `RPC_URL_*` values themselves stay server-side.
- AppKit / V3 / Swap / Pyth URLs + branding → `server/plugins/app-config.ts` →
`window.__APP_CONFIG__` → `useEnvConfig()`
- `NUXT_PUBLIC_CONFIG_*` feature flags/page toggles → Nuxt `runtimeConfig.public` → `useDeployConfig()`
- **Configured upstream secrets stay server-side.** `RPC_URL_*`, subgraph URLs (`SUBGRAPH_URL_*`),
and the V3 URL are never shipped to the browser; the app's own reads go through same-origin
proxies (`/api/internal/rpc/{chainId}`, `/api/internal/proxy/subgraph/{chainId}`,
`/api/internal/v3`), and `pythHermesUrl` is exposed to the client only as the string `'proxy'`.
This is **not** a claim that the browser makes zero third-party requests: per-chain viem
transports use the network's public RPC (`network.rpcUrls.default.http`) as a fallback behind the
proxy (`plugins/00.wagmi.ts`), and Reown AppKit + the connected wallet provider make their own
outbound calls (AppKit Blockchain API, WalletConnect relay, etc.).

## Repository layout

| Directory | Contents |
|-----------|----------|
| `pages/` | File-based routes. Key sections: `/explore`, `/lend`, `/borrow`, `/earn`, `/portfolio`, `/position/[number]/*` (supply/withdraw/repay/multiply/migrate), `/batch`. `/ui` is a dev-only component playground stripped from production builds. |
| `components/` | `base/`, `layout/`, `entities/` (feature components — scanned by Tailwind), `ui/` (design-system kit — SCSS, **excluded** from Tailwind scanning), plus root `Batch*.vue`. Auto-imported flat (`pathPrefix: false`), so use `LogoBrand`, not `BaseLogoBrand`. |
| `composables/` | Business logic (~97 files). Subdirs `borrow/`, `repay/`, `cowswap/`, `guards/`, `position/`, `useKeyring/`, `useRpcClient/`. **Auto-import nuance:** only top-level `composables/*/index.ts` are auto-imported; nested files must be imported explicitly (e.g. `~/composables/repay/useRepaySwapCore`). |
| `utils/` | Pure helpers. `utils/vault/` is the largest (classification, APY, LTV, liquidation). |
| `entities/` | App-specific domain types/constants (`account.ts`, `chainRegistry.ts`, `constants.ts`, `menu.ts`, `oracle.ts`, migration/cowswap helpers). On-chain types come from the SDK, not here. |
| `server/` | Nitro layer: `api/`, `middleware/`, `plugins/`, `utils/`. |
| `plugins/` | Client/Nuxt plugins (numbered for load order): `00.wagmi.ts`, `00.chartjs.client.ts`, `01.query.ts`, `theme.client.ts`, `node.ts` (Buffer polyfill). |
| `middleware/` | Route middleware (numbered): `01.network.global.ts` (normalizes `?network=`, applies legacy path rewrites), `02.spy-param.global.ts`, `ensure-vault.global.ts`. |
| `services/` | 3 thin fetch wrappers over internal API routes (`country.ts`, `trm.ts`, `vpn.ts`). |
| `abis/` | viem ABIs (`vault.ts`, `evc.ts`, `erc20.ts`, `pyth.ts`, `keyring.ts`, `merkl.ts`, ...). |
| `types/` | Shared TS types + global `Window` augmentations (`types/index.ts`). |
| `assets/` | `styles/` (SCSS + theme vars in `variables.scss`), `tokens/` (icon overrides), `chains/`, `sprite/`. |
| `tests/` | Vitest suites + `golden/`, `parity/`, `execution/` (see Testing). |
| `scripts/` | Node tooling for parity/execution runs, Doppler sync, preview-SDK install. |
| `docs/` | Developer docs (see below). |

## Server / proxy layer (`server/`)

- **Internal proxies** (`server/api/internal/`) keep configured upstreams/secrets server-side and
are the app's main outbound data path. Representative routes: `v3/[...path]` (allowlisted V3
proxy), `rpc/[chainId]` (JSON-RPC, allowlisted methods), `token-list`, `vaults` (pre-computed
snapshot), `euler-chains` (deployment metadata used as the SDK `deploymentsUrl`),
`proxy/subgraph/[chainId]`, `proxy/merkl|fuul|incentra|turtle/*`, `proxy/aave` + `proxy/morpho`
(external-migration discovery), `proxy/intrinsic-apy-overrides`, `pyth/updates`, `screen-address`,
`tenderly/*`, `oracle-*`, `labels/*`, `tos`. This list is **not** exhaustive and drifts between
branches (e.g. the client-error log endpoint has been removed on `development` to keep browser
diagnostics local) — treat the `server/api/internal/` directory + `docs/architecture.md` as the
authoritative inventory, especially when auditing CSP / `connect-src`.
- **Public routes** (`server/api/public/`): `is-known`, `metadata` (documented in `docs/public-api.md`).
- **Server middleware:** `geo-gate.ts` (451 for sanctioned countries via Cloudflare `CF-IPCountry`;
set `DEV_GEO_COUNTRY` locally since there's no CF header), `cors.ts`, `security-headers.ts`,
`body-limit.ts`, `ensure-vault.ts`.
- **Server plugins (load order matters):** `app-config.ts` / `chain-config.ts` inject the `window`
config; `csp.ts` (nonce-based CSP) must run after them; `warm-cache.ts` warms labels/token-list/
vault caches in the background. Caching internals are in `docs/server-side-caching.md`.
- **Logging:** server uses `pino` JSON (`server/utils/logger.ts`); client/shared uses a console
shim (`utils/logger.ts`). viem errors are sanitized via `utils/viem-errors.ts`.

## Testing

Three distinct test layers — most day-to-day work only touches the first.

1. **Unit / integration (Vitest, `vitest.config.ts`).** 125+ files under `tests/` mirroring source
dirs. Run with `npm run test:run`. Nuxt/Nitro globals and Vue reactivity are stubbed in
`tests/setup.ts`; `#app` is aliased to `tests/stubs/nuxt-app.ts`.
- **Gotcha:** `tests/utils/logger-bundle.test.ts` inspects the production client bundle at
`.output/public/_nuxt` and **auto-skips when no build exists** (this is the "1 skipped" test
in a plain run). CI runs `npm run build` before `npm run test:run` so it executes. Build
first if you need to exercise it locally.
2. **Golden canary (`tests/golden/`).** A version canary (not a correctness suite) that pins the
byte-for-byte calldata a user would sign for one representative operation per SDK encoder family
(plain vault op, borrow, repay, migration, swap, leverage). Each SDK `executionService.plan*`
output is normalized to a canonical `[{to, data, value, evcBatch}]` tx list (`normalize.ts`) and
asserted against a committed fixture under `tests/golden/plans/*.json`; its purpose is to catch an
SDK version bump that silently changes calldata. It uses **committed fixtures — no worktree and no
separate vitest config** — and **runs as part of `npm run test:run`**. Scripts: `npm run test:golden`
(golden only), `npm run test:golden:update` (`UPDATE_GOLDEN=1`; regenerate `plans/*.json` after a
deliberate builder change, then review the diff), and `npm run test:golden:fetch-fixtures`
(re-fetch the swap-quote fixtures in `tests/golden/fixtures/*.json` via the SDK `SwapService`;
override the endpoint with `GOLDEN_SWAP_API_URL`). Outside update mode a missing fixture is a hard
failure, so the suite can't mint its own baseline. Details in `tests/golden/README.md`.
3. **Parity / execution (Playwright, `scripts/*.mjs` + `tests/parity/`, `tests/execution/`).**
Browser-driven diff and fork-based (Anvil) transaction recording. Driven by the `parity:*` and
`execution:*` npm scripts using JSON scenario files. The UI exposes `data-id` / `data-field` /
`data-value` attributes specifically for these scrapers — don't remove them casually.

**CI (`.github/workflows/ci.yaml`)** runs three blocking jobs on every PR: `lint`, `typecheck`,
and `test` (which builds first, then `test:run`). Match these locally before pushing.

## Reference docs (`docs/`)

Start with `docs/README.md` (index) and `docs/architecture.md`. Notable topics:
`development-guide.md`, `sdk-integration.md`, `transaction-building.md`, `server-side-caching.md`,
`pricing-system.md`, `pyth-oracle-handling.md`, `portfolio-logic.md`,
`vault-labels-and-verification.md`, `token-list.md`, `geo-blocking.md`, `tos-signing.md`,
`keyring-hooks.md`, `public-api.md`, `intrinsic-apy.md`.

## Euler protocol safety invariants

When touching transaction-building or position flows, verify against the Euler v2 protocol
invariants — do **not** reinvent them inline. The authoritative sources are `docs/transaction-building.md`
(EVC batching, sub-accounts, Permit2), `docs/pricing-system.md` / `docs/pyth-oracle-handling.md`
(oracle pricing), and the `.claude/skills/review-business/SKILL.md` skill, which links the
upstream `euler-xyz/agent-skills` (`euler-vaults`, `euler-earn`, `euler-advanced`, `euler-irm-oracles`)
plus https://docs.euler.finance. Key invariants those cover (checklist, not a substitute for the docs):

- Vault state-changing calls go through the **EVC** (`abis/evc.ts`), not directly to the vault.
- At most **one controller (borrow) vault enabled per account**; `enableController()` must precede
the borrow, and `enableCollateral()` must be set before collateral is counted.
- Prices resolve through the **EulerRouter / cross-adapter** oracle config — never a raw price feed.
- Vaults/adapters are **factory-deployed**; treat non-factory addresses as untrusted.
- Flash loans must be **repaid within the same batch**; `pullDebt` moves debt *to* the caller.
- **EulerEarn** respects target allocations, cap timelocks, and ascending PublicAllocator withdrawal
order (`docs`/`euler-earn` skill).

## Conventions & gotchas

- **Vue style:** `<script setup lang="ts">` only (no Options API); `defineProps`/`defineEmits` use
the TypeScript generic form. Rely on Nuxt auto-imports for Vue APIs and top-level composables.
- **TypeScript:** avoid `any` (ESLint warns). Type on-chain amounts as `bigint`. Note Nuxt sets
`strict: false` / `noUncheckedIndexedAccess: false` in the generated tsconfig, so the compiler
won't catch nullable-index access for you.
- **viem:** normalize addresses with `getAddress()` before comparing; use `parseUnits`/`formatUnits`
with the asset's real decimals (never hardcode 18).
- **Styling:** Tailwind classes for `pages/`, `layout/`, `base/`, `entities/`; the `components/ui/`
design kit uses SCSS and is intentionally outside Tailwind's `content` scan (`tailwind.config.js`).
Colors/spacing derive from CSS vars in `assets/styles/variables.scss`.
- **Theme:** default dark; an inline critical script reads `localStorage.theme` before paint.
Chart.js colors are bridged from CSS vars via `useThemeColors`.
- **Local SDK dev:** `nuxt.config.ts` detects a symlinked `@eulerxyz/euler-v2-sdk` and excludes it
from Vite `optimizeDeps`. Keep `package.json`/`package-lock.json` pinned to the published SDK
version unless a PR is intentionally bumping it (see README "Development with the SDK").
- **PR review skills:** `.claude/skills/{review-business,review-security,review-stack}/SKILL.md`
document the review criteria used for this repo — useful before submitting changes.
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,9 +120,15 @@ These use Nuxt's `runtimeConfig` and are set via `NUXT_PUBLIC_CONFIG_*` env vars
| `NUXT_PUBLIC_CONFIG_ENABLE_MERKL` | `true` | Enable Merkl rewards integration |
| `NUXT_PUBLIC_CONFIG_ENABLE_INCENTRA` | `true` | Enable Incentra rewards integration |
| `NUXT_PUBLIC_CONFIG_ENABLE_FUUL` | `true` | Enable Fuul rewards integration |
| `NUXT_PUBLIC_CONFIG_ANNOUNCEMENT_TITLE` | — | Optional one-time modal title; any announcement content field enables the modal |
| `NUXT_PUBLIC_CONFIG_ANNOUNCEMENT_BODY` | — | Optional one-time modal body text |
| `NUXT_PUBLIC_CONFIG_ANNOUNCEMENT_ITEMS` | — | Optional newline-delimited or JSON-array bullet items |
| `NUXT_PUBLIC_CONFIG_ANNOUNCEMENT_URL` | — | Optional announcement link (`https://`, `http://`, or safe root-relative path) |
| `NUXT_PUBLIC_CONFIG_UNISWAP_TOKEN_LIST_URL` | `https://tokens.uniswap.org` | Uniswap token list for swap selector |
| `NUXT_PUBLIC_CONFIG_DEFILLAMA_TOKEN_LIST_URL` | `https://d3g10bzo9rdluh.cloudfront.net` | DefiLlama token list for swap selector |

Runtime deployments can also set the short `CONFIG_ANNOUNCEMENT_*` names. They are injected through `window.__APP_CONFIG__` at server startup and can change without rebuilding static assets. See [Announcement Modal](./docs/announcement-modal.md) for rollout and dismissal behavior.

#### Chain Configuration

Chains are configured dynamically at runtime. Each chain requires two env vars:
Expand Down
Loading
Loading