Skip to content

feat: add vault overview accordions#617

Merged
Seranged merged 55 commits into
developmentfrom
feature/lite-215-convert-vault-page-sections-to-accordion-style
Jul 1, 2026
Merged

feat: add vault overview accordions#617
Seranged merged 55 commits into
developmentfrom
feature/lite-215-convert-vault-page-sections-to-accordion-style

Conversation

@Seranged

@Seranged Seranged commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Convert vault overview cards into collapsible sections so long vault pages are easier to scan.
  • Apply the same accordion treatment across standard vault, borrow pair, and Securitize overview variants.

Changes

  • Add a reusable vault overview accordion wrapper with configurable default-open state and native button accessibility.
  • Keep high-signal overview and statistics sections open by default while collapsing deeper technical sections by default.
  • Preserve existing inline tooltips and modal triggers by avoiding clipping around accordion content.

Test plan

  • Run Nuxt typecheck.
  • Run focused ESLint over changed overview files.
  • Run git diff --check.
  • Smoke a vault deep link on the local dev server and confirm it returns 200 OK.

Summary by CodeRabbit

  • New Features
    • Vault overview pages now use accessible accordion sections for Overview, Statistics, Risk parameters, Addresses, Oracles, and interest-rate-model details.
    • Added live Exposure summaries across vault pages, vault discovery stats matrices, and earnings views, including a new exposure list modal.
    • Added open-interest/borrowed exposure accordion content with chart + breakdown on vault pages.
  • Accessibility & UX
    • Accordion supports configurable default expanded/collapsed states; improved action placement and spacing/hover styling.
    • Removed the “can be used as collateral” availability indicator from the overview.
  • Chores / Tests
    • Extended API proxy allowlist for open-interest endpoints and added tests for proxy + open-interest utilities/hook behavior.

Wrap vault overview sections in reusable accordion cards with configurable default open states across standard, pair, and Securitize views.
@railway-app railway-app Bot temporarily deployed to euler-lite / euler-lite-pr-617 June 24, 2026 11:46 Destroyed
@coderabbitai

coderabbitai Bot commented Jun 24, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Vault overview sections now render through a reusable accordion with explicit open-state control. The PR also adds shared open-interest and exposure utilities, then uses them to render exposure summaries and drilldowns in vault, earn, and discovery views, with matching proxy and test coverage.

Changes

Vault Overview Accordion Refactor

Layer / File(s) Summary
Accordion component
components/entities/vault/overview/VaultOverviewAccordionSection.vue
Adds the reusable accordion section with typed props, open/close state, ARIA wiring, actions slot, and auto-open behavior.
Overview and pair blocks
components/entities/vault/overview/VaultOverviewBlockGeneral.vue, VaultOverviewBlockStats.vue, VaultOverviewBlockRiskParameters.vue, VaultOverviewBlockAddresses.vue, VaultOverviewBlockBorrow.vue, VaultOverviewBlockIRM.vue, VaultOverviewBlockCyclicalIRM.vue, VaultOverviewBlockOracleAdapters.vue, VaultOverviewPairBlockGeneral.vue, VaultOverviewPairBlockTypes.vue, SecuritizeVaultOverviewPairBlockGeneral.vue, VaultOverviewPair.vue, SecuritizeVaultOverviewPair.vue, VaultOverview.vue, SecuritizeVaultOverview.vue
Adds defaultOpen props, replaces static wrappers with VaultOverviewAccordionSection, and passes explicit default-open values from parent overview containers.

Exposure and open-interest plumbing

Layer / File(s) Summary
Open-interest and exposure core
utils/vault/open-interest.ts, utils/vault/collateral-exposure.ts, utils/vault/exposure-groups.ts, utils/vault/exposure-display.ts, composables/useCollateralOpenInterest.ts, server/utils/v3-proxy.ts, tests/composables/useCollateralOpenInterest.test.ts, tests/server/v3-proxy.test.ts, tests/utils/open-interest.test.ts, tests/utils/vault/collateral-exposure.test.ts, tests/utils/vault/exposure-display.test.ts
Adds shared helpers and composable state for open-interest loading, exposure grouping, exposure display item creation, stale-request handling, proxy allowlists, and utility coverage.

Exposure UI updates

Layer / File(s) Summary
Vault, discovery, and earn exposure views
components/entities/vault/VaultCollateralExposureModal.vue, VaultExposureListModal.vue, VaultExposureSummary.vue, VaultItem.vue, VaultEarnItem.vue, components/entities/vault/overview/VaultOverviewBlockBorrow.vue, VaultOverviewBlockGeneral.vue, VaultOverviewBlockOpenInterest.vue, VaultOverviewPairBlockOpenInterest.vue, VaultOverviewBlockIRM.vue, VaultOverviewBlockCyclicalIRM.vue, VaultOverviewBlockStats.vue, components/entities/vault/overview/earn/VaultOverviewEarnBlockExposure.vue, VaultOverviewEarnBlockStats.vue, components/entities/vault/discovery/DiscoveryMarketAttributeMatrix.vue, components/entities/vault/VaultLabelsAndAssets.vue
Renders exposure summaries and modal drilldowns in vault list, overview, earn, and discovery views, and updates related labels, layouts, loading states, and table cells.

Discovery stats and labels

Layer / File(s) Summary
Discovery stats row
utils/discoveryCalculations.ts, components/entities/vault/discovery/DiscoveryMarketAccordion.vue
Adds the exposure stats row and updates the accompanying discovery comment.
Header label truncation
components/entities/vault/VaultLabelsAndAssets.vue
Adjusts the vault label title and truncation behavior.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

Suggested reviewers: kanvgupta

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: adding accordion sections to vault overview pages.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/lite-215-convert-vault-page-sections-to-accordion-style

Comment @coderabbitai help to get the list of available commands.

@railway-app

railway-app Bot commented Jun 24, 2026

Copy link
Copy Markdown

🚅 Deployed to the euler-lite-pr-617 environment in euler-lite

Service Status Web Updated (UTC)
dev-build ✅ Success (View Logs) Web Jul 1, 2026 at 10:58 am

…15-convert-vault-page-sections-to-accordion-style

# Conflicts:
#	components/entities/vault/overview/SecuritizeVaultOverview.vue
#	components/entities/vault/overview/VaultOverview.vue
#	components/entities/vault/overview/VaultOverviewBlockGeneral.vue
@railway-app railway-app Bot temporarily deployed to euler-lite / euler-lite-pr-617 June 24, 2026 12:03 Destroyed
@Seranged Seranged marked this pull request as ready for review June 24, 2026 12:05

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
components/entities/vault/overview/VaultOverviewBlockIRM.vue (1)

615-622: 🚀 Performance & Scalability | 🔵 Trivial | 🏗️ Heavy lift

Lazy-load the IRM chart when this section starts collapsed.

This accordion only hides the panel DOM; it does not stop the existing onMounted/watch-driven VaultLens and UtilsLens reads from running immediately. That turns a default-collapsed technical section into eager off-screen RPC + chart work. Consider wiring the fetch/render path to the expanded state, or hoisting the accordion above this component so the IRM body mounts only after expansion.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@components/entities/vault/overview/VaultOverviewBlockIRM.vue` around lines
615 - 622, The Interest rate model section currently mounts its
VaultLens/UtilsLens data and chart work even when the accordion starts
collapsed, so make the IRM body lazy by tying the fetch/render path to the
expanded state in VaultOverviewBlockIRM and the accordion section around
hasValidIRM/defaultOpen. Update the onMounted/watch-driven reads so they only
run after expansion, or move the conditional mounting above this component so
the IRM content does not mount until the section is opened.
components/entities/vault/overview/VaultOverviewBlockOracleAdapters.vue (1)

305-309: 🚀 Performance & Scalability | 🔵 Trivial | 🏗️ Heavy lift

Keep oracle adapter loading behind expansion for collapsed views.

After this refactor the section can start closed, but loadOracleAdapter(...) and useOracleAdapterPrices(...) still fan out as soon as the component mounts. In the pair overview this block is explicitly default-closed, so users still pay the metadata/price-loading cost before opening it. Please gate that work on the accordion’s open state, or move the accordion above this component so the expensive body mounts lazily.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@components/entities/vault/overview/VaultOverviewBlockOracleAdapters.vue`
around lines 305 - 309, The Oracle adapters section still triggers eager
metadata/price loading on mount even when the accordion is collapsed. Gate the
expensive work in VaultOverviewBlockOracleAdapters.vue behind the accordion’s
open state, or restructure so the body that calls loadOracleAdapter(...) and
useOracleAdapterPrices(...) only mounts when the VaultOverviewAccordionSection
is expanded. Keep the change centered on the accordion/open-state logic and the
Oracle adapters loading flow.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@components/entities/vault/overview/VaultOverviewBlockIRM.vue`:
- Around line 615-622: The Interest rate model section currently mounts its
VaultLens/UtilsLens data and chart work even when the accordion starts
collapsed, so make the IRM body lazy by tying the fetch/render path to the
expanded state in VaultOverviewBlockIRM and the accordion section around
hasValidIRM/defaultOpen. Update the onMounted/watch-driven reads so they only
run after expansion, or move the conditional mounting above this component so
the IRM content does not mount until the section is opened.

In `@components/entities/vault/overview/VaultOverviewBlockOracleAdapters.vue`:
- Around line 305-309: The Oracle adapters section still triggers eager
metadata/price loading on mount even when the accordion is collapsed. Gate the
expensive work in VaultOverviewBlockOracleAdapters.vue behind the accordion’s
open state, or restructure so the body that calls loadOracleAdapter(...) and
useOracleAdapterPrices(...) only mounts when the VaultOverviewAccordionSection
is expanded. Keep the change centered on the accordion/open-state logic and the
Oracle adapters loading flow.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: euler-xyz/coderabbit/.coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: d411ddb6-2ec1-428a-8f06-c8cbb521b894

📥 Commits

Reviewing files that changed from the base of the PR and between c61a9af and dccb4d0.

📒 Files selected for processing (16)
  • components/entities/vault/overview/SecuritizeVaultOverview.vue
  • components/entities/vault/overview/SecuritizeVaultOverviewPair.vue
  • components/entities/vault/overview/SecuritizeVaultOverviewPairBlockGeneral.vue
  • components/entities/vault/overview/VaultOverview.vue
  • components/entities/vault/overview/VaultOverviewAccordionSection.vue
  • components/entities/vault/overview/VaultOverviewBlockAddresses.vue
  • components/entities/vault/overview/VaultOverviewBlockBorrow.vue
  • components/entities/vault/overview/VaultOverviewBlockCyclicalIRM.vue
  • components/entities/vault/overview/VaultOverviewBlockGeneral.vue
  • components/entities/vault/overview/VaultOverviewBlockIRM.vue
  • components/entities/vault/overview/VaultOverviewBlockOracleAdapters.vue
  • components/entities/vault/overview/VaultOverviewBlockRiskParameters.vue
  • components/entities/vault/overview/VaultOverviewBlockStats.vue
  • components/entities/vault/overview/VaultOverviewPair.vue
  • components/entities/vault/overview/VaultOverviewPairBlockGeneral.vue
  • components/entities/vault/overview/VaultOverviewPairBlockTypes.vue

@LeonardEulerXYZ LeonardEulerXYZ left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Leonard review — PR #617

Verdict: COMMENT — no blocking issues found on the reviewed head dccb4d0c31afc7b9eb51b307f93e2dfeb56b5c12.

Walkthrough

This PR converts the vault overview surfaces into shared accordion sections:

  • Standard lend vault overview: Overview/Statistics remain open; deeper technical sections default closed.
  • Borrow pair overview: pair Overview remains open; Vault types and Oracles default closed.
  • Securitize vault and pair variants receive the same accordion treatment.
  • VaultOverviewAccordionSection centralizes the section chrome, native button toggling, aria-expanded, and optional action slot handling.

Risk assessment

The changed surface is user-visible but mostly layout/state presentation rather than transaction logic. The main risks I checked were:

  • hidden/closed sections accidentally dropping important risk data;
  • mobile overview drawer behavior diverging from desktop;
  • section actions/footnotes being swallowed by the accordion button;
  • accessibility regressions around the accordion trigger state;
  • one-off accordion wrappers creating inconsistency across sibling overview variants.

No blocking regression found.

Validation performed

  • npm run test:run — 95 files / 958 tests passed.
  • npm run typecheck — passed.
  • npm run build — passed; only existing-style Vite/Rollup chunk/dynamic-import warnings observed.
  • npx eslint <changed vault overview files> — passed.
  • Headed/Xvfb Playwright browser visual smoke against the PR preview:
    • desktop lend vault deep link: KPK VBILL/USDC lend vault, accordion headers/content present.
    • desktop borrow pair deep link: KPK VBILL/USDC borrow/multiply pair, pair overview and oracle accordion present.
    • mobile lend vault deep link: opened the Vault information drawer and verified the accordion content is reachable there.
    • desktop accordion interaction check: default-open/default-closed aria-expanded states matched intent; expanding Risk parameters changed aria-expanded from false to true.

Smoke coverage: browser visual smoke + mobile smoke. Wallet/signing smoke: not run; this PR does not change transaction preparation or wallet signing paths.

Visual evidence

Screenshots are from the public PR preview and contain only public app UI:

Desktop lend vault accordion smoke

Desktop borrow pair accordion smoke

Mobile lend vault information drawer smoke

Scalability / maintainability hygiene pass

I searched the sibling overview surfaces that consume the same vault overview data and UI concept:

  • covered by this PR: standard vault overview, standard borrow pair overview, Securitize vault overview, Securitize pair overview, addresses/stats/risk/IRM/oracle/type blocks;
  • genuinely out of scope: Earn overview blocks under components/entities/vault/overview/earn, which are a separate Earn-specific overview surface and not mentioned by the PR intent;
  • reusable abstraction: the shared VaultOverviewAccordionSection is the right direction and avoids per-block button/chrome duplication;
  • focused tests: there is no dedicated component-level accordion test, but the wrapper is small, uses native button semantics, and the repo’s current test suite is not component-render focused. I do not think this is a blocker for this layout refactor.

One non-blocking follow-up worth keeping in mind: CodeRabbit’s performance note is valid. VaultOverviewBlockIRM and VaultOverviewBlockOracleAdapters can still do chart/oracle loading while their accordion sections start collapsed, because the accordion hides the panel body inside an already-mounted component. That does not break correctness, and I would not block this PR on it, but if the product goal includes reducing first-load work rather than only reducing visual density, the next clean step is to hoist lazy mounting above those expensive bodies or expose accordion open state to gate those fetches.

Bot/reviewer feedback pass

I inspected the active CodeRabbit review. Its two notes about eager IRM/oracle loading behind collapsed accordions are materially valid but non-blocking performance/architecture follow-ups; I did not duplicate them as inline Leonard findings.

Comment lifecycle

No prior Leonard-authored top-level comments, reviews, or inline review comments were present for this PR head, so there was nothing to edit or clean up.

@Seranged Seranged marked this pull request as draft June 24, 2026 13:29
Collapse a vault overview accordion by default when it is the only accordion section in its parent view.
@railway-app railway-app Bot temporarily deployed to euler-lite / euler-lite-pr-617 June 24, 2026 13:48 Destroyed
Force a lone vault overview accordion section open after mount so single-section views show their content by default.
@railway-app railway-app Bot temporarily deployed to euler-lite / euler-lite-pr-617 June 24, 2026 13:55 Destroyed
Keep accordion actions before a far-right chevron so sections with badges retain the same expand affordance as other rows.
@railway-app railway-app Bot temporarily deployed to euler-lite / euler-lite-pr-617 June 24, 2026 14:01 Destroyed
Make the full vault overview accordion header toggle the section while keeping header actions independently clickable.
@railway-app railway-app Bot temporarily deployed to euler-lite / euler-lite-pr-617 June 24, 2026 14:03 Destroyed
@Seranged Seranged marked this pull request as ready for review June 24, 2026 14:04
Seranged added 11 commits June 25, 2026 09:45
Polish the vault open interest graph with card-based nodes, responsive mobile stacking, and data-aware spacing for dense collateral sets.
Remove the standalone vault-page open interest chart and add a compact Explore matrix Open Interest view grouped by backing asset, including duplicate-vault counts for aggregated exposures.
Limit the matrix Open Interest view to the first four borrow-vault cards by default and add a Show more/Show less control for dense markets.
Keep every borrow-vault card visible in the matrix Open Interest view, but collapse each card's backing-asset rows after the top three with a per-card Show more control.
Center the per-card Show more control in the matrix Open Interest view.
Show token avatars next to borrow and backing asset symbols in the matrix Open Interest view.
Drops the redundant 'Can be used as collateral' field from vault overview surfaces now that collateral exposure is covered by the matrix/open-interest work.
Adds shared backing-asset grouping for exposure data and applies it across lend rows, lend exposure details, earn rows, and earn vault exposure sections.
Hides backing-asset group headers when an exposure section only has one backing asset, leaving the underlying vault rows as the primary content.
Seranged added 5 commits June 30, 2026 18:48
Moves markets listed links into their own row in the exposure popup so Earn vault users can scan and click listed markets more easily.
Omits exposure market source labels when no market route exists, avoiding duplicate non-clickable escrow asset entries in Earn exposure popovers.
Adds punctuation and reduces the label gap so exposure market links do not appear offset in the popover.
Renders exposure market links as inline units so the first link and commas do not appear separated by extra spacing.
@railway-app railway-app Bot temporarily deployed to euler-lite / euler-lite-pr-617 July 1, 2026 09:58 Destroyed
Seranged added 2 commits July 1, 2026 11:09
…rest-section-to-vault-page-sankey-style-flow

feat: add grouped exposure views
@railway-app railway-app Bot temporarily deployed to euler-lite / euler-lite-pr-617 July 1, 2026 10:11 Destroyed
@railway-app railway-app Bot temporarily deployed to euler-lite / euler-lite-pr-617 July 1, 2026 10:17 Destroyed

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
components/entities/vault/overview/SecuritizeVaultOverview.vue (1)

76-91: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

shareTokenExchangeRate won't update if vault changes without a remount.

Every other derived value in this file (priceDisplay, totalSupplyDisplay, supplyCapDisplay) is wrapped in watchEffect to react to vault prop changes. loadRiskParameters() is called once at setup time instead, so if this component instance persists across a vault-to-vault navigation (e.g. same route component, only params changing), the displayed exchange rate will be stale.

🐛 Proposed fix
-loadRiskParameters()
+watchEffect(() => {
+  loadRiskParameters()
+})
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@components/entities/vault/overview/SecuritizeVaultOverview.vue` around lines
76 - 91, `shareTokenExchangeRate` is only computed once in
`loadRiskParameters()`, so it can become stale when the `vault` prop changes
without remounting. Move this derivation into a reactive `watchEffect` like the
other display values in `SecuritizeVaultOverview.vue`, and keep using
`vault.convertToAssets(...)` with the existing `logWarn` handling so the
exchange rate recalculates whenever `vault` updates.
🧹 Nitpick comments (4)
components/entities/vault/overview/earn/VaultOverviewEarnBlockStats.vue (1)

48-61: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicated logic vs. VaultOverviewEarnBlockExposure.vue; recomputation not memoized.

getStrategyMarketSource (lines 48-61) is duplicated verbatim in VaultOverviewEarnBlockExposure.vue (lines 167-180). Also, getStrategyCollateralGroups (73-80) is called directly inside both hasUnavailableExposureSplit and exposureDisplayItems without caching per strategy — unlike the sibling file, which memoizes results in a collateralExposureGroupsByStrategy computed.

Consider extracting getStrategyMarketSource into a shared util (e.g. utils/vault/strategy-market-source.ts) and memoizing collateral groups per strategy similarly to the Exposure block file.

Also applies to: 73-80, 102-122

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@components/entities/vault/overview/earn/VaultOverviewEarnBlockStats.vue`
around lines 48 - 61, `getStrategyMarketSource` in
`VaultOverviewEarnBlockStats.vue` is duplicated in
`VaultOverviewEarnBlockExposure.vue`, so extract that shared market-link builder
into a common utility (for example a strategy market source helper) and reuse it
from both components. Also memoize `getStrategyCollateralGroups` results per
strategy, following the `collateralExposureGroupsByStrategy` pattern from the
sibling component, and update `hasUnavailableExposureSplit` and
`exposureDisplayItems` to read from the cached groups instead of recomputing
repeatedly.
components/entities/vault/overview/earn/VaultOverviewEarnBlockExposure.vue (1)

155-219: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Mixed address-normalization conventions.

collateralExposureGroupsByStrategy keys by getAddress(...) (checksummed), while exposureUsdPrices/unavailableExposureUsdPrices key by .toLowerCase(). Each map is internally consistent, but the mixed convention within the same file is easy to regress when adding new lookups.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@components/entities/vault/overview/earn/VaultOverviewEarnBlockExposure.vue`
around lines 155 - 219, The exposure state and lookup logic in
VaultOverviewEarnBlockExposure.vue mixes checksummed and lowercased address keys
across related helpers. Standardize the keying convention used by
getStrategyExposureValueState, collateralExposureGroupsByStrategy, and
getStrategyExposureDisplayItems so the same address normalization is applied
consistently for all maps and lookups. Use the existing symbols getAddress,
strategyVault.address, exposureUsdPrices, and unavailableExposureUsdPrices to
align the convention and avoid future lookup regressions.
utils/vault/open-interest.ts (1)

71-79: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Linear scan + re-normalization on every lookup — consider a memoized lookup map.

findOpenInterestMapForVault re-normalizes every key of data on each call. It's invoked once per vault row (e.g. from VaultItem.vue's collateralExposureGroups and VaultOverviewBlockOpenInterest.vue), so rendering a list of N borrowable vaults against an M-entry OI map costs O(N·M) checksum computations instead of O(N+M).

Consider building a normalized-address → sub-map cache once per data change (e.g., memoize keyed off object identity of data) and doing O(1) lookups per vault.

♻️ Example approach
+const normalizedMapCache = new WeakMap<object, Map<string, Record<string, number>>>()
+
+const getNormalizedMap = (data: Record<string, Record<string, number>>) => {
+  let cached = normalizedMapCache.get(data)
+  if (!cached) {
+    cached = new Map(Object.entries(data).map(([addr, v]) => [normalizeOpenInterestAddress(addr), v]))
+    normalizedMapCache.set(data, cached)
+  }
+  return cached
+}
+
 export const findOpenInterestMapForVault = (
   data: Record<string, Record<string, number>> | undefined,
   vaultAddress: string,
 ): Record<string, number> => {
   if (!data) return {}
-  const normalizedVault = normalizeOpenInterestAddress(vaultAddress)
-  const entry = Object.entries(data).find(([address]) => normalizeOpenInterestAddress(address) === normalizedVault)
-  return entry?.[1] ?? {}
+  return getNormalizedMap(data).get(normalizeOpenInterestAddress(vaultAddress)) ?? {}
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@utils/vault/open-interest.ts` around lines 71 - 79,
findOpenInterestMapForVault currently does a linear scan and re-normalizes every
address on each lookup, which makes repeated vault rendering unnecessarily
expensive. Refactor this helper to use a memoized normalized-address lookup map
keyed by the identity of data, so the normalization work is done once per data
change and subsequent calls are O(1). Keep the fix localized around
findOpenInterestMapForVault and the normalizeOpenInterestAddress flow,
preserving the same return shape.
utils/vault/collateral-exposure.ts (1)

46-54: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Export the address-lookup helper to avoid triplicated logic.

readOpenInterestUsd is the single, checksum-safe implementation for matching a collateral address against the openInterestUsdByCollateral map, but it isn't exported. VaultCollateralExposureModal.vue and VaultOverviewBlockBorrow.vue each reimplement the same lookup with a plain .toLowerCase() comparison instead of reusing it. Exporting it (or a small getOpenInterestUsdForCollateral wrapper) would remove the duplication and keep the normalization logic in one place.

♻️ Proposed export
-const readOpenInterestUsd = (
+export const readOpenInterestUsd = (
   openInterestUsdByCollateral: Record<string, number>,
   collateralAddress: string,
 ): number => {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@utils/vault/collateral-exposure.ts` around lines 46 - 54, Export the
address-lookup helper from collateral-exposure so the checksum-safe matching
logic lives in one place. Update readOpenInterestUsd (or add a small
getOpenInterestUsdForCollateral wrapper) in the utility module, then change
VaultCollateralExposureModal.vue and VaultOverviewBlockBorrow.vue to call that
shared helper instead of reimplementing the .toLowerCase() lookup. Keep
normalizeAddress-based matching as the single source of truth.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@components/entities/vault/overview/earn/VaultOverviewEarnBlockExposure.vue`:
- Around line 293-297: The new Exposure summary inside
VaultOverviewEarnBlockExposure is bubbling clicks to the row container’s
onExposureClick handler, so opening the preview modal also triggers
navigation/side effects. Add a click guard on the VaultExposureSummary
interaction in the exposure block (the same way the hookWarning icon and
Exposure cap tooltip prevent bubbling) so the summary can open its modal without
invoking the parent row click handler.

In `@components/entities/vault/overview/earn/VaultOverviewEarnBlockStats.vue`:
- Around line 129-152: The async strategy-allocation loader in watchEffect lacks
error handling, so a rejection from formatAssetValue can escape Promise.all and
become unhandled. Update the watchEffect block in
VaultOverviewEarnBlockStats.vue to mirror VaultOverviewEarnBlockExposure.vue’s
load() pattern by wrapping the async work in try/catch, logging failures with
the existing logger/logWarn style, and only committing
strategyAllocationUsdByAddress when the load succeeds and the loadId still
matches.
- Around line 94-101: Treat zero-strategy vaults as ready in exposureValueState,
because the current computed in VaultOverviewEarnBlockStats.vue can stay stuck
on loading when useCollateralOpenInterest().isLoaded never becomes true but
loadOpenInterest() is intentionally skipped for vaults with no strategies.
Update the logic around exposureValueState and the related open-interest loading
path so that a vault with vault.strategies.length === 0 resolves to ready
instead of waiting for isOpenInterestLoaded, while preserving the existing
loading and unavailable states for vaults that do have strategies.

In `@components/entities/vault/overview/VaultOverviewBlockOpenInterest.vue`:
- Around line 184-185: The template in VaultOverviewBlockOpenInterest is calling
raw getAddress(vault.address).toLowerCase() multiple times instead of using the
already imported normalizeOpenInterestAddress helper. Replace those template
bindings with a single normalized/computed value derived from
normalizeOpenInterestAddress, and reuse it for the data-key/data-vault-address
bindings (and the same repeated usages elsewhere in this component) so checksum
failures are handled safely during render.

---

Outside diff comments:
In `@components/entities/vault/overview/SecuritizeVaultOverview.vue`:
- Around line 76-91: `shareTokenExchangeRate` is only computed once in
`loadRiskParameters()`, so it can become stale when the `vault` prop changes
without remounting. Move this derivation into a reactive `watchEffect` like the
other display values in `SecuritizeVaultOverview.vue`, and keep using
`vault.convertToAssets(...)` with the existing `logWarn` handling so the
exchange rate recalculates whenever `vault` updates.

---

Nitpick comments:
In `@components/entities/vault/overview/earn/VaultOverviewEarnBlockExposure.vue`:
- Around line 155-219: The exposure state and lookup logic in
VaultOverviewEarnBlockExposure.vue mixes checksummed and lowercased address keys
across related helpers. Standardize the keying convention used by
getStrategyExposureValueState, collateralExposureGroupsByStrategy, and
getStrategyExposureDisplayItems so the same address normalization is applied
consistently for all maps and lookups. Use the existing symbols getAddress,
strategyVault.address, exposureUsdPrices, and unavailableExposureUsdPrices to
align the convention and avoid future lookup regressions.

In `@components/entities/vault/overview/earn/VaultOverviewEarnBlockStats.vue`:
- Around line 48-61: `getStrategyMarketSource` in
`VaultOverviewEarnBlockStats.vue` is duplicated in
`VaultOverviewEarnBlockExposure.vue`, so extract that shared market-link builder
into a common utility (for example a strategy market source helper) and reuse it
from both components. Also memoize `getStrategyCollateralGroups` results per
strategy, following the `collateralExposureGroupsByStrategy` pattern from the
sibling component, and update `hasUnavailableExposureSplit` and
`exposureDisplayItems` to read from the cached groups instead of recomputing
repeatedly.

In `@utils/vault/collateral-exposure.ts`:
- Around line 46-54: Export the address-lookup helper from collateral-exposure
so the checksum-safe matching logic lives in one place. Update
readOpenInterestUsd (or add a small getOpenInterestUsdForCollateral wrapper) in
the utility module, then change VaultCollateralExposureModal.vue and
VaultOverviewBlockBorrow.vue to call that shared helper instead of
reimplementing the .toLowerCase() lookup. Keep normalizeAddress-based matching
as the single source of truth.

In `@utils/vault/open-interest.ts`:
- Around line 71-79: findOpenInterestMapForVault currently does a linear scan
and re-normalizes every address on each lookup, which makes repeated vault
rendering unnecessarily expensive. Refactor this helper to use a memoized
normalized-address lookup map keyed by the identity of data, so the
normalization work is done once per data change and subsequent calls are O(1).
Keep the fix localized around findOpenInterestMapForVault and the
normalizeOpenInterestAddress flow, preserving the same return shape.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: euler-xyz/coderabbit/.coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: ad4936b8-5fd3-42ba-ac3c-fc745c5a6cd0

📥 Commits

Reviewing files that changed from the base of the PR and between 0f45f3c and d6977ef.

📒 Files selected for processing (29)
  • components/entities/vault/VaultCollateralExposureModal.vue
  • components/entities/vault/VaultEarnItem.vue
  • components/entities/vault/VaultExposureListModal.vue
  • components/entities/vault/VaultExposureSummary.vue
  • components/entities/vault/VaultItem.vue
  • components/entities/vault/VaultLabelsAndAssets.vue
  • components/entities/vault/discovery/DiscoveryMarketAccordion.vue
  • components/entities/vault/discovery/DiscoveryMarketAttributeMatrix.vue
  • components/entities/vault/overview/SecuritizeVaultOverview.vue
  • components/entities/vault/overview/VaultOverviewBlockBorrow.vue
  • components/entities/vault/overview/VaultOverviewBlockCyclicalIRM.vue
  • components/entities/vault/overview/VaultOverviewBlockGeneral.vue
  • components/entities/vault/overview/VaultOverviewBlockIRM.vue
  • components/entities/vault/overview/VaultOverviewBlockOpenInterest.vue
  • components/entities/vault/overview/VaultOverviewPairBlockOpenInterest.vue
  • components/entities/vault/overview/earn/VaultOverviewEarnBlockExposure.vue
  • components/entities/vault/overview/earn/VaultOverviewEarnBlockStats.vue
  • composables/useCollateralOpenInterest.ts
  • server/utils/v3-proxy.ts
  • tests/composables/useCollateralOpenInterest.test.ts
  • tests/server/v3-proxy.test.ts
  • tests/utils/open-interest.test.ts
  • tests/utils/vault/collateral-exposure.test.ts
  • tests/utils/vault/exposure-display.test.ts
  • utils/discoveryCalculations.ts
  • utils/vault/collateral-exposure.ts
  • utils/vault/exposure-display.ts
  • utils/vault/exposure-groups.ts
  • utils/vault/open-interest.ts
✅ Files skipped from review due to trivial changes (1)
  • components/entities/vault/discovery/DiscoveryMarketAccordion.vue
🚧 Files skipped from review as they are similar to previous changes (2)
  • components/entities/vault/overview/VaultOverviewBlockIRM.vue
  • components/entities/vault/overview/VaultOverviewBlockCyclicalIRM.vue

Comment thread components/entities/vault/overview/VaultOverviewBlockOpenInterest.vue Outdated
@railway-app railway-app Bot temporarily deployed to euler-lite / euler-lite-pr-617 July 1, 2026 10:44 Destroyed
@railway-app railway-app Bot temporarily deployed to euler-lite / euler-lite-pr-617 July 1, 2026 10:50 Destroyed
@railway-app railway-app Bot temporarily deployed to euler-lite / euler-lite-pr-617 July 1, 2026 10:57 Destroyed
Seranged added 2 commits July 1, 2026 12:08
Hide bad debt and open-interest surfaces when the V3 backend is disabled, while preserving soft unavailable states for enabled fetch failures.
@railway-app railway-app Bot temporarily deployed to euler-lite / euler-lite-pr-617 July 1, 2026 11:25 Destroyed
Seranged added 2 commits July 1, 2026 12:34
Keep the static collateral/LTV overview visible when the V3 backend is disabled while continuing to gate live exposure values on open interest data.
fix: preserve collateral config without v3
@railway-app railway-app Bot temporarily deployed to euler-lite / euler-lite-pr-617 July 1, 2026 11:35 Destroyed
@Seranged Seranged requested a review from VSBDev July 1, 2026 11:36
@Seranged Seranged merged commit 807c759 into development Jul 1, 2026
2 checks passed
@Seranged Seranged deleted the feature/lite-215-convert-vault-page-sections-to-accordion-style branch July 1, 2026 11:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants