Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: swap max for non native assets #5923

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from 6 commits
Commits
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
4 changes: 2 additions & 2 deletions src/components/DeFi/components/AssetInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export type AssetInputProps = {
assetIcon: string
onChange?: (value: string, isFiat?: boolean) => void
onAssetClick?: () => void
onMaxClick?: () => Promise<void>
onMaxClick?: () => void
onPercentOptionClick?: (args: number) => void
isReadOnly?: boolean
isSendMaxDisabled?: boolean
Expand Down Expand Up @@ -217,7 +217,7 @@ export const AssetInput: React.FC<AssetInputProps> = ({
options={percentOptions}
isDisabled={isReadOnly || isSendMaxDisabled}
onMaxClick={onMaxClick}
onClick={onPercentOptionClick}
onPercentOptionClick={onPercentOptionClick}
herbig marked this conversation as resolved.
Show resolved Hide resolved
/>
)}
</Stack>
Expand Down
24 changes: 14 additions & 10 deletions src/components/DeFi/components/PercentOptionsButtonGroup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import { Amount } from 'components/Amount/Amount'

type MaxButtonProps = {
isDisabled?: boolean
onClick: (args: number) => void
onMaxClick?: () => Promise<void>
onPercentOptionClick?: (args: number) => void
herbig marked this conversation as resolved.
Show resolved Hide resolved
onMaxClick?: () => void
option: number
value?: number | null
}
Expand All @@ -18,15 +18,19 @@ const percentFormatOptions = {

export const PercentOptionsButton: React.FC<MaxButtonProps> = ({
isDisabled,
onClick,
onPercentOptionClick,
onMaxClick,
option,
value,
}) => {
const translate = useTranslate()
const handleClick = useCallback(async () => {
onMaxClick && option === 1 ? await onMaxClick() : onClick(option)
}, [onClick, onMaxClick, option])
const handleClick = useCallback(() => {
if (onMaxClick && option === 1) {
onMaxClick()
} else if (onPercentOptionClick) {
onPercentOptionClick(option)
}
}, [onPercentOptionClick, onMaxClick, option])
Copy link
Author

Choose a reason for hiding this comment

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

changed the logic slightly, since onPercentOptionClick is now optional

Copy link
Contributor

Choose a reason for hiding this comment

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

Since both are now optional, we could end up in a situation where we forget to pass onMaxClick, and the regular onClick handler gets fired, which could have very different logic as the max one


return (
<Button isDisabled={isDisabled} isActive={option === value} key={option} onClick={handleClick}>
Expand All @@ -41,15 +45,15 @@ export const PercentOptionsButton: React.FC<MaxButtonProps> = ({

type MaxButtonGroupProps = {
isDisabled?: boolean
onClick: (args: number) => void
onMaxClick?: () => Promise<void>
onPercentOptionClick?: (args: number) => void
Copy link
Contributor

Choose a reason for hiding this comment

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

Since this is a shared component, we absolutely want a discriminated union here - one or the other should always be present: if we ever get to rendering this component, we should have an event handler for it.

onMaxClick?: () => void
options: number[]
value?: number | null
}

export const PercentOptionsButtonGroup: React.FC<MaxButtonGroupProps> = ({
isDisabled,
onClick,
onPercentOptionClick,
onMaxClick,
options,
value,
Expand All @@ -62,7 +66,7 @@ export const PercentOptionsButtonGroup: React.FC<MaxButtonGroupProps> = ({
value={value}
option={option}
key={option}
onClick={onClick}
onPercentOptionClick={onPercentOptionClick}
onMaxClick={onMaxClick}
/>
))}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export type TradeAmountInputProps = {
assetSymbol: string
assetIcon: string
onChange?: (value: string, isFiat?: boolean) => void
onMaxClick?: () => Promise<void>
onMaxClick?: () => void
onPercentOptionClick?: (args: number) => void
onAccountIdChange: AccountDropdownProps['onChange']
isReadOnly?: boolean
Expand Down Expand Up @@ -269,12 +269,12 @@ export const TradeAmountInput: React.FC<TradeAmountInputProps> = memo(
<Skeleton isLoaded={!showFiatSkeleton}>{oppositeCurrency}</Skeleton>
</Button>
)}
{onPercentOptionClick && (
{(onMaxClick || onPercentOptionClick) && (
<PercentOptionsButtonGroup
options={percentOptions}
isDisabled={isReadOnly || isSendMaxDisabled}
onMaxClick={onMaxClick}
onClick={onPercentOptionClick}
onPercentOptionClick={onPercentOptionClick}
/>
)}
</Flex>
Expand Down
24 changes: 21 additions & 3 deletions src/components/MultiHopTrade/components/TradeAssetInput.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { Skeleton, SkeletonCircle, Stack, useColorModeValue } from '@chakra-ui/react'
import type { AssetId } from '@shapeshiftoss/caip'
import React, { memo, useMemo } from 'react'
import { type AssetId, fromAssetId } from '@shapeshiftoss/caip'
import { isEvmChainId } from '@shapeshiftoss/chain-adapters'
import React, { memo, useCallback, useMemo } from 'react'
import type { TradeAmountInputProps } from 'components/MultiHopTrade/components/TradeAmountInput'
import { TradeAmountInput } from 'components/MultiHopTrade/components/TradeAmountInput'
import { bnOrZero } from 'lib/bignumber/bignumber'
import { isNativeEvmAsset } from 'lib/swapper/swappers/utils/helpers/helpers'
import {
selectMarketDataById,
selectPortfolioCryptoPrecisionBalanceByFilter,
Expand Down Expand Up @@ -46,7 +48,23 @@ const AssetInputWithAsset: React.FC<AssetInputLoadedProps> = props => {
)
const fiatBalance = bnOrZero(balance).times(marketData.price).toString()

return <TradeAmountInput balance={balance} fiatBalance={fiatBalance} {...props} />
const onMaxClick = useCallback(() => {
if (props.onChange) props.onChange(balance, false)
}, [balance, props])

const shouldShowMax = useMemo(
() => isEvmChainId(fromAssetId(assetId).chainId) && !isNativeEvmAsset(assetId),
Copy link
Contributor

Choose a reason for hiding this comment

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

We also have a isToken method you may want to take a look at to keep things declarative, it effectively checks for "is non-native asset", which realistically means EVM chain tokens currently, though that could change in the future (as e.g Cosmos SDK chains can have tokens as wells, though we do not support those in the swapper) so the isEvmChainId() check is definitely welcomed here!

Copy link
Author

Choose a reason for hiding this comment

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

@gomesalexandre hmm yeah maybe isToken is more future thinking, it would be better that if Cosmos support is added later on that the Max button will appear by default

Copy link
Contributor

Choose a reason for hiding this comment

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

Either or, that one is fine as-is as we most likely won't support tokens from non-EVM chains for trading in the future but we may. Would prefer it regardless as it reads nicer and is more declarative

[assetId],
)

return (
<TradeAmountInput
balance={balance}
fiatBalance={fiatBalance}
onMaxClick={shouldShowMax ? onMaxClick : undefined}
{...props}
/>
)
}

export type TradeAssetInputProps = {
Expand Down