Skip to content
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
8f7e626
feat: prevent re-access to /earn/confirm after yield transaction comp…
gomesalexandre Jan 26, 2026
c27a523
fix: correct yield routing from /yields/ to /yield/
gomesalexandre Jan 26, 2026
530d6b5
fix: eliminate dead space in yield success step footer
gomesalexandre Jan 26, 2026
e2e5cc4
fix: style fiat zero amount as placeholder in yield modals
gomesalexandre Jan 26, 2026
01fe19b
fix: filter non-default validator positions at data layer
gomesalexandre Jan 26, 2026
36a34a3
fix: show provider row for liquid staking yields
gomesalexandre Jan 26, 2026
bb00234
chore: add PR template rule to CLAUDE.md
gomesalexandre Jan 26, 2026
1df6077
fix: add spinner loading state for legacy positions in DeFi earn
gomesalexandre Jan 27, 2026
5284013
fix: select best actionable yield for asset page CTA
gomesalexandre Jan 27, 2026
c4bb8b8
fix: aggregate yield balances across all accounts in drawer
gomesalexandre Jan 27, 2026
0be3258
refactor: yield domain code improvements
gomesalexandre Jan 27, 2026
99c4280
fix: use inputToken symbol for deposit-related displays
gomesalexandre Jan 27, 2026
808a613
fix: add Claim button for withdrawable balances with CLAIM_UNSTAKED a…
gomesalexandre Jan 27, 2026
c066157
fix: show action-relevant info in exit/withdraw modal
gomesalexandre Jan 27, 2026
45836a2
fix: disable unstake button when no active balance to unstake
gomesalexandre Jan 27, 2026
98da651
merge: resolve conflict with origin/develop in yieldxyz utils
gomesalexandre Jan 27, 2026
defb87a
Merge branch 'develop' into feat_yield_full_toggle_3
gomesalexandre Jan 28, 2026
281082d
fix: use bnOrZero to select effective claim balance and add input tok…
gomesalexandre Jan 28, 2026
149e530
Merge remote-tracking branch 'origin/develop' into feat_yield_full_to…
gomesalexandre Jan 28, 2026
e99bd15
Merge branch 'develop' into feat_yield_full_toggle_3
gomesalexandre Jan 28, 2026
4c16ec0
Merge branch 'feat_yield_full_toggle_3' into feat_yield_unstaking_polish
gomesalexandre Jan 28, 2026
b2344fb
Merge remote-tracking branch 'origin/develop' into feat_yield_unstaki…
NeOMakinG Feb 10, 2026
15b4393
Merge branch 'develop' into feat_yield_unstaking_polish
gomesalexandre Feb 10, 2026
8527a2e
chore: trigger CI
gomesalexandre Feb 10, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@
- When creating commits, follow the Git Safety Protocol (see session notes)
- Main branch is `develop` - use this for PRs
- Branch naming: Use descriptive names (e.g., `feat_gridplus`, `fix_wallet_connect`)
- When opening PRs (via `gh`, Aviator `av`, or any CLI tool), ALWAYS use the `.github/PULL_REQUEST_TEMPLATE.md` template as the base for the PR body

### UI/UX Standards
- Account for light/dark mode using `useColorModeValue` hook
Expand Down
5 changes: 4 additions & 1 deletion src/assets/translations/en/main.json
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,7 @@
"balance": "Balance",
"netWorth": "Net Worth",
"loadingAccounts": "Loaded %{portfolioAccountsLoaded} accounts",
"loadingMorePositions": "More DeFi positions are still loading",
"walletBalanceChange24Hr": "24-hour change",
"earnBody": "Earn passive income by staking your assets or depositing them into a DeFi strategy.",
"noAccountsOpportunities": "You have no accounts for this asset, so staking opportunities are currently unavailable.",
Expand Down Expand Up @@ -2801,6 +2802,7 @@
"depositsDisabledDescription": "Deposits are temporarily unavailable for this yield opportunity.",
"withdrawalsDisabled": "Withdrawals Disabled",
"withdrawalsDisabledDescription": "Withdrawals are temporarily unavailable for this yield opportunity.",
"noActiveBalanceToExit": "No active balance available to unstake.",
"noAvailableYields": "No yield opportunities available for your assets",
"connectWalletAvailable": "Connect a wallet to see yields available for your assets",
"aboutProvider": "About %{provider}",
Expand Down Expand Up @@ -2829,7 +2831,8 @@
"potentialEarningsAmount": "%{amount}/yr at %{apy}% APY",
"depositNow": "Deposit Now",
"strategyInfo": "Strategy Info",
"overview": "Overview"
"overview": "Overview",
"readyToClaim": "Ready to claim"
},
"earn": {
"enterFrom": "Enter from",
Expand Down
44 changes: 44 additions & 0 deletions src/components/CryptoAmountInput/CryptoAmountInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { Input } from '@chakra-ui/react'
import type { ChangeEvent } from 'react'
import { memo, useMemo } from 'react'

const INPUT_LENGTH_BREAKPOINTS = {
FOR_XS_FONT: 22,
FOR_SM_FONT: 14,
FOR_MD_FONT: 10,
} as const

const getInputFontSize = (length: number): string => {
if (length >= INPUT_LENGTH_BREAKPOINTS.FOR_XS_FONT) return '24px'
if (length >= INPUT_LENGTH_BREAKPOINTS.FOR_SM_FONT) return '30px'
if (length >= INPUT_LENGTH_BREAKPOINTS.FOR_MD_FONT) return '38px'
return '48px'
}

export type CryptoAmountInputProps = {
value?: string
onChange?: (e: ChangeEvent<HTMLInputElement>) => void
placeholder?: string
[key: string]: unknown
}

export const CryptoAmountInput = memo((props: CryptoAmountInputProps) => {
const valueLength = useMemo(() => (props.value ? String(props.value).length : 0), [props.value])
const fontSize = useMemo(() => getInputFontSize(valueLength), [valueLength])

return (
<Input
size='lg'
fontSize={fontSize}
lineHeight={fontSize}
fontWeight='medium'
textAlign='center'
border='none'
borderRadius='lg'
bg='transparent'
variant='unstyled'
color={props.value ? 'text.base' : 'text.subtle'}
{...props}
/>
)
})
85 changes: 66 additions & 19 deletions src/components/MultiHopTrade/components/Earn/EarnConfirm.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Avatar, Box, Button, Flex, HStack, Skeleton, Text, VStack } from '@chakra-ui/react'
import { memo, useCallback, useEffect, useMemo } from 'react'
import { memo, useCallback, useEffect, useMemo, useRef } from 'react'
import { useTranslate } from 'react-polyglot'
import { useNavigate } from 'react-router-dom'

Expand Down Expand Up @@ -33,11 +33,13 @@ import {
selectSelectedYieldId,
selectSellAccountId,
} from '@/state/slices/tradeEarnInputSlice/selectors'
import { useAppSelector } from '@/state/store'
import { tradeEarnInput } from '@/state/slices/tradeEarnInputSlice/tradeEarnInputSlice'
import { useAppDispatch, useAppSelector } from '@/state/store'

export const EarnConfirm = memo(() => {
const translate = useTranslate()
const navigate = useNavigate()
const dispatch = useAppDispatch()

const sellAsset = useAppSelector(selectInputSellAsset)
const sellAmountCryptoPrecision = useAppSelector(selectInputSellAmountCryptoPrecision)
Expand Down Expand Up @@ -141,6 +143,19 @@ export const EarnConfirm = memo(() => {
accountId: accountIdToUse,
})

// Track step in ref for cleanup
const stepRef = useRef(step)
stepRef.current = step

// Clear Redux when unmounting from success state to prevent re-access
useEffect(() => {
return () => {
if (stepRef.current === ModalStep.Success) {
dispatch(tradeEarnInput.actions.clear())
}
}
}, [dispatch])

// Align loading states with YieldEnterModal
const isQuoteActive = isQuoteLoading || isAllowanceCheckPending
const isLoading = isLoadingYields || isQuoteActive
Expand Down Expand Up @@ -193,21 +208,13 @@ export const EarnConfirm = memo(() => {
return null
}, [selectedValidator, selectedYield, providers])

if (!selectedYield) {
return (
<SharedConfirm
bodyContent={
<VStack spacing={4} p={6} flex={1} justify='center'>
<Text>{translate('earn.selectYieldOpportunity')}</Text>
<Button onClick={handleBack}>{translate('common.goBack')}</Button>
</VStack>
}
footerContent={null}
onBack={handleBack}
headerTranslation='earn.confirmEarn'
/>
)
}
const handleViewPosition = useCallback(() => {
if (!selectedYieldId) return
const params = new URLSearchParams()
if (accountIdToUse) params.set('accountId', accountIdToUse)
const queryString = params.toString()
navigate(queryString ? `/yield/${selectedYieldId}?${queryString}` : `/yield/${selectedYieldId}`)
}, [selectedYieldId, accountIdToUse, navigate])

if (step === ModalStep.Success) {
return (
Expand All @@ -221,16 +228,52 @@ export const EarnConfirm = memo(() => {
transactionSteps={transactionSteps}
yieldId={selectedYieldId}
onDone={handleBack}
showButtons={false}
/>
</Flex>
}
footerContent={null}
footerContent={
<Box p={4}>
<VStack spacing={3} width='full'>
{selectedYieldId && (
<Button colorScheme='blue' size='lg' width='full' onClick={handleViewPosition}>
{translate('yieldXYZ.viewPosition')}
</Button>
)}
<Button
variant={selectedYieldId ? 'ghost' : 'solid'}
colorScheme={selectedYieldId ? undefined : 'blue'}
size='lg'
width='full'
onClick={handleBack}
>
{translate('common.close')}
</Button>
</VStack>
</Box>
}
onBack={handleBack}
headerTranslation='yieldXYZ.success'
/>
)
}

if (!selectedYield) {
return (
<SharedConfirm
bodyContent={
<VStack spacing={4} p={6} flex={1} justify='center'>
<Text>{translate('earn.selectYieldOpportunity')}</Text>
<Button onClick={handleBack}>{translate('common.goBack')}</Button>
</VStack>
}
footerContent={null}
onBack={handleBack}
headerTranslation='earn.confirmEarn'
/>
)
}

const bodyContent = (
<Flex direction='column' width='full' flex={1}>
<Box px={6}>
Expand Down Expand Up @@ -324,7 +367,11 @@ export const EarnConfirm = memo(() => {

{selectedYield && (
<Box mt={4}>
<YieldExplainers selectedYield={selectedYield} sellAssetSymbol={sellAsset?.symbol} />
<YieldExplainers
selectedYield={selectedYield}
sellAssetSymbol={sellAsset?.symbol}
action='enter'
/>
</Box>
)}

Expand Down
Loading