Skip to content

Commit

Permalink
fix: swapper uses default asset instead of default default assets whi…
Browse files Browse the repository at this point in the history
…le accounts are loading (#7896)
  • Loading branch information
gomesalexandre authored Oct 9, 2024
1 parent e78c095 commit 144b1c4
Show file tree
Hide file tree
Showing 12 changed files with 282 additions and 196 deletions.
17 changes: 12 additions & 5 deletions src/components/MultiHopTrade/components/TradeInput/TradeInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import { selectIsSnapshotApiQueriesPending, selectVotingPower } from 'state/apis
import {
selectHasUserEnteredAmount,
selectInputSellAsset,
selectIsAccountMetadataLoading,
selectIsAnyAccountMetadataLoadedForChainId,
} from 'state/slices/selectors'
import {
selectActiveQuote,
Expand Down Expand Up @@ -79,12 +79,18 @@ export const TradeInput = ({ isCompact, tradeInputRef }: TradeInputProps) => {
const [shouldShowArbitrumBridgeAcknowledgement, setShouldShowArbitrumBridgeAcknowledgement] =
useState(false)
const isKeplr = useMemo(() => !!wallet && isKeplrHDWallet(wallet), [wallet])
const isAccountMetadataLoading = useAppSelector(selectIsAccountMetadataLoading)

const sellAsset = useAppSelector(selectInputSellAsset)
const tradeQuoteStep = useAppSelector(selectFirstHop)
const isUnsafeQuote = useAppSelector(selectIsUnsafeActiveQuote)

const isAnyAccountMetadataLoadedForChainIdFilter = useMemo(
() => ({ chainId: sellAsset.chainId }),
[sellAsset.chainId],
)
const isAnyAccountMetadataLoadedForChainId = useAppSelector(state =>
selectIsAnyAccountMetadataLoadedForChainId(state, isAnyAccountMetadataLoadedForChainIdFilter),
)

const shouldShowTradeQuoteOrAwaitInput = useAppSelector(selectShouldShowTradeQuoteOrAwaitInput)
const isTradeQuoteRequestAborted = useAppSelector(selectIsTradeQuoteRequestAborted)
const hasUserEnteredAmount = useAppSelector(selectHasUserEnteredAmount)
Expand Down Expand Up @@ -116,19 +122,20 @@ export const TradeInput = ({ isCompact, tradeInputRef }: TradeInputProps) => {

const isLoading = useMemo(
() =>
isAccountMetadataLoading ||
// No account meta loaded for that chain
!isAnyAccountMetadataLoadedForChainId ||
(!shouldShowTradeQuoteOrAwaitInput && !isTradeQuoteRequestAborted) ||
isConfirmationLoading ||
// Only consider snapshot API queries as pending if we don't have voting power yet
// if we do, it means we have persisted or cached (both stale) data, which is enough to let the user continue
// as we are optimistic and don't want to be waiting for a potentially very long time for the snapshot API to respond
isVotingPowerLoading,
[
isAnyAccountMetadataLoadedForChainId,
shouldShowTradeQuoteOrAwaitInput,
isTradeQuoteRequestAborted,
isConfirmationLoading,
isVotingPowerLoading,
isAccountMetadataLoading,
],
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { useTranslate } from 'react-polyglot'
import { useHistory } from 'react-router'
import { ButtonWalletPredicate } from 'components/ButtonWalletPredicate/ButtonWalletPredicate'
import { usePriceImpact } from 'components/MultiHopTrade/hooks/quoteValidation/usePriceImpact'
import { useAccountIds } from 'components/MultiHopTrade/hooks/useAccountIds'
import { TradeRoutePaths } from 'components/MultiHopTrade/types'
import { Text } from 'components/Text'
import { useIsSmartContractAddress } from 'hooks/useIsSmartContractAddress/useIsSmartContractAddress'
Expand All @@ -20,7 +21,7 @@ import {
selectHasUserEnteredAmount,
selectInputBuyAsset,
selectInputSellAsset,
selectIsAccountMetadataLoading,
selectIsAccountsMetadataLoading,
selectManualReceiveAddressIsEditing,
selectManualReceiveAddressIsValid,
selectManualReceiveAddressIsValidating,
Expand Down Expand Up @@ -96,14 +97,16 @@ export const ConfirmSummary = ({
const buyAssetFeeAsset = useAppSelector(state =>
selectFeeAssetById(state, buyAsset?.assetId ?? ''),
)
const isAccountMetadataLoading = useAppSelector(selectIsAccountMetadataLoading)
const isAccountsMetadataLoading = useAppSelector(selectIsAccountsMetadataLoading)

const { priceImpactPercentage } = usePriceImpact(activeQuote)
const walletSupportsBuyAssetChain = useWalletSupportsChain(buyAsset.chainId, wallet)

const { data: _isSmartContractReceiveAddress, isLoading: isReceiveAddressByteCodeLoading } =
useIsSmartContractAddress(receiveAddress ?? '', buyAsset.chainId)

const { sellAssetAccountId: initialSellAssetAccountId } = useAccountIds()

const isTaprootReceiveAddress = useMemo(
() => isUtxoChainId(buyAsset.chainId) && receiveAddress?.startsWith('bc1p'),
[buyAsset.chainId, receiveAddress],
Expand Down Expand Up @@ -137,12 +140,17 @@ export const ConfirmSummary = ({
])

const displayManualAddressEntry = useMemo(() => {
if (isAccountMetadataLoading) return false
if (isAccountsMetadataLoading && !initialSellAssetAccountId) return false
if (!walletSupportsBuyAssetChain) return true
if (disableThorNativeSmartContractReceive) return true

return false
}, [isAccountMetadataLoading, walletSupportsBuyAssetChain, disableThorNativeSmartContractReceive])
}, [
isAccountsMetadataLoading,
initialSellAssetAccountId,
walletSupportsBuyAssetChain,
disableThorNativeSmartContractReceive,
])

const quoteHasError = useMemo(() => {
if (!shouldShowTradeQuoteOrAwaitInput) return false
Expand All @@ -163,7 +171,7 @@ export const ConfirmSummary = ({

const shouldDisablePreviewButton = useMemo(() => {
return (
isAccountMetadataLoading ||
(isAccountsMetadataLoading && !initialSellAssetAccountId) ||
// don't allow executing a quote with errors
quoteHasError ||
// don't execute trades while address is validating
Expand All @@ -185,7 +193,8 @@ export const ConfirmSummary = ({
isTradeQuoteApiQueryPending[activeSwapperName]
)
}, [
isAccountMetadataLoading,
isAccountsMetadataLoading,
initialSellAssetAccountId,
quoteHasError,
manualReceiveAddressIsValidating,
manualReceiveAddressIsEditing,
Expand All @@ -204,7 +213,7 @@ export const ConfirmSummary = ({
const quoteResponseError = quoteResponseErrors[0]
const tradeQuoteError = activeQuoteErrors?.[0]
switch (true) {
case isAccountMetadataLoading:
case isAccountsMetadataLoading && !initialSellAssetAccountId:
return 'common.accountsLoading'
case !shouldShowTradeQuoteOrAwaitInput:
case !hasUserEnteredAmount:
Expand All @@ -227,7 +236,8 @@ export const ConfirmSummary = ({
quoteRequestErrors,
quoteResponseErrors,
activeQuoteErrors,
isAccountMetadataLoading,
isAccountsMetadataLoading,
initialSellAssetAccountId,
shouldShowTradeQuoteOrAwaitInput,
hasUserEnteredAmount,
isAnyTradeQuoteLoading,
Expand Down Expand Up @@ -342,10 +352,11 @@ export const ConfirmSummary = ({
shouldForceManualAddressEntry={disableThorNativeSmartContractReceive}
component={ManualAddressEntry}
description={manualAddressEntryDescription}
chainId={buyAsset.chainId}
/>

<ButtonWalletPredicate
isLoading={isAccountMetadataLoading}
isLoading={isAccountsMetadataLoading && !initialSellAssetAccountId}
loadingText={buttonText}
type='submit'
colorScheme={quoteHasError ? 'red' : 'blue'}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { FormControl, FormLabel, Link } from '@chakra-ui/react'
import { type ChainId } from '@shapeshiftoss/caip'
import { isLedger } from '@shapeshiftoss/hdwallet-ledger'
import { isMetaMask } from '@shapeshiftoss/hdwallet-metamask'
import type { FC } from 'react'
Expand All @@ -15,20 +16,27 @@ import { useModal } from 'hooks/useModal/useModal'
import { useWallet } from 'hooks/useWallet/useWallet'
import { useWalletSupportsChain } from 'hooks/useWalletSupportsChain/useWalletSupportsChain'
import { parseAddressInputWithChainId } from 'lib/address/address'
import { selectAccountIdsByAssetId, selectIsAccountMetadataLoading } from 'state/slices/selectors'
import {
selectAccountIdsByAssetId,
selectIsAnyAccountMetadataLoadingForChainId,
} from 'state/slices/selectors'
import { selectInputBuyAsset } from 'state/slices/tradeInputSlice/selectors'
import { tradeInput } from 'state/slices/tradeInputSlice/tradeInputSlice'
import { useAppDispatch, useAppSelector } from 'state/store'

type ManualAddressEntryProps = {
description?: string
shouldForceManualAddressEntry?: boolean
chainId: ChainId
}

export const ManualAddressEntry: FC<ManualAddressEntryProps> = memo(
({ description, shouldForceManualAddressEntry }: ManualAddressEntryProps): JSX.Element | null => {
({
description,
shouldForceManualAddressEntry,
chainId,
}: ManualAddressEntryProps): JSX.Element | null => {
const dispatch = useAppDispatch()
const isAccountMetadataLoading = useAppSelector(selectIsAccountMetadataLoading)

const {
formState: { isValidating },
Expand Down Expand Up @@ -57,21 +65,30 @@ export const ManualAddressEntry: FC<ManualAddressEntryProps> = memo(
)
const { manualReceiveAddress } = useReceiveAddress(useReceiveAddressArgs)

const isAnyAccountMetadataLoadingByChainIdFilter = useMemo(() => ({ chainId }), [chainId])
const isAnyAccountMetadataLoadingByChainId = useAppSelector(state =>
selectIsAnyAccountMetadataLoadingForChainId(
state,
isAnyAccountMetadataLoadingByChainIdFilter,
),
)

const shouldShowManualReceiveAddressInput = useMemo(() => {
if (isAccountMetadataLoading) return false
// Some AccountIds are loading for that chain - don't show the manual address input since these will eventually be populated
if (isAnyAccountMetadataLoadingByChainId) return false
if (shouldForceManualAddressEntry) return true
if (manualReceiveAddress) return false
// Ledger "supports" all chains, but may not have them connected
if (wallet && isLedger(wallet)) return !buyAssetAccountIds.length
// We want to display the manual address entry if the wallet doesn't support the buy asset chain
return !walletSupportsBuyAssetChain
}, [
buyAssetAccountIds.length,
isAnyAccountMetadataLoadingByChainId,
shouldForceManualAddressEntry,
manualReceiveAddress,
wallet,
buyAssetAccountIds.length,
walletSupportsBuyAssetChain,
shouldForceManualAddressEntry,
isAccountMetadataLoading,
])

const chainAdapterManager = getChainAdapterManager()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import type { TextPropTypes } from 'components/Text/Text'
import { useWallet } from 'hooks/useWallet/useWallet'
import { parseAddressInputWithChainId } from 'lib/address/address'
import { middleEllipsis } from 'lib/utils'
import { selectInputBuyAsset, selectIsAccountMetadataLoading } from 'state/slices/selectors'
import { selectInputBuyAsset } from 'state/slices/selectors'
import { tradeInput } from 'state/slices/tradeInputSlice/tradeInputSlice'
import { useAppDispatch, useAppSelector } from 'state/store'

Expand All @@ -44,7 +44,6 @@ export const RecipientAddress: React.FC<RecipientAddressProps> = ({
}) => {
const translate = useTranslate()
const dispatch = useAppDispatch()
const isAccountMetadataLoading = useAppSelector(selectIsAccountMetadataLoading)
const wallet = useWallet().state.wallet
const useReceiveAddressArgs = useMemo(
() => ({
Expand Down Expand Up @@ -157,7 +156,7 @@ export const RecipientAddress: React.FC<RecipientAddressProps> = ({

const handleFormSubmit = useMemo(() => handleSubmit(onSubmit), [handleSubmit, onSubmit])

if (!receiveAddress || shouldForceManualAddressEntry || isAccountMetadataLoading) return null
if (!receiveAddress || shouldForceManualAddressEntry) return null

return isRecipientAddressEditing ? (
<form>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ import {
selectHighestMarketCapFeeAsset,
selectInputBuyAsset,
selectInputSellAsset,
selectIsAccountMetadataLoadingByAccountId,
selectIsAccountsMetadataLoading,
selectWalletConnectedChainIds,
} from 'state/slices/selectors'
import { tradeInput } from 'state/slices/tradeInputSlice/tradeInputSlice'
Expand Down Expand Up @@ -67,6 +69,10 @@ export const TradeInputBody = ({
state: { wallet },
} = useWallet()

const isAccountMetadataLoadingByAccountId = useAppSelector(
selectIsAccountMetadataLoadingByAccountId,
)
const isAccountsMetadataLoading = useAppSelector(selectIsAccountsMetadataLoading)
const buyAmountAfterFeesCryptoPrecision = useAppSelector(selectBuyAmountAfterFeesCryptoPrecision)
const buyAmountAfterFeesUserCurrency = useAppSelector(selectBuyAmountAfterFeesUserCurrency)
const walletConnectedChainIds = useAppSelector(selectWalletConnectedChainIds)
Expand Down Expand Up @@ -104,12 +110,23 @@ export const TradeInputBody = ({

// If the user disconnects the chain for the currently selected sell asset, switch to the default asset
useEffect(() => {
// Don't do any default asset business as some accounts meta is still loading, or a wrong default asset may be set,
// which takes over the "default default" sellAsset - double default intended:
// https://github.com/shapeshift/web/blob/ba43c41527156f8c7e0f1170472ff362e091b450/src/state/slices/tradeInputSlice/tradeInputSlice.ts#L27
if (Object.values(isAccountMetadataLoadingByAccountId).some(Boolean)) return
if (!defaultSellAsset) return

if (walletConnectedChainIds.includes(sellAsset.chainId)) return

setSellAsset(defaultSellAsset)
}, [defaultSellAsset, sellAsset, setSellAsset, walletConnectedChainIds])
}, [
defaultSellAsset,
isAccountMetadataLoadingByAccountId,
isAccountsMetadataLoading,
sellAsset,
setSellAsset,
walletConnectedChainIds,
])

const handleSellAssetClick = useCallback(() => {
sellAssetSearch.open({
Expand Down
Loading

0 comments on commit 144b1c4

Please sign in to comment.