Skip to content

Conversation

@reallybeard
Copy link
Contributor

@reallybeard reallybeard commented Jan 19, 2026

Description

Visual polish everywhere:

  • Cards now have a sleeker look with inset shadows instead of plain borders
  • Green buttons look better in dark mode (black text instead of white)
  • The "Available to Deposit" section got a nice green gradient glow effect and is more prominent now

New "Add More" feature:

  • When you already have a position staked somewhere, you'll now see a slick prompt letting you know you have more tokens available to add, along with potential earnings - makes it super easy to compound

Related Markets redesign:

  • Instead of a grid of cards, related yields now show up as a clean list - more compact and easier to scan
    Better page layouts:
  • All the Yields pages now use the standard app layout (Main + PageHeader components) instead of doing their own thing with Container - makes everything feel more consistent with the rest of the app

Provider info cards:

  • Added descriptions for major DeFi protocols (Morpho, Lido, Aave, Compound, etc.) so users know what they're getting into

Account selector flexibility:

  • You can now hide the balance or icon in the account selector dropdown - useful in tighter spaces

Misc cleanup:

  • Avatar sizing is now standardized across the app
  • LazyLoadAvatar works better with the size system
  • Some memoization tweaks in the yield items

Issue (if applicable)

closes #

Risk

High Risk PRs Require 2 approvals

low risk mostly UI updates

What protocols, transaction types, wallets or contract interactions might be affected by this PR?

Testing

Engineering

Operations

  • 🏁 My feature is behind a flag and doesn't require operations testing (yet)

Screenshots (if applicable)

Screenshot 2026-01-19 at 1 51 54 PM Screenshot 2026-01-19 at 1 51 20 PM

Summary by CodeRabbit

  • New Features

    • "Add More" quick action to deposit into existing yield positions
    • Yield comparison cards showing provider, chain and APY
  • Improvements

    • Enhanced "Available to Deposit" panel with balance, fiat estimate and yearly earnings
    • Redesigned yield detail pages with header-driven navigation and responsive layout
    • Toggleable display options for balances and icons
    • Refreshed card/badge visuals and gradients
    • Added new translation strings for yield UI
  • Refactor

    • Page/layout restructuring and theme refinements

✏️ Tip: You can customize this high-level summary in your review settings.

gomesalexandre and others added 24 commits January 16, 2026 12:41
Remove titleOverride props so single yields show their metadata.name
(e.g., "Aave v3 USDC Lending") instead of just the asset symbol.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Display warning badges on yield cards and alerts on detail pages when
yields are marked as underMaintenance or deprecated in the API.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…badges

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Create reusable YieldExplainers component that shows reward schedule,
unbonding periods, and other yield-specific info based on mechanics type.
Add it to YieldEnterModal and EarnConfirm for consistent user education
across all enter flows.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Show a "Learn more" link with external icon when a yield has
metadata.documentation available. Appears below the description
text on the yield detail page.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add YieldExplainers component to YieldForm.tsx (yield page enter modal)
- Update ProviderDto to include website and references fields
- Use provider.references[0] for documentation link (protocol website)
- Fix docs link styling: inline icon next to description
- Prefer provider documentation over yield metadata documentation

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add 3-tab structure: All | Available to Earn | My Positions
- Auto-navigate to "Available to Earn" tab when wallet connects
- Each tab has explicit URL param for proper navigation
- Keep recommended strip visible in all tabs
- Add YieldTab enum for type safety
- Rename Earn button action to navigate to Available tab

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Fix filters not working on My Positions tab (use unfiltered data)
- Fix filters affecting Recommended For You (should be independent)
- Fix "Show all" button navigating to All tab when on Available tab
- Show full metadata.name on yield detail page below provider pill
- Hide Withdraw button when user has no balance (instead of disabled)
- Add YieldProviderInfo component with provider descriptions
- Add YieldRelatedMarkets component showing other yields for same token
- Make Yields first tab in Earn menu when feature flag enabled
- Code simplification: remove unnecessary useMemo wrappers

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Show fiat value as primary (large text) and crypto as secondary.
This matches user expectations where fiat is the more relevant metric.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add a professional two-column layout for desktop screens with info on the
left and actions on the right. Create YieldInfoCard and YieldAvailableToDeposit
components. Mobile layout remains unchanged.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Fix validator mismatch on yield detail page by passing selectedValidatorAddress
  from YieldDetail to YieldPositionCard (ensures Cosmos uses ShapeShift DAO)
- Add "New" badge support to NavigationDropdown component
- Mark Yields menu item with New badge
- Apply react-best-practices cleanup to yield components

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Create getYieldDisplayName that returns curator name for Morpho/Yearn
  vaults (e.g., "Steakhouse High Yield") and symbol for simple yields
- Integrate into YieldItem, YieldDetail, YieldInfoCard, YieldHero
- Remove useless secondary row on mobile showing ugly metadata name
- Fix Visit Website alignment in YieldProviderInfo
- Fix Lending badge from UPPERCASE to Capitalize
- Simplify components by removing unnecessary useMemo calls

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Replace cramped pill with clean icon + label pairs
- Show Asset (icon + symbol), Chain (icon + name), Protocol (icon + name)
- Apply same pattern to both desktop (YieldInfoCard) and mobile (YieldHero)
- Remove redundant subtitle section
- Clean up unused code

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
… skill

- Prefer arrow functions over function keyword in memo components
- Prefer useMemo over IIFE in TSX
- Prefer implicit returns in useMemo/useCallback
- Document avoidance of nested ternaries

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Fix dangerous fallback to 18 decimals in YieldAvailableToDeposit
- Remove unnecessary Box wrapper in YieldEnterModal
- Simplify nested ternary in YieldDetail using useMemo
- Refactor YieldExplainers with memoized translations
- Convert function keywords to arrow functions in memo components
- Use implicit returns in useMemo/useCallback
- Simplify enum mappings in YieldsList using array pattern
- Remove NEAR_IMPROVEMENTS.md

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Fixes potential runtime error when allBalances[id] is undefined,
which would cause .minus() to throw or produce NaN.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Check aggregatedAmount > 0 for hasClaimable, consistent with
hasWithdrawable check, to avoid showing empty claimable UI.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@reallybeard reallybeard requested a review from a team as a code owner January 19, 2026 19:52
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 19, 2026

📝 Walkthrough

Walkthrough

Adds translation entries, optional display props to AccountSelector, exports avatar size constants/types, tweaks button/card themes, and implements a broad refactor of the yields feature: new components (YieldAddMore, YieldCompareItem), updated page layouts (Main/PageHeader), enriched data flows (market data and balances), and various UI updates across yield components.

Changes

Cohort / File(s) Summary
Translations
src/assets/translations/en/main.json
Added three new translation keys under "overview": availableToAdd, earnUpTo, and addMore.
Account Selector
src/components/AccountSelector/AccountSelector.tsx
Added optional props showBalance?: boolean and showIcon?: boolean (both default to true); conditional rendering of icon and balance.
Avatar / Theming
src/components/Avatar/Avatar.theme.ts, src/components/LazyLoadAvatar.tsx
Exported AVATAR_SIZES const and AvatarSize type; LazyLoadAvatar now derives skeleton size from AVATAR_SIZES and forwards sizing changes.
Button / Card Theme
src/components/Button/Button.theme.ts, src/components/Card/Card.theme.ts
Button: adjust green solid variant dark-mode text and hover background; Card: elevated variant replaces borderColor with inset box-shadow.
Yields — Pages / Layout
src/pages/Yields/YieldAssetDetails.tsx, src/pages/Yields/YieldDetail.tsx, src/pages/Yields/YieldsList.tsx
Replace Container with Main/PageHeader patterns, introduce headerComponent and container padding, move header controls (back, account selector) into header area, adjust responsive layout.
Yields — New Components
src/pages/Yields/components/YieldAddMore.tsx, src/pages/Yields/components/YieldCompareItem.tsx
YieldAddMore: new memoized component showing available-to-add alert, yearly earnings, and navigation to enter modal. YieldCompareItem: new clickable card for comparing yield providers and APY.
Yields — Data Flow & UI Enhancements
src/pages/Yields/components/YieldHero.tsx, src/pages/Yields/components/YieldAvailableToDeposit.tsx, src/pages/Yields/components/YieldInfoCard.tsx
Introduced inputTokenMarketData / userBalance* props, compute available balances/fiat/potential earnings, and render position-aware UIs (different displays when user has a position).
Yields — Refactors / Prop Changes
src/pages/Yields/components/YieldPositionCard.tsx, src/pages/Yields/components/YieldProviderInfo.tsx, src/pages/Yields/components/YieldStats.tsx, src/pages/Yields/components/YieldRelatedMarkets.tsx
YieldPositionCard: simplified, removes validator/USD totals, adds inputTokenMarketData and integrates YieldAddMore. YieldProviderInfo: accepts description? (removed providerId) and uses Card layout. YieldStats: adds `variant?: 'list'

Sequence Diagram(s)

(omitted)

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • gomesalexandre
  • NeOMakinG

Poem

🐇 I hopped through code, made layouts bright,

New cards and heroes spring to light,
Balances counted, avatars gleam,
Add more earnings — chase the dream,
A little rabbit cheers: hop, deploy delight!

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'fix: yield details style polish' accurately describes the primary changes, which are visual and UI refinements across Yields components including cards, buttons, layouts, and new features like 'Add More' functionality.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch style-updates

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@reallybeard reallybeard changed the title Yield Page Style Updates fix: yield details style polish Jan 19, 2026
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 7

Caution

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

⚠️ Outside diff range comments (3)
src/components/LazyLoadAvatar.tsx (1)

9-28: Constrain size prop to non-responsive AvatarSize

AvatarProps.size supports responsive values (e.g., { base: 'sm', md: 'lg' }), but the dictionary lookup ignores them and falls back to md silently. Type-assert without validation, violating the guideline. Constrain the prop to AvatarSize (string literal union) to define clear boundaries for this wrapper component and eliminate the unvalidated assertion.

♻️ Suggested refactor
-export type LazyLoadAvatarProps = SkeletonProps &
-  Pick<AvatarProps, 'src' | 'boxSize' | 'name' | 'icon' | 'bg' | 'size'>
+export type LazyLoadAvatarProps = SkeletonProps &
+  Pick<AvatarProps, 'src' | 'boxSize' | 'name' | 'icon' | 'bg'> & {
+    size?: AvatarSize
+  }
-  const skeletonSize = useMemo(() => {
-    return AVATAR_SIZES[size as AvatarSize] ?? AVATAR_SIZES.md
-  }, [size])
+  const skeletonSize = useMemo(() => AVATAR_SIZES[size] ?? AVATAR_SIZES.md, [size])
src/pages/Yields/components/YieldAvailableToDeposit.tsx (1)

47-80: Handle zero‑decimal tokens correctly.
Line 47–51 and Line 74–80 treat precision 0 as falsy, which hides the card for 0‑decimal assets.

🔧 Proposed fix
-    const availableBalance = useMemo(
-      () =>
-        inputTokenPrecision
-          ? bnOrZero(availableBalanceBaseUnit).shiftedBy(-inputTokenPrecision)
-          : bnOrZero(0),
-      [availableBalanceBaseUnit, inputTokenPrecision],
-    )
+    const availableBalance = useMemo(() => {
+      if (inputTokenPrecision === undefined) return bnOrZero(0)
+      return bnOrZero(availableBalanceBaseUnit).shiftedBy(-inputTokenPrecision)
+    }, [availableBalanceBaseUnit, inputTokenPrecision])
@@
-    if (!inputTokenPrecision) return null
+    if (inputTokenPrecision === undefined) return null
@@
-    if (!hasAvailableBalance || hasPosition) return null
+    if (!hasAvailableBalance || hasPosition) return null
src/pages/Yields/components/YieldPositionCard.tsx (1)

132-314: Don’t hide the card when rewards are only claimable.
Line 313 returns null when hasAnyPosition is false, but hasAnyPosition excludes claimable rewards. If a user has only claimable rewards (no active/entering/exiting/withdrawable), the claim UI vanishes.

🔧 Proposed fix
-    const hasAnyPosition = totalAmount.gt(0)
+    const hasAnyPosition = totalAmount.gt(0) || hasClaimable
🤖 Fix all issues with AI agents
In `@src/lib/yieldxyz/providerDescriptions.ts`:
- Around line 1-47: The PROVIDER_DESCRIPTIONS object currently embeds hardcoded
English strings which breaks i18n rules; change its shape to export mapping
entries that reference translation keys (e.g., replace description fields with
translationKey fields such as 'providers.morpho.description') so the UI reads
text via the translation system; update any consumers that access
PROVIDER_DESCRIPTIONS to call the translator with the provided translationKey
(or resolve keys to strings at render time) and add the corresponding entries
(providers.*.description) to the locale files so all provider copy is kept in
translations.

In `@src/pages/Yields/components/YieldAddMore.tsx`:
- Around line 100-108: The UI currently hardcodes the "/yr" suffix in
YieldAddMore.tsx inside the Amount.Fiat component; replace that fixed string
with a localized translation by calling translate('yieldXYZ.perYear') and
passing the result as the suffix prop to Amount.Fiat (use the existing translate
function in this file), and add/update the corresponding translation key
(yieldXYZ.perYear) in the i18n resource so the suffix is localized and available
for all locales; ensure potentialYearlyEarningsFiat.toFixed() remains the value
prop and keep the component props (as, color, fontWeight) unchanged.
- Around line 37-65: The component treats inputTokenPrecision as a falsy flag
which breaks 0‑decimal tokens; update the checks to explicitly test for
undefined/null instead of truthiness: in the availableBalance useMemo (the
computation that depends on inputTokenPrecision), the availableBalanceFiat and
potentialYearlyEarningsFiat calculations, and the final render guard (the if
that returns null), replace the truthy checks with an explicit check such as
"inputTokenPrecision !== undefined && inputTokenPrecision !== null" or "typeof
inputTokenPrecision === 'number'" so a precision value of 0 is handled correctly
while still guarding against missing precision.
- Around line 57-62: handleEnter currently replaces the entire query string
losing existing params; change it to parse and merge the existing query (using
location.search) with { action: 'enter', modal: 'yield' } before calling
navigate (keep using qs.stringify), and add location.search to the useCallback
dependency array so navigation updates when existing params change; reference
the handleEnter function, navigate call, location.search, and qs.stringify when
making the change.

In `@src/pages/Yields/components/YieldAvailableToDeposit.tsx`:
- Around line 121-133: The Amount.Fiat component currently hardcodes the "/yr"
suffix; replace that with a localized string by calling
translate('yieldXYZ.perYear') and passing its result into the Amount.Fiat suffix
prop (in YieldAvailableToDeposit.tsx where Amount.Fiat is rendered and
potentialYearlyEarningsFiat is used). Ensure you add the new translation key
yieldXYZ.perYear to the i18n resource files so the UI displays the localized
per-year suffix.

In `@src/pages/Yields/components/YieldCompareItem.tsx`:
- Around line 52-66: The Card component is rendered as a button (Card
as='button') but lacks an explicit type, which can cause unintended form
submissions; update the Card JSX in YieldCompareItem.tsx to include
type='button' on the Card element (where as='button' is set) so clicks trigger
handleClick without submitting enclosing forms, keeping all other props
(onClick={handleClick}, cursor='pointer', etc.) unchanged.

In `@src/pages/Yields/components/YieldHero.tsx`:
- Around line 87-103: The code treats inputTokenPrecision as falsy so 0‑decimal
tokens get zeroed and multiplying by a possibly undefined reward rate yields
NaN; update the availableBalance computation to check for null/undefined (e.g.,
inputTokenPrecision != null) so precision of 0 is honored, ensure
availableBalanceFiat uses bnOrZero(inputTokenMarketData?.price) as it already
does, and compute potentialYearlyEarningsFiat using
bnOrZero(yieldItem?.rewardRate?.total) (or default 0) before multiplying (e.g.,
availableBalanceFiat.times(bnOrZero(yieldItem?.rewardRate?.total))) so missing
rates produce 0 not NaN; adjust the useMemo dependency arrays to reference the
exact values used (inputTokenPrecision and yieldItem.rewardRate.total)
accordingly.
🧹 Nitpick comments (3)
src/pages/Yields/components/YieldItem.tsx (1)

81-113: Reintroduce memoization for derived UI values

These IIFE-derived objects/JSX are recreated on every render and drop the memoization guarantees expected in this codebase. Consider restoring useMemo for these derived values (stats, iconElement, cardStatElement, mobileBalanceKey/Element, rowBalanceElement) to keep stable references and avoid unnecessary re-renders.

♻️ Suggested refactor (example for stats)
-    const stats = (() => {
+    const stats = useMemo(() => {
       if (isSingle) {
         const y = data.yieldItem
         return {
           apy: y.rewardRate.total,
           apyLabel: y.rewardRate.rateType,
           tvlUsd: y.statistics?.tvlUsd ?? '0',
           providers: [{ id: y.providerId, logo: data.providerIcon }],
           chainIds: y.chainId ? [y.chainId] : [],
           count: 1,
           name: y.metadata.name,
           canEnter: y.status.enter,
         }
       }
       const yields = data.yields
       const maxApy = Math.max(0, ...yields.map(y => y.rewardRate.total))
       const totalTvlUsd = yields
         .reduce((acc, y) => acc.plus(bnOrZero(y.statistics?.tvlUsd)), bnOrZero(0))
         .toFixed()
       const providerIds = [...new Set(yields.map(y => y.providerId))]
       const chainIds = [...new Set(yields.map(y => y.chainId).filter(Boolean))] as string[]

       return {
         apy: maxApy,
         apyLabel: 'APY',
         tvlUsd: totalTvlUsd,
         providers: providerIds.map(id => ({ id, logo: yieldProviders?.[id]?.logoURI })),
         chainIds,
         count: yields.length,
         name: data.assetName,
         canEnter: true,
       }
-    })()
+    }, [isSingle, data, yieldProviders])

As per coding guidelines, consider useMemo for derived values.

Also applies to: 137-151, 190-225, 229-233, 235-255, 257-304

src/pages/Yields/YieldAssetDetails.tsx (1)

534-545: Extract the back-navigation handler into useCallback

The inline onClick recreates a new handler when headerComponent rememoizes. Hoist it to a useCallback to keep a stable reference.

♻️ Suggested refactor
+  const handleBackClick = useCallback(() => navigate('/yields'), [navigate])
+
   const headerComponent = useMemo(
     () => (
       <>
         <Button
           leftIcon={<ArrowBackIcon />}
           variant='ghost'
-          onClick={() => navigate('/yields')}
+          onClick={handleBackClick}
           mb={6}
         >
           {translate('common.back')}
         </Button>

As per coding guidelines, event handlers passed as props should be memoized.

src/pages/Yields/components/YieldStats.tsx (1)

83-99: Consider adding a comment or type documentation clarifying whether ValidatorDto.tvl and YieldStatistics.tvl are in token units or another denomination, since the code assumes token units but this is not explicitly documented.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

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

⚠️ Outside diff range comments (2)
src/pages/Yields/components/YieldAvailableToDeposit.tsx (2)

47-53: Handle 0-decimal tokens in available balance calculation.

The truthy check on inputTokenPrecision (line 49) fails for 0-decimal tokens where decimals === 0 is falsy. This was fixed in YieldHero.tsx (line 89) but not applied here, causing inconsistent behavior.

🩹 Proposed fix
     const availableBalance = useMemo(
       () =>
-        inputTokenPrecision
+        inputTokenPrecision != null
           ? bnOrZero(availableBalanceBaseUnit).shiftedBy(-inputTokenPrecision)
           : bnOrZero(0),
       [availableBalanceBaseUnit, inputTokenPrecision],
     )

60-63: Wrap reward rate with bnOrZero to prevent NaN.

Multiplying by a potentially undefined rewardRate.total can yield NaN. This was fixed in YieldHero.tsx (line 101) but not applied here.

🩹 Proposed fix
     const potentialYearlyEarningsFiat = useMemo(
-      () => availableBalanceFiat.times(yieldItem.rewardRate.total),
+      () => availableBalanceFiat.times(bnOrZero(yieldItem.rewardRate.total)),
       [availableBalanceFiat, yieldItem.rewardRate.total],
     )
🤖 Fix all issues with AI agents
In `@src/pages/Yields/components/YieldHero.tsx`:
- Around line 327-334: The hardcoded "/yr" suffix on Amount.Fiat should be
replaced with the i18n translation key used elsewhere; update the Amount.Fiat
prop suffix to use translate('yieldXYZ.perYear') (or the same translate import
used in YieldAvailableToDeposit.tsx), ensure the translate function is imported
at top of YieldHero.tsx, and remove the hardcoded string so the suffix is fully
localized.
🧹 Nitpick comments (2)
src/pages/Yields/components/YieldCompareItem.tsx (1)

69-82: Consider using LazyLoadAvatar for the provider icon for consistency.

The chain icon overlay (lines 72-80) uses LazyLoadAvatar which provides loading skeleton and lazy loading benefits, but the main provider avatar on line 70 uses the standard Avatar. For consistency and to benefit from lazy loading for external provider icons, consider using LazyLoadAvatar here as well.

♻️ Suggested change
         <Box position='relative'>
-          <Avatar size='sm' src={providerIcon} name={displayProviderName} />
+          <LazyLoadAvatar size='sm' src={providerIcon} name={displayProviderName} />
           {chainIcon && (
src/pages/Yields/components/YieldHero.tsx (1)

291-291: Redundant condition.

This !hasPosition check is inside the else branch (line 285) where hasPosition is already false, making the condition always true.

♻️ Suggested simplification
-              {!hasPosition && descriptionSection}
+              {descriptionSection}

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@src/pages/Yields/components/YieldHero.tsx`:
- Around line 67-107: YieldHero is using the portfolio-wide balance because
selectPortfolioCryptoBalanceBaseUnitByFilter is called with only { assetId:
inputTokenAssetId }; remove the inline comment near the top, add an accountId
prop to the YieldHero component (e.g. accept accountId: string | undefined) and
pass it into the selector filter like
selectPortfolioCryptoBalanceBaseUnitByFilter(state, { accountId, assetId:
inputTokenAssetId }); ensure the prop name matches uses in parent components
(YieldDetail via useYieldAccountSync) so the selectedAccountId is forwarded into
YieldHero.
🧹 Nitpick comments (1)
src/pages/Yields/components/YieldHero.tsx (1)

286-349: Preserve fiat precision before formatting (optional).

toFixed() defaults to 0 decimals, which may truncate cents. Consider passing string values via .toString() (or specify decimals) so Amount.Fiat can format with locale rules.

♻️ Optional refinement
-                        <Amount.Fiat value={availableBalanceFiat.toFixed()} />
+                        <Amount.Fiat value={availableBalanceFiat.toString()} />
@@
-                          value={potentialYearlyEarningsFiat.toFixed()}
+                          value={potentialYearlyEarningsFiat.toString()}

Based on learnings, prefer .toString() for BigNumber-derived values before passing to Amount.* components.

Comment on lines 67 to 107
const iconSource = resolveYieldInputAssetIcon(yieldItem)
const apy = bnOrZero(yieldItem.rewardRate.total).times(100).toFixed(2)
const hasExitBalance = bnOrZero(userBalanceCrypto).gt(0)
const hasPosition = bnOrZero(userBalanceCrypto).gt(0)

// Available to deposit logic
const inputToken = yieldItem.inputTokens[0]
const inputTokenAssetId = inputToken?.assetId ?? ''
const inputTokenPrecision = inputToken?.decimals

const assetIcon = useMemo(() => {
if (iconSource.assetId) {
return <AssetIcon assetId={iconSource.assetId} size='2xl' />
}
return <AssetIcon src={iconSource.src} size='2xl' />
}, [iconSource])

const availableBalanceBaseUnit = useAppSelector(state =>
selectPortfolioCryptoBalanceBaseUnitByFilter(state, { assetId: inputTokenAssetId }),
)

const availableBalance = useMemo(
() =>
inputTokenPrecision != null
? bnOrZero(availableBalanceBaseUnit).shiftedBy(-inputTokenPrecision)
: bnOrZero(0),
[availableBalanceBaseUnit, inputTokenPrecision],
)

const availableBalanceFiat = useMemo(
() => availableBalance.times(bnOrZero(inputTokenMarketData?.price)),
[availableBalance, inputTokenMarketData?.price],
)

const potentialYearlyEarningsFiat = useMemo(
() => availableBalanceFiat.times(bnOrZero(yieldItem?.rewardRate?.total)),
[availableBalanceFiat, yieldItem?.rewardRate?.total],
)

const hasAvailableBalance = availableBalance.gt(0)
const showAvailableToDeposit = !hasPosition && hasAvailableBalance

Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# Check if YieldDetail passes accountId to YieldHero
rg -n "YieldHero" src/pages/Yields/YieldDetail.tsx -A 5 -B 5

# Also check for account-related variables in YieldDetail
rg -n "accountId|selectedAccountId|accountIdParam|AccountSelector" src/pages/Yields/YieldDetail.tsx -A 2 -B 2

Repository: shapeshift/web

Length of output: 3386


🏁 Script executed:

# Inspect the selector signature to understand its parameters
rg -n "selectPortfolioCryptoBalanceBaseUnitByFilter" src/state/slices/portfolioSlice/selectors.ts -A 10 -B 2

Repository: shapeshift/web

Length of output: 40


🏁 Script executed:

# Check the full YieldHero component file to understand its props
head -150 src/pages/Yields/components/YieldHero.tsx

Repository: shapeshift/web

Length of output: 5014


🏁 Script executed:

# Search for the selector definition in selectors files
find src/state -name "*selector*" -type f | head -20

Repository: shapeshift/web

Length of output: 898


🏁 Script executed:

# Search for selectPortfolioCryptoBalanceBaseUnitByFilter across the codebase
rg "selectPortfolioCryptoBalanceBaseUnitByFilter" --type ts --type tsx -A 5 -B 2

Repository: shapeshift/web

Length of output: 85


🏁 Script executed:

# Check if there's an account-scoped variant of the balance selector
rg "selectPortfolioBalance.*Filter\|selectCryptoBalance.*accountId" src/state/slices/portfolioSlice/selectors.ts -A 3

Repository: shapeshift/web

Length of output: 40


🏁 Script executed:

# Read the portfolio selectors file to find the function definition
rg -n "selectPortfolioCryptoBalanceBaseUnitByFilter" src/state/slices/portfolioSlice/selectors.ts -A 10

Repository: shapeshift/web

Length of output: 40


🏁 Script executed:

# Also check the main selectors export file
grep -n "selectPortfolioCryptoBalanceBaseUnitByFilter" src/state/slices/selectors.ts

Repository: shapeshift/web

Length of output: 40


🏁 Script executed:

# List contents of portfolioSlice directory
ls -la src/state/slices/portfolioSlice/

Repository: shapeshift/web

Length of output: 577


🏁 Script executed:

# Read the selectors.ts file to find the function
grep -n "selectPortfolioCryptoBalanceBaseUnitByFilter" src/state/slices/portfolioSlice/selectors.ts

Repository: shapeshift/web

Length of output: 40


🏁 Script executed:

# If it's not found there, check if it's exported from src/state/slices/selectors.ts
grep -n "selectPortfolioCryptoBalanceBaseUnitByFilter" src/state/slices/selectors.ts

Repository: shapeshift/web

Length of output: 40


🏁 Script executed:

# Search the entire codebase for where this selector is defined
rg "selectPortfolioCryptoBalanceBaseUnitByFilter\s*=" --type ts

Repository: shapeshift/web

Length of output: 176


🏁 Script executed:

# Read the common-selectors.ts file to understand the selector definition
grep -n "selectPortfolioCryptoBalanceBaseUnitByFilter" src/state/slices/common-selectors.ts -A 20 -B 2

Repository: shapeshift/web

Length of output: 1189


🏁 Script executed:

# Also check if there's an account-scoped version available
grep -n "selectPortfolioBalance\|selectCryptoBalance" src/state/slices/common-selectors.ts | head -30

Repository: shapeshift/web

Length of output: 40


Account-scope the available balance and remove the inline comment.

The selector selectPortfolioCryptoBalanceBaseUnitByFilter supports account scoping via an accountId parameter in its filter object. When accountId is provided, it returns the account-specific balance; without it, it falls back to the portfolio-wide balance. Currently, YieldHero passes only the assetId, which causes line 81 to display the portfolio-wide balance instead of the selected account's balance. YieldDetail already provides selectedAccountId (via useYieldAccountSync), but it's not passed to YieldHero—this needs to be fixed.

Also, remove the inline comment on line 71 per coding guidelines.

Proposed cleanup
-    // Available to deposit logic

To fix the account scoping, YieldHero needs to receive accountId as a prop and pass it to the selector's filter object:

selectPortfolioCryptoBalanceBaseUnitByFilter(state, { accountId, assetId: inputTokenAssetId })
🤖 Prompt for AI Agents
In `@src/pages/Yields/components/YieldHero.tsx` around lines 67 - 107, YieldHero
is using the portfolio-wide balance because
selectPortfolioCryptoBalanceBaseUnitByFilter is called with only { assetId:
inputTokenAssetId }; remove the inline comment near the top, add an accountId
prop to the YieldHero component (e.g. accept accountId: string | undefined) and
pass it into the selector filter like
selectPortfolioCryptoBalanceBaseUnitByFilter(state, { accountId, assetId:
inputTokenAssetId }); ensure the prop name matches uses in parent components
(YieldDetail via useYieldAccountSync) so the selectedAccountId is forwarded into
YieldHero.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

Caution

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

⚠️ Outside diff range comments (2)
src/pages/Yields/components/YieldProviderInfo.tsx (1)

19-28: Unused providerId prop in type definition.

The providerId property is defined in YieldProviderInfoProps (line 20) but is not destructured or used anywhere in the component (line 28). This creates dead code since callers pass this prop but it has no effect.

🧹 Proposed fix to remove unused prop
 type YieldProviderInfoProps = {
-  providerId: string
   providerName: string
   providerLogoURI?: string
   providerWebsite?: string
   description?: string
 }

 export const YieldProviderInfo = memo(
-  ({ providerName, providerLogoURI, providerWebsite, description }: YieldProviderInfoProps) => {
+  ({ providerName, providerLogoURI, providerWebsite, description }: YieldProviderInfoProps) => {

Then update callers in YieldDetail.tsx to remove the providerId prop:

             <YieldProviderInfo
-              providerId={yieldItem.providerId}
               providerName={validatorOrProvider.name}
src/pages/Yields/YieldDetail.tsx (1)

291-298: Missing description prop in mobile layout.

The mobile YieldProviderInfo does not receive the description prop, while the desktop version (line 323) does pass description={validatorOrProvider.description}. This creates an inconsistent experience where mobile users won't see the provider description.

🐛 Proposed fix to add missing prop
             <YieldProviderInfo
               providerId={yieldItem.providerId}
               providerName={validatorOrProvider.name}
               providerLogoURI={validatorOrProvider.logoURI}
               providerWebsite={validatorOrProvider.documentation}
+              description={validatorOrProvider.description}
             />
🧹 Nitpick comments (1)
src/pages/Yields/YieldDetail.tsx (1)

262-262: Consider extracting static responsive value outside the component.

The containerPaddingX is a static object with no dependencies. Per coding guidelines, static values can be defined as constants outside the component to avoid unnecessary memoization overhead.

♻️ Optional refactor
+const containerPaddingX = { base: 4, xl: 0 }
+
 export const YieldDetail = memo(() => {
   // ...
-  const containerPaddingX = useMemo(() => ({ base: 4, xl: 0 }), [])

Comment on lines -333 to -341
const addressBadge = useMemo(() => {
if (!address) return null
return (
<Badge variant='subtle' colorScheme='blue' borderRadius='full' px={2} py={0.5}>
{addressBadgeText}
</Badge>
)
}, [address, addressBadgeText])

Copy link
Contributor

Choose a reason for hiding this comment

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

Is this an intended change? Personally, that badge helps me to identify the address and I find it useful as a user

])

if (!accountId) return null
if (!isBalancesLoading && !hasAnyPosition) return null
Copy link
Contributor

Choose a reason for hiding this comment

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

q: is this an intended change? This may produce flashes, also, it removes the changes introduced in #11703 re: showing out position as zero'd out

This will now produce a layout shift from no/wallet state vs. zero'd out state before.

Image

Also, this produces an inconsistent layout for positions without balance.

Image

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I was thinking we would show either of them, but not at the same time. So I can adjust this logic if we want to show them buttons they can't interact with.

Copy link
Contributor

@gomesalexandre gomesalexandre left a comment

Choose a reason for hiding this comment

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

While yield parts has some sexy improvements, it also has regressions (missing position data e.g unstakings, claims etc not available on mobile now), regressed bits like zero'd out position without a wallet, inconsistent layout for balance/apy depending on no/position.

Most importantly, the app is in a fully broken state now. If going with a fully vibe-coded PR, at the very least sanity check the changeset and make Claude itself do some review pass and test the changes according to the surface area diffed.

Tbh just rage quitted commenting the code and went for functional testing - pls salvage actual yield visual changes from this, be mindful about global theme changes and yield logic/visual regressions introduced and test them well ty

Spotted issues:

  • All app buttons are now broken in light mode

https://jam.dev/c/e9f2f354-eec9-4e24-9d11-f7a3c72eecef

  • We broke cards in the app across the board
Image
  • We broke avatars in the app across the board
Image Image
  • We lost section with unstaking, rewards etc on mobile, see develop vs this diff - how to see my unstaking and claims my rewards on mobile now?
Image Image
  • Also lost description of yield on mobile (see screenshot above, the yield-specific description)

  • Asset icons looking pixelated on mobile w/ this new bigger icons

Image
  • While it looks a lot better for some assets with more info, looks a bit empty for others now i.e seemed to fill a teeny tiny bit more space before
Image Image image image
  • Not all yields looking the same re: APY/balance depending on active, is this visual discrepancy expected?
Image Image
  • Inconsistent use of APY green badge vs. gradient apy (directly related to the discrepancy above)
Image Image
  • Available to deposit doesn't display a fiat value anymore
Image Image
  • Double provider reference, once in subheading, then in tags, is this expected?
Image
  • Yield symbol list is now visually broken on desktop and mobile alike
Image Image
  • Personal taste but I find things way less readable and when stretched more to take full screen width with 3 col layout, see this diff vs. develop
Image Image
  • The new related yields list view doesn't show TVL for comparison
Image Image
  • Not sure if intentional, but the new related yields list does not display the parsed name with Curator
Image Image
  • On a tangent, personally not fan of a list view for related at the very least on desktop as related cards of sorts is a nice familiar UX, a list doesn't feel very "related" kind of UX - for mobile, it is currently list-y already and saves a bit more space with the new way so why not

  • We're displaying way too much precision in the new available to earn in yield details page

Image Image

Copy link
Contributor

Choose a reason for hiding this comment

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

u sure?

Image

size={size}
icon={icon}
boxSize={boxSize}
boxSize='100%'
Copy link
Contributor

Choose a reason for hiding this comment

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

is this expected?

Comment on lines +47 to +48
boxShadow:
'0 1px 0 var(--chakra-colors-border-base) inset, 0 0 0 1px var(--chakra-colors-border-base) inset',
Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah also not sure about this one

Image

Comment on lines +39 to +41
typeof inputTokenPrecision === 'number'
? bnOrZero(availableBalanceBaseUnit).shiftedBy(-inputTokenPrecision)
: bnOrZero(0),
Copy link
Contributor

Choose a reason for hiding this comment

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

?

Copy link
Contributor

Choose a reason for hiding this comment

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

This doesn't display on mobile, is this expected?

Image

[translate],
)

const containerPaddingX = useMemo(() => ({ base: 4, xl: 0 }), [])
Copy link
Contributor

Choose a reason for hiding this comment

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

nitpick but this doesn't need to be a useMemo'd constant and also conventional px ?

Copy link
Contributor

Choose a reason for hiding this comment

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

Why was this file diffed so much?

[isStaking, selectedValidator],
)

if (variant === 'list') {
Copy link
Contributor

Choose a reason for hiding this comment

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

This is used only in one place, not sure we need the variant complexity for only one usage.

.sort((a, b) => b.potentialEarnings.minus(a.potentialEarnings).toNumber())
.slice(0, 3)
}, [unfilteredAvailableYields, userCurrencyBalances])
}, [isConnected, yields?.unfiltered, userCurrencyBalances, assetBalancesBaseUnit])
Copy link
Contributor

Choose a reason for hiding this comment

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

q: why the exhaustive assetBalancesBaseUnit dep if unused?

}
return validatorParam || defaultValidator
}, [yieldItem.id, validatorParam, defaultValidator])
}, [yieldItem.id, defaultValidator, validatorParam])
Copy link
Contributor

Choose a reason for hiding this comment

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

q: why did the order changes if those are the same as before?

@gomesalexandre
Copy link
Contributor

@reallybeard closing to not make you spend more time on this one, I don't think this PR is in a salvage-able state so no point to tackle.

If you're confident in reopening a PR with the few yield style chunks only (with all the comments above taken into consideration), will take a look, else I'll salvage said style bits tomo.

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.

3 participants