fix(token-details): cp-7.81.0 show swap/quick buy when only viewed token held#31393
Conversation
The Swap button and QuickBuy flash icon were gated on selectHasEligibleSwapSource called with the viewed token excluded, so a user whose only funded asset was the token on screen saw neither, even though the swap handler can swap the held token away and QuickBuy's sell mode works on it. Call the selector with no exclusion args so the viewed token counts. The optional excludedChainId/excludedAddress params are kept for future reuse. Co-authored-by: Cursor <cursoragent@cursor.com>
|
CLA Signature Action: All authors have signed the CLA. You may need to manually re-run the blocking PR check if it doesn't pass in a few minutes. |
| sourcePage?: string; | ||
| }) => { | ||
| const hasEligibleSwapTokens = useSelector((state: RootState) => | ||
| selectHasEligibleSwapSource(state, token.chainId, token.address), |
There was a problem hiding this comment.
Are we sure dropping the token from this selector is intentional?
There was a problem hiding this comment.
Yes it is, it's actually the core of the fix.
Passing the token.chain and token.address was excluding it from the eligible list, causing the swap button to not appear when I'm viewing it, even when I'm'owning a non-null amount of it
There was a problem hiding this comment.
Okay tested, I think this is a good change
- before - we checked all possible tokens, excluding current token
- now - we always just check if you at least have a token eligable (including current token).
What is nice is the underlying token actions for swap navigation will select the selected token as "source" token, in case you want an easy way to swap out of the token to something else.
| export const selectHasEligibleSwapSource = createSelector( | ||
| [ | ||
| selectAssetsBySelectedAccountGroup, | ||
| (_state: RootState, excludedChainId: string | undefined) => excludedChainId, | ||
| ( | ||
| _state: RootState, | ||
| _excludedChainId: string | undefined, | ||
| excludedAddress: string | undefined, | ||
| ) => excludedAddress, | ||
| (_state: RootState, excludedChainId?: string) => excludedChainId, | ||
| (_state: RootState, _excludedChainId?: string, excludedAddress?: string) => | ||
| excludedAddress, |
There was a problem hiding this comment.
Quick audit, there are no other places that use this.
Lets clean up - remove the params and keep it simple :)
There was a problem hiding this comment.
Noted, will do. I wasn't sure whether or not to remove it.
…t coverage This update refactors the `selectHasEligibleSwapSource` selector to streamline its logic by removing unnecessary parameters and consolidating test cases. The selector now directly checks for positive fiat balances without exclusion parameters, ensuring that the currently-viewed token is counted as a valid swap source. Additionally, the test suite has been enhanced to cover more scenarios, improving overall test coverage and clarity. Co-authored-by: Cursor <cursoragent@cursor.com>
🔍 Smart E2E Test Selection
click to see 🤖 AI reasoning detailsE2E Test Selection: Key impact areas:
The changes are contained to the selector and hook layer, with no changes to UI components themselves. The test files confirm the behavioral change is intentional (the currently-viewed token is now a valid swap source). Risk is medium because this changes user-visible behavior (swap button visibility) in token detail screens. Performance Test Selection: |
Description
On the Token Details screen, the Swap button and the QuickBuy (lightning/flash) icon were both gated on
selectHasEligibleSwapSource, which the footer called with the currently-viewed token excluded. As a result, a user whose only funded asset was the token they were viewing (e.g. holding only ETH, on the ETH page) saw neither control, and only the fiat Buy fallback was shown.This is incorrect: the swap handler (
useHandleOnSwap) already swaps the held token away when the user holds it, and QuickBuy's sell mode operates on the position token. So holding only the viewed token is a perfectly valid reason to surface Swap and QuickBuy.The fix calls the selector with no exclusion args in
useStickyTokenActions, so the viewed token counts as a fundable/swappable asset. The selector's optionalexcludedChainId/excludedAddressparameters are intentionally retained for any future caller that genuinely needs "any funded asset except X".Behavioral delta: for a held token that is not fiat-buyable and with no other holdings, the Buy on-ramp fallback no longer shows; Swap is shown instead. For buyable tokens (e.g. ETH) both still show.
Changelog
CHANGELOG entry: Fixed the Swap and Quick Buy buttons not appearing on the token details screen when the only funded asset was the token being viewed.
Related issues
Fixes: Issue where the Swap button wouldn't show, preventing me from swapping a token I actually hold.
Manual testing steps
Screenshots/Recordings
Before
After
Pre-merge author checklist
Performance checks (if applicable)
trace()for usage andaddTokenfor an exampleFor performance guidelines and tooling, see the Performance Guide.
Pre-merge reviewer checklist
Note
Low Risk
Localized change to swap-eligibility gating and footer visibility on Token Details; no auth, payments, or data-layer changes.
Overview
Fixes Token Details Swap and Quick Buy staying hidden when the user’s only funded asset is the token on screen.
useStickyTokenActionsno longer passes the viewed token intoselectHasEligibleSwapSourceas an exclusion.selectHasEligibleSwapSourceis simplified to a state-only check: any asset with positive fiat balance counts, including the current token, since that balance is a valid swap source.Tests now assert the hook uses the selector without exclusions and that a single funded “viewed” token yields eligibility true. For wallets with only one non–fiat-buyable holding, the footer may show Swap instead of the Buy on-ramp fallback when swap eligibility is true.
Reviewed by Cursor Bugbot for commit 5722fd1. Bugbot is set up for automated code reviews on this repo. Configure here.