diff --git a/.env.example b/.env.example index 83954d643..7d8c285e5 100644 --- a/.env.example +++ b/.env.example @@ -52,9 +52,9 @@ NUXT_PUBLIC_SWAP_API_URL="https://swap-dev.euler.finance" # Pyth Hermes authentication (server-side only, required for Pyth-backed markets) PYTH_API_KEY= -# Stablewatch intrinsic APY (server-side only, optional) -# If not set, Stablewatch APY data will not be fetched even if tokens are configured. -STABLEWATCH_API_KEY= +# Merkl authentication (server-side only, optional) +# Sends X-API-Key through /api/internal/proxy/merkl for an increased upstream quota. +MERKL_API_KEY= # Tenderly simulation (server-side only, optional) TENDERLY_ACCESS_KEY= @@ -129,6 +129,8 @@ NUXT_PUBLIC_CONFIG_TELEGRAM_URL= NUXT_PUBLIC_CONFIG_GITHUB_URL="https://github.com/euler-xyz" NUXT_PUBLIC_CONFIG_APP_TITLE="Euler Lite" NUXT_PUBLIC_CONFIG_APP_DESCRIPTION="Lightweight interface for Euler Finance." +# Custom logo URL (optional). LOGO_URL takes precedence at server startup. +NUXT_PUBLIC_CONFIG_LOGO_URL= # Absolute URL to a social share image (og:image / twitter:image). # Recommended: 1200x630 or larger (2400x1260), PNG or JPG. Must be publicly # reachable — X, Slack, Discord fetch it directly. Empty = no preview image. @@ -153,6 +155,7 @@ NUXT_PUBLIC_CONFIG_ENABLE_EARN_PAGE="true" NUXT_PUBLIC_CONFIG_ENABLE_LEND_PAGE="true" NUXT_PUBLIC_CONFIG_ENABLE_EXPLORE_PAGE="true" NUXT_PUBLIC_CONFIG_ENABLE_APP_TITLE="true" +NUXT_PUBLIC_CONFIG_ENABLE_POWERED_BY_EULER="true" # Incentives provider flags — all enabled by default NUXT_PUBLIC_CONFIG_ENABLE_MERKL="true" @@ -161,8 +164,9 @@ 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. +# after onboarding. At server startup, short CONFIG_ANNOUNCEMENT_* names take +# precedence over the corresponding NUXT_PUBLIC_CONFIG_ANNOUNCEMENT_* names. +# Clear both naming forms for all four fields to disable the announcement. NUXT_PUBLIC_CONFIG_ANNOUNCEMENT_TITLE= NUXT_PUBLIC_CONFIG_ANNOUNCEMENT_BODY= # Newline-delimited text or JSON array string, e.g. '["First","Second"]'. diff --git a/Dockerfile b/Dockerfile index 87442bc0d..012fa621b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -43,7 +43,7 @@ ENV NODE_OPTIONS=--max-old-space-size=4096 RUN npm run build # ── Production stage (distroless: no shell, no tools, non-root) ── -FROM gcr.io/distroless/nodejs24-debian12:nonroot AS production +FROM gcr.io/distroless/nodejs24-debian13:nonroot AS production ENV MODE=production ENV NODE_ENV=production diff --git a/README.md b/README.md index 4971871c5..26756b298 100644 --- a/README.md +++ b/README.md @@ -66,7 +66,7 @@ Euler Lite uses the [Euler V2 SDK](https://github.com/euler-xyz/euler-sdks) for | `DEPRECATED_CHAINS` | — | Comma-separated chain IDs shown collapsed in the chain selector and skipped by startup warm-cache cycles. | | `ONCHAIN_SDK_CHAINS` | — | Comma-separated chain IDs pinned to the onchain SDK adapter config for chain-aware browser reads and the server vault snapshot, bypassing V3. Independent of `DEPRECATED_CHAINS`; list a chain in both to deprecate it and route it onchain. | | `EVAULT_FETCH_CHUNK_CHAINS` | — | Comma-separated chain IDs whose EVault list reads are split into small sequential SDK calls. Use for RPC/lens endpoints that fail under larger concurrent onchain EVault fetches. | -| `EULER_SDK_EULER_INTERFACES_BRANCH` | `master` | Euler interfaces branch used for runtime ABIs and `EulerChains.json`. When set, it takes precedence over `NUXT_PUBLIC_CONFIG_EULER_CHAINS_URL`. | +| `EULER_SDK_EULER_INTERFACES_BRANCH` | `master` | Euler interfaces branch used for runtime ABIs and `EulerChains.json`. Explicit `NUXT_PUBLIC_CONFIG_EULER_CHAINS_URL` and `NUXT_PUBLIC_CONFIG_EULER_ABIS_BASE_URL` overrides take precedence over the branch for their respective manifests. | `fallback` uses V3 first and on-chain reads second. If no V3 URL is configured, Lite passes `disableV3: true` to the SDK so fallback reads go straight on-chain. @@ -75,18 +75,19 @@ Euler Lite uses the [Euler V2 SDK](https://github.com/euler-xyz/euler-sdks) for | Variable | Description | | -------- | ----------- | | `CORS_ALLOWED_ORIGINS` | Comma-separated allowlist for `/api/*`; falls back to `NUXT_PUBLIC_APP_URL`. | +| `FIRST_PARTY_COOKIE_SECRET` | Optional server-only secret that keeps the internal API marker cookie stable across replicas and deploys. Defaults to a value derived from `NUXT_PUBLIC_APP_URL` or `RAILWAY_PUBLIC_DOMAIN`. | | `CSP_EXTRA_CONNECT_SRC` | Extra `connect-src` origins for development or staging endpoints. | | `DEV_GEO_COUNTRY` | Local/preview country fallback when Cloudflare geo headers are absent. Do not set in production behind Cloudflare. | | `ADDRESS_SCREENING_URI` / `ADDRESS_SCREENING_API_KEY` | Server-side data-v3 compliance endpoint + restricted API key, proxied by `/api/internal/screen-address` (also serves first-party `*.euler.finance` SPAs). Both unset ⇒ screening disabled (all addresses pass) — except in production (`DOPPLER_ENVIRONMENT=prd`), where missing configuration fails closed; only one set ⇒ fails closed everywhere. URI must be https (localhost http allowed for dev). | -| `STABLEWATCH_API_KEY` | Optional server-side Stablewatch key for intrinsic APY data. | | `MERKL_API_KEY` | Optional server-side Merkl key. The Merkl API works anonymously (10 req/sec shared across all users via `/api/internal/proxy/merkl`); set this to send `X-API-Key` upstream for a higher quota. Server-only — never exposed to the browser. | | `TENDERLY_ACCESS_KEY`, `TENDERLY_ACCOUNT_SLUG`, `TENDERLY_PROJECT_SLUG` | Optional Tenderly simulation configuration. | | `FUUL_API_URL` or `NUXT_PUBLIC_FUUL_API_URL` | Optional Fuul API upstream override. | | `INCENTRA_API_URL` or `NUXT_PUBLIC_INCENTRA_API_URL` | Optional Incentra/Brevis API upstream override. | +| `TURTLE_EARN_API_URL` or `NUXT_PUBLIC_TURTLE_EARN_API_URL` | Optional Turtle reward-proof upstream override; defaults to `https://earn.turtle.xyz/v1`. | #### Branding & Feature Flags -These use Nuxt's `runtimeConfig` and are set via `NUXT_PUBLIC_CONFIG_*` env vars: +These settings use `NUXT_PUBLIC_CONFIG_*` env vars. Branding and announcement values are read by the server at startup and injected into `window.__APP_CONFIG__`; links and feature flags use Nuxt's public `runtimeConfig`. The server proxy handlers resolve the upstream source overrides. | Variable | Default | Description | | ------------------------------------------- | ------------------------------------------ | ----------------------------------------------------- | @@ -97,7 +98,8 @@ These use Nuxt's `runtimeConfig` and are set via `NUXT_PUBLIC_CONFIG_*` env vars | `NUXT_PUBLIC_CONFIG_LABELS_REPO` | `euler-xyz/euler-labels` | GitHub labels repo | | `NUXT_PUBLIC_CONFIG_LABELS_REPO_BRANCH` | `master` | Branch to fetch labels from | | `NUXT_PUBLIC_CONFIG_LABELS_BASE_URL` | — | S3/CDN base URL for labels (overrides repo/branch) | -| `NUXT_PUBLIC_CONFIG_EULER_CHAINS_URL` | — | URL for EulerChains.json, used when no Euler interfaces branch is configured | +| `NUXT_PUBLIC_CONFIG_EULER_CHAINS_URL` | — | Explicit URL for EulerChains.json; takes precedence over the Euler interfaces branch | +| `NUXT_PUBLIC_CONFIG_EULER_ABIS_BASE_URL` | — | Base URL serving `{contract}.json` for runtime ABIs; takes precedence over the Euler interfaces branch. | | `NUXT_PUBLIC_CONFIG_DOCS_URL` | — | Documentation link | | `NUXT_PUBLIC_CONFIG_STARGATE_URL` | — | Stargate link | | `NUXT_PUBLIC_CONFIG_TOS_URL` | — | Terms of Service link | @@ -115,9 +117,11 @@ These use Nuxt's `runtimeConfig` and are set via `NUXT_PUBLIC_CONFIG_*` env vars | `NUXT_PUBLIC_CONFIG_ENABLE_ENTITY_BRANDING` | `true` | Show entity branding | | `NUXT_PUBLIC_CONFIG_ENABLE_VAULT_TYPE` | `true` | Show vault type labels | | `NUXT_PUBLIC_CONFIG_ENABLE_APP_TITLE` | `true` | Show app title in the navbar | +| `NUXT_PUBLIC_CONFIG_ENABLE_POWERED_BY_EULER` | `true` | Show the Powered by Euler branding in the navbar | | `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_ENABLE_TURTLE` | `true` | Enable Turtle 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 | @@ -125,7 +129,7 @@ These use Nuxt's `runtimeConfig` and are set via `NUXT_PUBLIC_CONFIG_*` env vars | `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. +At server startup, short `CONFIG_ANNOUNCEMENT_*` names take precedence over the corresponding `NUXT_PUBLIC_CONFIG_ANNOUNCEMENT_*` names. In Nitro-served deployments, both naming forms are read at server startup, so updates require a server restart without rebuilding client assets. Generated/static deployments must rebuild when `NUXT_PUBLIC_CONFIG_ANNOUNCEMENT_*` fallback values change. To disable an announcement, clear both naming forms for all four content fields. See [Announcement Modal](./docs/announcement-modal.md) for rollout and dismissal behavior. #### Chain Configuration diff --git a/app.vue b/app.vue index 691790cd7..91891a3de 100644 --- a/app.vue +++ b/app.vue @@ -14,7 +14,7 @@ let isAnnouncementOpen = false const { loadEulerConfig, chainId } = useEulerAddresses() const { loadVaults, isReady: isVaultsReady, resetVaultsState, refreshVaults, setShowAllLabelEntries } = useVaults() const { loadTokenList, isLoaded: isTokenListLoaded } = useTokenList() -const { loadLabels } = useEulerLabels() +const { loadLabels, retryLabels, isReady: isLabelsReady, isLoading: isLabelsLoading, loadError: labelsLoadError } = useEulerLabels() const { loadCountry } = useGeoBlock() const { updateBalances, resetBalances } = useWallets() const { isConnected, address } = useWagmi() @@ -217,6 +217,15 @@ onUnmounted(() => { :class="isMenuVisible ? 'pb-[98px]' : 'pb-16'" >
+ { if (!entry.review) return modal.open(OperationReviewModal, { - // Forward the entry's sub-account so the review shows the same "Position N" - // pill as the batch operations list (entry.review itself doesn't carry it). - props: { ...entry.review, subAccount: entry.subAccount, plan: entryPlans.value[entry.id], hideExecute: true }, + // Forward account context so position-funded operations identify both the + // target and the distinct account supplying assets or shares. + props: { ...entry.review, subAccount: entry.subAccount, sourceSubAccount: entry.sourceSubAccount, plan: entryPlans.value[entry.id], hideExecute: true }, }) } diff --git a/components/BatchReviewModal.vue b/components/BatchReviewModal.vue index bd6872df9..81df5951d 100644 --- a/components/BatchReviewModal.vue +++ b/components/BatchReviewModal.vue @@ -1,7 +1,7 @@