Skip to content

feat: add manager profile pages - #618

Draft
Seranged wants to merge 10 commits into
developmentfrom
feature/lite-256-dedicated-profile-page-for-curators-risk-managers
Draft

Seranged wants to merge 10 commits into
developmentfrom
feature/lite-256-dedicated-profile-page-for-curators-risk-managers

Conversation

@Seranged

@Seranged Seranged commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Add dedicated manager profile pages for curator and risk-manager entities.
  • Link unambiguous manager displays from vault surfaces to an app-native profile destination.

Changes

  • Adds a /managers/[slug] route with entity details, social links, current-network managed markets, and Earn vaults.
  • Adds shared manager-profile helpers and a reusable manager entity link component.
  • Keeps multi-manager labels as plain text so ambiguous displays do not route to a single profile.
  • Preserves keyboard access for manager links rendered inside clickable vault cards.
  • Waits for labels, market groups, TVL, and Earn vault loading before showing empty results.

Test plan

  • npm run test:run -- tests/utils/manager-profile.test.ts (7 tests)
  • Run focused ESLint across the 13 changed files
  • npm run typecheck

Summary by CodeRabbit

  • New Features

    • Added manager profile pages with descriptions, social links, managed markets, and Earn vaults.
    • Added reusable manager entity links with optional avatars and profile navigation.
    • Improved risk manager and capital allocator displays across vault views.
    • Added support for product logos and flexible social profile metadata.
  • Bug Fixes

    • Standardized manager names, links, and external URL handling across the application.
  • Tests

    • Added coverage for manager identification, profile paths, names, and social links.

@railway-app
railway-app Bot temporarily deployed to euler-lite / euler-lite-pr-618 June 24, 2026 11:59 Destroyed
@coderabbitai

coderabbitai Bot commented Jun 24, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Changes

Manager Profiles and Entity Links

Layer / File(s) Summary
Manager profile contracts and utilities
entities/euler/labels.ts, utils/manager-profile.ts, tests/utils/manager-profile.test.ts
Extends label metadata types and adds manager slug, display-name, profile path, and external-link utilities with tests.
Reusable manager entity links
components/entities/manager/ManagerEntityLink.vue, components/entities/vault/..., components/entities/vault/overview/...
Adds reusable entity linking and replaces inline vault manager and curator rendering.
Manager profile data and page
composables/useEulerManagerProfile.ts, pages/managers/[slug].vue
Derives managed markets and Earn vaults, then renders manager identity, external links, markets, and vaults.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Suggested reviewers: kasperpawlowski

Sequence Diagram(s)

sequenceDiagram
  participant Browser
  participant ManagerPage
  participant useEulerManagerProfile
  participant EulerLabels
  participant MarketGroups
  Browser->>ManagerPage: navigate to /managers/:slug
  ManagerPage->>useEulerManagerProfile: load profile for slug
  useEulerManagerProfile->>EulerLabels: resolve entity and managed products
  useEulerManagerProfile->>MarketGroups: resolve managed markets
  useEulerManagerProfile-->>ManagerPage: return profile data and loading state
  ManagerPage-->>Browser: render manager profile
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the added manager profile pages, which are a significant part of the pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/lite-256-dedicated-profile-page-for-curators-risk-managers

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

@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: 2

🧹 Nitpick comments (2)
composables/useEulerManagerProfile.ts (2)

35-61: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Consider precomputing an entity→slug map to avoid repeated O(n) scans.

getEulerLabelEntitySlug runs Object.entries(entities).find(...) (up to twice) for every manager of every vault, so each list recompute is roughly O(vaults · managers · entities). Building a reverse map once per recompute keeps the filters near-linear.

🤖 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 `@composables/useEulerManagerProfile.ts` around lines 35 - 61, The filtering in
useEulerManagerProfile.ts repeatedly calls getEulerLabelEntitySlug for every
manager, causing nested O(n) scans over entities during evaults,
securitizeVaults, and earnVaults recomputation. Precompute a reverse
entity-to-slug lookup once per recompute in the same composable, then have
managesEntity and managesEarnEntity read from that map instead of calling
getEulerLabelEntitySlug repeatedly. Keep the existing computed lists and
filter/sort flow, but make the slug resolution constant-time by keying off the
same entities source used by getEulerLabelEntitySlug.

35-43: 🗄️ Data Integrity & Integration | 🔵 Trivial

Cache entity slug lookups in useEulerManagerProfile
getEulerLabelEntitySlug scans entities with Object.entries for every manager check, so these computed filters repeat the same work. A reverse lookup map would avoid the extra passes.

🤖 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 `@composables/useEulerManagerProfile.ts` around lines 35 - 43, Cache the
repeated entity slug resolution in useEulerManagerProfile by introducing a
reverse lookup from entity id to slug instead of calling getEulerLabelEntitySlug
inside every managesEntity and managesEarnEntity check. Build the lookup once
from entities, then use it in the managers.some callbacks so the computed
filters no longer repeatedly scan Object.entries. Keep the changes localized
around getEulerLabelEntitySlug, managesEntity, and managesEarnEntity.
🤖 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/manager/ManagerEntityLink.vue`:
- Around line 59-67: The clickable `span` in `ManagerEntityLink.vue` is
mouse-only and needs keyboard support. Update the `spanLink && isLinked` branch
on the `<span>` used for `goToManager` to be accessible by adding link semantics
and keyboard activation, such as `role="link"`, `tabindex="0"`, and a keydown
handler for Enter/Space, while keeping the existing click behavior and avoiding
nested anchors.

In `@components/entities/vault/VaultItem.vue`:
- Around line 301-308: The manager-profile link is using the click-only span
branch in ManagerEntityLink, so it is not keyboard accessible from the vault
cards. Update the shared span-link path in ManagerEntityLink so it renders a
tabbable, keyboard-activatable control with proper link semantics, and make the
VaultItem usage continue through that fixed path rather than relying on the
non-accessible span behavior.

---

Nitpick comments:
In `@composables/useEulerManagerProfile.ts`:
- Around line 35-61: The filtering in useEulerManagerProfile.ts repeatedly calls
getEulerLabelEntitySlug for every manager, causing nested O(n) scans over
entities during evaults, securitizeVaults, and earnVaults recomputation.
Precompute a reverse entity-to-slug lookup once per recompute in the same
composable, then have managesEntity and managesEarnEntity read from that map
instead of calling getEulerLabelEntitySlug repeatedly. Keep the existing
computed lists and filter/sort flow, but make the slug resolution constant-time
by keying off the same entities source used by getEulerLabelEntitySlug.
- Around line 35-43: Cache the repeated entity slug resolution in
useEulerManagerProfile by introducing a reverse lookup from entity id to slug
instead of calling getEulerLabelEntitySlug inside every managesEntity and
managesEarnEntity check. Build the lookup once from entities, then use it in the
managers.some callbacks so the computed filters no longer repeatedly scan
Object.entries. Keep the changes localized around getEulerLabelEntitySlug,
managesEntity, and managesEarnEntity.
🪄 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: 2f674161-5228-4d81-a9d8-4e6637ca87c5

📥 Commits

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

📒 Files selected for processing (12)
  • components/entities/manager/ManagerEntityLink.vue
  • components/entities/vault/SecuritizeVaultItem.vue
  • components/entities/vault/VaultBorrowItem.vue
  • components/entities/vault/VaultEarnItem.vue
  • components/entities/vault/VaultItem.vue
  • components/entities/vault/overview/SecuritizeVaultOverview.vue
  • components/entities/vault/overview/VaultOverviewBlockGeneral.vue
  • components/entities/vault/overview/earn/VaultOverviewEarnBlockGeneral.vue
  • composables/useEulerManagerProfile.ts
  • pages/managers/[slug].vue
  • tests/utils/manager-profile.test.ts
  • utils/manager-profile.ts

Comment thread components/entities/manager/ManagerEntityLink.vue
Comment thread components/entities/vault/VaultItem.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.

Reviewed current head 1138f42baffd32f7f6d04e479131efc869a2e887.

Verdict: COMMENT — one non-blocking accessibility/UX suggestion inline; no correctness or security blocker found.

Scope reviewed:

  • New /managers/[slug] page and useEulerManagerProfile data path.
  • Shared ManagerEntityLink and profile helper utilities.
  • Refactor of curator/risk-manager display across vault cards and overview blocks.
  • Active bot feedback: CodeRabbit had a walkthrough/pre-merge summary only; no material inline findings to verify.

Validation performed:

  • git diff --check origin/development...HEAD
  • npm run test:run -- tests/utils/manager-profile.test.ts
  • npx eslint components/entities/manager/ManagerEntityLink.vue composables/useEulerManagerProfile.ts pages/managers/[slug].vue utils/manager-profile.ts tests/utils/manager-profile.test.ts
  • npm run typecheck
  • npm run build
  • Headed Chromium/Xvfb browser smoke on a local production build:
    • Desktop /managers/k3?network=ethereum: rendered manager identity, social links, product cards, and managed vault list.
    • Mobile /managers/k3?network=ethereum: same content rendered in the narrow viewport.
    • /managers/not-a-real-manager?network=ethereum: rendered the not-found state.

Smoke coverage: browser visual smoke + mobile smoke; no wallet/signing coverage, as this PR is read-only navigation/profile UI.

Screenshots: captured during local smoke, but not attached here because the available GitHub CLI path in this environment does not support binary PNG upload. No sensitive content was captured.

Scalability / maintainability hygiene pass:

  • Good direction overall: the repeated avatar/name rendering was centralized in ManagerEntityLink, and helper logic has focused utility tests.
  • Sibling surfaces checked: lend/borrow/earn cards, Securitize cards, and vault overview blocks now use the shared component where this PR touches manager display. Explore/list filters remain name-based filters and are out of scope for profile navigation.
  • Remaining refinement is the spanLink accessibility point inline; because most card call sites need to avoid nested anchors, the shared component is the right place to make that behavior reusable and keyboard-accessible.

:class="avatarClass"
:label="displayName"
:src="entityLogos"
/>

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.

💡 Suggestion: spanLink makes the manager destination mouse-clickable, but it renders as a plain <span> with no href, role="link", tabindex, or keyboard handler. Most of the new vault-card call sites use this mode to avoid nested anchors, so keyboard and screen-reader users do not get the same navigation affordance as mouse users. Consider making the span mode an accessible link-like control (for example role="link", tabindex="0", and Enter/Space handling) or otherwise restructuring the card/link relationship so the shared component stays accessible everywhere.

@railway-app

railway-app Bot commented Jun 24, 2026

Copy link
Copy Markdown

🚅 Deployed to the euler-lite-pr-618 environment in euler-lite(dev,PR previews)

Service Status Web Updated (UTC)
dev-build ✅ Success (View Logs) Web Aug 5, 2026 at 8:24 am

@Seranged
Seranged marked this pull request as draft June 24, 2026 13:19
@railway-app
railway-app Bot temporarily deployed to euler-lite / euler-lite-pr-618 June 24, 2026 14:00 Destroyed
@railway-app
railway-app Bot temporarily deployed to euler-lite / euler-lite-pr-618 June 24, 2026 14:16 Destroyed
@railway-app
railway-app Bot temporarily deployed to euler-lite / euler-lite-pr-618 June 25, 2026 10:48 Destroyed
@railway-app
railway-app Bot temporarily deployed to euler-lite / euler-lite-pr-618 June 25, 2026 10:52 Destroyed
@railway-app
railway-app Bot temporarily deployed to euler-lite / euler-lite-pr-618 June 25, 2026 10:54 Destroyed
@railway-app
railway-app Bot temporarily deployed to euler-lite / euler-lite-pr-618 June 25, 2026 11:04 Destroyed
@railway-app
railway-app Bot temporarily deployed to euler-lite / euler-lite-pr-618 June 25, 2026 11:05 Destroyed
@railway-app
railway-app Bot temporarily deployed to euler-lite / euler-lite-pr-618 June 25, 2026 11:11 Destroyed
@railway-app
railway-app Bot temporarily deployed to euler-lite(dev,PR previews) / euler-lite-pr-618 July 31, 2026 09:55 Destroyed
@railway-app
railway-app Bot temporarily deployed to euler-lite(dev,PR previews) / euler-lite-pr-618 August 4, 2026 16:03 Destroyed
@Seranged Seranged changed the title feat: add manager profile pages feat: integrate public labels Aug 4, 2026
@Seranged
Seranged marked this pull request as ready for review August 4, 2026 16:07

@cursor cursor 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.

Review summary

This PR switches client label loading to Public Labels V3 while still hydrating the vault registry from the legacy /api/internal/vaults snapshot. That split creates a reachable path where vaults delisted (or never present) in V3 remain listed as verified on Lend/Borrow/Earn/Explore. I also traced geo/visibility composition, co-brand vs ownership, governor verification, v-html/autoLink, logo URL handling, and the V3 proxy allowlist.

Reviewed: composables/useEulerLabels.ts, utils/public-labels.ts, utils/manager-profile.ts, composables/useEulerManagerProfile.ts, composables/useMarketGroups.ts, utils/discoveryCalculations.ts, manager profile page/ManagerEntityLink, vault card/overview call sites, server/utils/v3-proxy.ts, vaults-cache vs client load path · Traced beyond the diff: hydrateFromServer → registry verified: truegetVerifiedEVaults / isVerifiedVault / market-group orphans; useGeoBlock + legacy visibility merge; governor verification product.entity (not co-brands) · Protocol skills consulted: none needed (labels/listing UI; no tx-building changes)

Findings

🚨 Critical

composables/useEulerLabels.ts:131 — V3 listing set does not prune legacy snapshot vaults from the registry
Client labels now come from fetchPublicLabelsData (V3 inventory → verifiedVaultAddresses / earnVaults), but loadVaults still hydrates /api/internal/vaults, which server/utils/vaults-cache.ts builds from legacy products.json / earn-vaults.json, and marks every hydrated vault verified: true. The silent follow-up RPC only setManys the V3 address set; it never removes registry entries outside that set. Lend uses getVerifiedEVaults() (registry entry.verified === true only), Borrow uses that same list via borrowList, Earn uses isVerifiedVault() which is true when entry.verified === true, and Explore orphan clustering walks the full registry.

Trigger: A vault remains in the warm legacy snapshot but is absent from the V3-derived verified/earn sets (V3 delist, inventory lag, or assessment-only row without compatibility retain).
Consequence: That vault stays listed and deep-linkable as a verified market after labels load, so Public Labels is not actually authoritative for what users can open.
Fix direction: After labels are ready, intersect hydrate/RPC registration with the V3 verified/earn/escrow sets (prune extras), or make getVerifiedEVaults / Earn list filters require membership in those label sets—not only the registry flag.

⚠️ Warnings

utils/public-labels.ts:465 — Effective geo/visibility only attaches when V3 product IDs match legacy keys
block / restricted / notExplorable (and vault-override equivalents) copy from the SDK snapshot only when legacy.products[productKey] exists. Raw V3 geo-policies are stored on rawGeoPolicies and never fed into useGeoBlock. New V3-only products, standalone __vault_* wrappers, or renamed product IDs therefore list with no country blocks even when V3 policy rows exist.

Trigger: US (or other restricted) user opens a newly published or ID-renamed product that is blocked only in V3 geo policies / under the old legacy key.
Consequence: Client geo gating does not hide or disable that market; only the global sanctioned-country server gate still applies.
Fix direction: Until V3 ships derived eligibility, map policies by vault address (not only product key), or refuse to list V3-only products that lack a legacy visibility row.

💬 Suggestions

utils/public-labels.ts:291 — Earn vaults with productId are written into product.vaults
buildProduct does not filter vaultType, so an earn inventory row with a productId becomes a normal product member. That address then participates in product lookup, discovery grouping (if present in the registry), and product-path geo helpers. Prefer excluding vaultType === 'earn' from product.vaults / deprecatedVaults (keep earn-only structures) unless product membership for earn is an explicit contract.

Open questions

  • Are production V3 /curation/vaults and legacy products.json guaranteed identical for the rollout window? If yes, the critical hydrate skew is latent until the first deliberate V3 delist or inventory drift; if not, it is already user-visible. Checking one chain’s address-set diff between the warm snapshot and normalizePublicLabelsData(...).verifiedVaultAddresses would settle it.

Not flagged

  • Co-brands correctly stay display-only for ownership/governor checks (product.entity only).
  • Manager v-html goes through autoLink HTML escaping; hosted https:// logos match existing CSP img-src https:.
  • Prior a11y notes on spanLink look addressed in current ManagerEntityLink.
  • Expanding the V3 proxy allowlist for unused assessment paths is extra surface but not a funds/XSS issue by itself.
Open in Web View Automation 

Sent by Cursor Automation: Lite PR Reviewer

Comment thread composables/useEulerLabels.ts Outdated
return sdk.eulerLabelsService.fetchEulerLabelsData(chainId)
const legacy = sdk.eulerLabelsService.fetchEulerLabelsData(chainId)
try {
return await fetchPublicLabelsData(request, chainId, undefined, legacy)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🚨 Critical — V3 labels vs legacy vault snapshot skew

This path makes Public Labels V3 authoritative for verifiedVaultAddresses / earnVaults, but loadVaults still hydrates /api/internal/vaults from legacy products.json / earn-vaults.json and stamps those entries verified: true. The later silent RPC only upserts the V3 address set; it does not prune registry rows outside it.

Trigger: Vault still in the warm snapshot, absent from the V3-derived verified/earn sets (delist, inventory lag, or non-retained assessment-only row).
Consequence: /lend (getVerifiedEVaults), /borrow (borrowList), /earn (isVerifiedVault via entry.verified), and Explore orphans can keep showing that vault as listed/verified even though V3 no longer lists it.
Fix: Intersect hydrate/registration with the V3 verified/earn/escrow sets (drop extras), or require label-set membership in the list filters—not only the registry flag.

Comment thread utils/public-labels.ts Outdated
Comment on lines +465 to +470
for (const [productKey, product] of Object.entries(products)) {
const legacyProduct = legacy?.products?.[productKey] as EulerLabelProduct | undefined
if (!legacyProduct) continue
product.block = legacyProduct.block
product.restricted = legacyProduct.restricted
product.notExplorable = legacyProduct.notExplorable

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Warning — geo/visibility merge is product-key exact match only

Effective block / restricted / notExplorable copy from legacy only when the V3 product id matches a legacy key. Raw V3 geo-policies stay on rawGeoPolicies and never reach useGeoBlock.

Trigger: Newly published V3-only product, standalone __vault_* product, or renamed product id whose block list lives only under the old key / in V3 geo policies.
Consequence: Restricted-jurisdiction users still see and can open that market in the client; only the global sanctioned-country gate remains.
Fix: Until derived eligibility exists, attach visibility by vault address or withhold listing when no legacy visibility row matches.

Comment thread utils/public-labels.ts Outdated
Comment on lines +291 to +296
for (const vault of vaults) {
const address = getAddress(vault.address)
if (vault.isDeprecated) deprecated.push(address)
else active.push(address)
vaultOverrides[address] = makeVaultOverride(vault)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💬 Suggestion — exclude earn rows from product.vaults

This loop adds every inventory row on the product, including vaultType === 'earn', into active / deprecated. Those addresses then participate in product lookup, discovery membership, and product-path geo helpers.

Unless earn-in-product membership is an explicit Public Labels contract, filter earn (and escrow) out here and keep them on the earn-only structures only.

@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: 2

🧹 Nitpick comments (1)
composables/useEulerLabels.ts (1)

81-92: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Type the test override with the migrated label shape.

__setEulerLabelsDataForTest still accepts Partial<EulerLabelsData>, but it reads rawGeoPolicies through a cast. Tests cannot provide this new field without another cast. Use Partial<MigratedEulerLabelsData> or a dedicated test input type.

Suggested type update
-export const __setEulerLabelsDataForTest = (data: Partial<EulerLabelsData> = {}) => {
+export const __setEulerLabelsDataForTest = (data: Partial<MigratedEulerLabelsData> = {}) => {
...
-    rawGeoPolicies: (data as Partial<MigratedEulerLabelsData>).rawGeoPolicies ?? [],
+    rawGeoPolicies: data.rawGeoPolicies ?? [],
🤖 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 `@composables/useEulerLabels.ts` around lines 81 - 92, Update
__setEulerLabelsDataForTest to accept Partial<MigratedEulerLabelsData> (or an
equivalent dedicated test input type) instead of Partial<EulerLabelsData>, then
access rawGeoPolicies directly without casting. Preserve the existing default
initialization and setLabelsData behavior.
🤖 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 `@composables/useEulerLabels.ts`:
- Around line 130-140: Update the legacy fallback handling in loadLabels so the
compatibility request is resolved best-effort before or independently of
fetchPublicLabelsData, preventing its rejection from failing the V3 Promise.all
path. Preserve successful V3 products, entities, and vaults when legacy data is
unavailable, and only use the resolved legacy result for fallback data rather
than awaiting the same rejected promise in the catch block.

In `@pages/managers/`[slug].vue:
- Around line 89-95: Update getManagerProfileSocialLinks() to parse each
social-link URL and retain only valid values whose protocol is exactly "https:".
Filter out invalid URLs and all non-HTTPS protocols before the resulting
link.url is bound to the manager profile anchor href.

---

Nitpick comments:
In `@composables/useEulerLabels.ts`:
- Around line 81-92: Update __setEulerLabelsDataForTest to accept
Partial<MigratedEulerLabelsData> (or an equivalent dedicated test input type)
instead of Partial<EulerLabelsData>, then access rawGeoPolicies directly without
casting. Preserve the existing default initialization and setLabelsData
behavior.
🪄 Autofix

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: 17a1aa85-91fe-471b-a394-027eeb238af5

📥 Commits

Reviewing files that changed from the base of the PR and between 1138f42 and 251192f.

📒 Files selected for processing (19)
  • components/entities/manager/ManagerEntityLink.vue
  • components/entities/vault/SecuritizeVaultItem.vue
  • components/entities/vault/VaultBorrowItem.vue
  • components/entities/vault/VaultEarnItem.vue
  • components/entities/vault/VaultItem.vue
  • components/entities/vault/VaultPoints.vue
  • components/entities/vault/VaultPointsModal.vue
  • components/entities/vault/discovery/DiscoveryMarketCard.vue
  • components/entities/vault/overview/SecuritizeVaultOverview.vue
  • components/entities/vault/overview/VaultOverviewBlockGeneral.vue
  • components/entities/vault/overview/earn/VaultOverviewEarnBlockGeneral.vue
  • composables/useEulerLabels.ts
  • composables/useEulerManagerProfile.ts
  • composables/useMarketGroups.ts
  • docs/architecture.md
  • docs/vault-labels-and-verification.md
  • entities/euler/labels.ts
  • entities/lend-discovery.ts
  • pages/managers/[slug].vue
🚧 Files skipped from review as they are similar to previous changes (6)
  • components/entities/vault/overview/SecuritizeVaultOverview.vue
  • components/entities/vault/SecuritizeVaultItem.vue
  • components/entities/vault/VaultEarnItem.vue
  • components/entities/vault/VaultItem.vue
  • components/entities/vault/overview/VaultOverviewBlockGeneral.vue
  • components/entities/vault/overview/earn/VaultOverviewEarnBlockGeneral.vue

Comment thread composables/useEulerLabels.ts Outdated
Comment on lines +130 to +140
const legacy = sdk.eulerLabelsService.fetchEulerLabelsData(chainId)
try {
return await fetchPublicLabelsData(request, chainId, undefined, legacy)
}
catch (error) {
logWarn('labels/public-v3', error)
return {
...await legacy,
rawGeoPolicies: [],
} as MigratedEulerLabelsData
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

Keep the V3 load independent from the legacy fallback request.

legacy is passed to fetchPublicLabelsData as a promise. The supplied utils/public-labels.ts implementation awaits that promise in the same Promise.all as the V3 page requests. If the SDK compatibility request fails while V3 succeeds, the V3 result is discarded. The catch then awaits the same rejected promise again, so loadLabels finishes with empty or stale label state.

Resolve legacy data as best-effort input. Preserve successful V3 products, entities, and vaults when the compatibility request is unavailable.

🤖 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 `@composables/useEulerLabels.ts` around lines 130 - 140, Update the legacy
fallback handling in loadLabels so the compatibility request is resolved
best-effort before or independently of fetchPublicLabelsData, preventing its
rejection from failing the V3 Promise.all path. Preserve successful V3 products,
entities, and vaults when legacy data is unavailable, and only use the resolved
legacy result for fallback data rather than awaiting the same rejected promise
in the catch block.

Comment thread pages/managers/[slug].vue
Comment on lines +89 to +95
<a
v-for="link in socialLinks"
:key="link.label"
:href="link.url"
target="_blank"
rel="noopener noreferrer"
class="inline-flex items-center gap-6 rounded-8 border border-line-default bg-surface-elevated px-12 py-8 text-p3 text-content-primary hover:border-line-emphasis hover:text-accent-600 transition-colors"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

ast-grep outline utils/manager-profile.ts --items all
rg -n -C 12 'getManagerProfileSocialLinks|new URL|protocol|https:|javascript:|url' \
  utils/manager-profile.ts

Repository: euler-xyz/euler-lite

Length of output: 4439


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- relevant source ---'
sed -n '1,82p' utils/manager-profile.ts

printf '%s\n' '--- entity type and data references ---'
rg -n -C 8 'type EulerLabelEntity|interface EulerLabelEntity|social:|getManagerProfileSocialLinks|entity\.url' \
  --glob '*.{ts,tsx,vue,json,yaml,yml}' .

printf '%s\n' '--- URL behavior for representative inputs ---'
node - <<'JS'
const withProtocol = (url) => {
  if (url.startsWith('hhttps://')) return `https://${url.slice('hhttps://'.length)}`
  if (/^https?:\/\//i.test(url)) return url
  return `https://${url}`
}
const inputs = [
  'https://example.com',
  'http://example.com',
  'javascript:alert(1)',
  ' //example.com',
  '//example.com',
  'HTTPS://example.com',
  'hhttps://example.com',
]
for (const input of inputs) {
  const href = withProtocol(input.trim())
  let parsed
  try {
    const u = new URL(href)
    parsed = {protocol: u.protocol, href: u.href}
  } catch (error) {
    parsed = {error: String(error)}
  }
  console.log(JSON.stringify({input, href, parsed}))
}
JS

Repository: euler-xyz/euler-lite

Length of output: 21675


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- relevant source ---'
sed -n '1,82p' utils/manager-profile.ts

printf '%s\n' '--- entity type and data references ---'
rg -n -C 8 'type EulerLabelEntity|interface EulerLabelEntity|social:|getManagerProfileSocialLinks|entity\.url' \
  --glob '*.{ts,tsx,vue,json,yaml,yml}' .

printf '%s\n' '--- URL behavior for representative inputs ---'
node - <<'JS'
const withProtocol = (url) => {
  if (url.startsWith('hhttps://')) return `https://${url.slice('hhttps://'.length)}`
  if (/^https?:\/\//i.test(url)) return url
  return `https://${url}`
}
for (const input of [
  'https://example.com',
  'http://example.com',
  'javascript:alert(1)',
  '//example.com',
  'HTTPS://example.com',
  'hhttps://example.com',
]) {
  const href = withProtocol(input.trim())
  try {
    const u = new URL(href)
    console.log(JSON.stringify({input, href, protocol: u.protocol, parsed: u.href}))
  } catch (error) {
    console.log(JSON.stringify({input, href, error: String(error)}))
  }
}
JS

Repository: euler-xyz/euler-lite

Length of output: 21500


Security Misconfiguration (CWE-20): Improper Input Validation

Exploitability: Theoretical

Restrict manager profile links to https: URLs. getManagerProfileSocialLinks() passes http:// values through unchanged. Parse each URL and retain it only when protocol === 'https:'; reject invalid and non-HTTPS values before binding link.url to href.

🤖 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 `@pages/managers/`[slug].vue around lines 89 - 95, Update
getManagerProfileSocialLinks() to parse each social-link URL and retain only
valid values whose protocol is exactly "https:". Filter out invalid URLs and all
non-HTTPS protocols before the resulting link.url is bound to the manager
profile anchor href.

Add profile pages for label entities and link risk manager/curator displays into the new route.
Add keyboard semantics to the span-based manager profile affordance used inside vault cards.
Normalize manager profile links and surface labeled governance addresses plus product metadata from labels.
Align manager profiles with the review notes by removing governance addresses and individual lending vault lists, using market cards, and keeping flexible profile links.
Drop the low-value links stat card now that profile links render directly below the summary.
Remove the extra manager-profile label and place the back button with the manager identity header.
@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@Seranged Seranged changed the title feat: integrate public labels feat: add manager profile pages Aug 5, 2026
@Seranged
Seranged marked this pull request as draft August 5, 2026 08:23

@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: 2

🤖 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 `@pages/managers/`[slug].vue:
- Around line 89-94: Update the v-for key in the socialLinks rendering to use
the same label-and-URL combination as the existing deduplication logic, rather
than link.label alone, so every external link has a unique key.

In `@utils/manager-profile.ts`:
- Around line 9-11: Update the URL normalization logic to detect and repair the
hhttps:// typo case-insensitively, preventing values such as HHTTPS:// from
producing nested schemes. Preserve normal http/https handling, and add a test
covering the uppercase typo variant.
🪄 Autofix

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: b66558c8-53c5-43b1-af63-1577a30e0ba5

📥 Commits

Reviewing files that changed from the base of the PR and between fc0dacc and ed47cdb.

📒 Files selected for processing (13)
  • components/entities/manager/ManagerEntityLink.vue
  • components/entities/vault/SecuritizeVaultItem.vue
  • components/entities/vault/VaultBorrowItem.vue
  • components/entities/vault/VaultEarnItem.vue
  • components/entities/vault/VaultItem.vue
  • components/entities/vault/overview/SecuritizeVaultOverview.vue
  • components/entities/vault/overview/VaultOverviewBlockGeneral.vue
  • components/entities/vault/overview/earn/VaultOverviewEarnBlockGeneral.vue
  • composables/useEulerManagerProfile.ts
  • entities/euler/labels.ts
  • pages/managers/[slug].vue
  • tests/utils/manager-profile.test.ts
  • utils/manager-profile.ts
🚧 Files skipped from review as they are similar to previous changes (9)
  • components/entities/vault/overview/VaultOverviewBlockGeneral.vue
  • components/entities/vault/overview/SecuritizeVaultOverview.vue
  • components/entities/manager/ManagerEntityLink.vue
  • components/entities/vault/VaultEarnItem.vue
  • components/entities/vault/SecuritizeVaultItem.vue
  • components/entities/vault/VaultBorrowItem.vue
  • components/entities/vault/overview/earn/VaultOverviewEarnBlockGeneral.vue
  • composables/useEulerManagerProfile.ts
  • components/entities/vault/VaultItem.vue

Comment thread pages/managers/[slug].vue
Comment on lines +89 to +94
<a
v-for="link in socialLinks"
:key="link.label"
:href="link.url"
target="_blank"
rel="noopener noreferrer"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Use a unique key for each external link.

Line 91 uses link.label as the key. The link helper permits the same label with different URLs. For example, an entity website and a website social field both produce Website. Duplicate keys can reuse the wrong anchor during updates. Use the same label-and-URL key as the deduplication logic.

Proposed fix
-                :key="link.label"
+                :key="`${link.label}:${link.url}`"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<a
v-for="link in socialLinks"
:key="link.label"
:href="link.url"
target="_blank"
rel="noopener noreferrer"
<a
v-for="link in socialLinks"
:key="`${link.label}:${link.url}`"
:href="link.url"
target="_blank"
rel="noopener noreferrer"
🤖 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 `@pages/managers/`[slug].vue around lines 89 - 94, Update the v-for key in the
socialLinks rendering to use the same label-and-URL combination as the existing
deduplication logic, rather than link.label alone, so every external link has a
unique key.

Comment thread utils/manager-profile.ts
Comment on lines +9 to +11
if (url.startsWith('hhttps://')) return `https://${url.slice('hhttps://'.length)}`
if (/^https?:\/\//i.test(url)) return url
return `https://${url}`

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Normalize hhttps:// case-insensitively.

Line 9 repairs only lowercase hhttps://. Line 36 accepts HHTTPS://, then this function produces a malformed URL such as https://HHTTPS://…. Normalize the typo case-insensitively and add a matching test.

Proposed fix
 const withProtocol = (url: string): string => {
-  if (url.startsWith('hhttps://')) return `https://${url.slice('hhttps://'.length)}`
+  if (/^hhttps:\/\//i.test(url)) return `https://${url.slice('hhttps://'.length)}`
   if (/^https?:\/\//i.test(url)) return url
   return `https://${url}`
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (url.startsWith('hhttps://')) return `https://${url.slice('hhttps://'.length)}`
if (/^https?:\/\//i.test(url)) return url
return `https://${url}`
const withProtocol = (url: string): string => {
if (/^hhttps:\/\//i.test(url)) return `https://${url.slice('hhttps://'.length)}`
if (/^https?:\/\//i.test(url)) return url
return `https://${url}`
}
🤖 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/manager-profile.ts` around lines 9 - 11, Update the URL normalization
logic to detect and repair the hhttps:// typo case-insensitively, preventing
values such as HHTTPS:// from producing nested schemes. Preserve normal
http/https handling, and add a test covering the uppercase typo variant.

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.

2 participants