diff --git a/src/components/MultiHopTrade/MultiHopTrade.tsx b/src/components/MultiHopTrade/MultiHopTrade.tsx
index 358d62c6769..b979f92766e 100644
--- a/src/components/MultiHopTrade/MultiHopTrade.tsx
+++ b/src/components/MultiHopTrade/MultiHopTrade.tsx
@@ -85,15 +85,17 @@ export const MultiHopTrade = memo(
}, [defaultBuyAsset, defaultSellAsset, dispatch, routeBuyAsset])
return (
-
-
-
-
-
-
-
-
-
+ <>
+
+
+
+
+
+
+
+
+
+ >
)
},
)
diff --git a/src/components/MultiHopTrade/components/MultiHopTradeConfirm/MultiHopTradeConfirm.tsx b/src/components/MultiHopTrade/components/MultiHopTradeConfirm/MultiHopTradeConfirm.tsx
index 958c1c7b842..46b3f75d00e 100644
--- a/src/components/MultiHopTrade/components/MultiHopTradeConfirm/MultiHopTradeConfirm.tsx
+++ b/src/components/MultiHopTrade/components/MultiHopTradeConfirm/MultiHopTradeConfirm.tsx
@@ -1,12 +1,4 @@
-import {
- Card,
- CardBody,
- CardHeader,
- Heading,
- Stack,
- useDisclosure,
- usePrevious,
-} from '@chakra-ui/react'
+import { Card, CardBody, CardHeader, Heading, useDisclosure, usePrevious } from '@chakra-ui/react'
import { memo, useCallback, useEffect } from 'react'
import { useHistory } from 'react-router-dom'
import { WithBackButton } from 'components/MultiHopTrade/components/WithBackButton'
@@ -14,41 +6,24 @@ import { TradeRoutePaths } from 'components/MultiHopTrade/types'
import { SlideTransition } from 'components/SlideTransition'
import { Text } from 'components/Text'
import { swappers as swappersSlice } from 'state/slices/swappersSlice/swappersSlice'
-import {
- selectActiveSwapperName,
- selectFirstHop,
- selectIsActiveQuoteMultiHop,
- selectLastHop,
- selectTradeExecutionState,
-} from 'state/slices/tradeQuoteSlice/selectors'
+import { selectTradeExecutionState } from 'state/slices/tradeQuoteSlice/selectors'
import { tradeQuoteSlice } from 'state/slices/tradeQuoteSlice/tradeQuoteSlice'
import { MultiHopExecutionState } from 'state/slices/tradeQuoteSlice/types'
import { useAppDispatch, useAppSelector } from 'state/store'
+import { TradeSuccess } from '../TradeSuccess/TradeSuccess'
import { Footer } from './components/Footer'
-import { Hop } from './components/Hop'
+import { Hops } from './components/Hops'
import { useIsApprovalInitiallyNeeded } from './hooks/useIsApprovalInitiallyNeeded'
const cardBorderRadius = { base: 'xl' }
export const MultiHopTradeConfirm = memo(() => {
const dispatch = useAppDispatch()
- const swapperName = useAppSelector(selectActiveSwapperName)
- const firstHop = useAppSelector(selectFirstHop)
- const lastHop = useAppSelector(selectLastHop)
- const isMultiHopTrade = useAppSelector(selectIsActiveQuoteMultiHop)
const tradeExecutionState = useAppSelector(selectTradeExecutionState)
const previousTradeExecutionState = usePrevious(tradeExecutionState)
const history = useHistory()
- const { isOpen: isFirstHopOpen, onToggle: onToggleFirstHop } = useDisclosure({
- defaultIsOpen: true,
- })
-
- const { isOpen: isSecondHopOpen, onToggle: onToggleSecondHop } = useDisclosure({
- defaultIsOpen: true,
- })
-
const { isApprovalInitiallyNeeded, isLoading } = useIsApprovalInitiallyNeeded()
// set initial approval requirements
@@ -63,6 +38,14 @@ export const MultiHopTradeConfirm = memo(() => {
history.push(TradeRoutePaths.Input)
}, [dispatch, history])
+ const { isOpen: isFirstHopOpen, onToggle: onToggleFirstHop } = useDisclosure({
+ defaultIsOpen: true,
+ })
+
+ const { isOpen: isSecondHopOpen, onToggle: onToggleSecondHop } = useDisclosure({
+ defaultIsOpen: true,
+ })
+
// toggle hop open states as we transition to the next hop
useEffect(() => {
if (
@@ -81,50 +64,40 @@ export const MultiHopTradeConfirm = memo(() => {
tradeExecutionState,
])
- // TODO: redirect to completed page when trade is complete
- // useEffect(() => {
- // if (
- // previousTradeExecutionState !== tradeExecutionState &&
- // tradeExecutionState === MultiHopExecutionState.TradeComplete
- // ) {
- // history.push(TradeRoutePaths.Complete)
- // }
- // }, [history, previousTradeExecutionState, tradeExecutionState])
-
- if (!firstHop || !swapperName) return null
-
return (
-
+
-
-
-
- {isMultiHopTrade && lastHop && (
-
+
+
+ ) : (
+ <>
+
+
- )}
-
-
+
+
+ >
+ )}
-
)
})
diff --git a/src/components/MultiHopTrade/components/MultiHopTradeConfirm/components/ApprovalStep.tsx b/src/components/MultiHopTrade/components/MultiHopTradeConfirm/components/ApprovalStep.tsx
index a481f534b01..bfc0c082ca3 100644
--- a/src/components/MultiHopTrade/components/MultiHopTradeConfirm/components/ApprovalStep.tsx
+++ b/src/components/MultiHopTrade/components/MultiHopTradeConfirm/components/ApprovalStep.tsx
@@ -1,4 +1,5 @@
import { Box, Button, Card, Icon, Switch, Tooltip, VStack } from '@chakra-ui/react'
+import { TxStatus } from '@shapeshiftoss/unchained-client'
import { useCallback, useMemo } from 'react'
import { FaInfoCircle } from 'react-icons/fa'
import { useTranslate } from 'react-polyglot'
@@ -69,8 +70,10 @@ export const ApprovalStep = ({
// the txStatus needs to be undefined before the tx is executed to handle "ready" but not "executing" status
const txStatus =
- HOP_EXECUTION_STATE_ORDERED.indexOf(hopExecutionState) >=
- HOP_EXECUTION_STATE_ORDERED.indexOf(HopExecutionState.AwaitingApprovalExecution)
+ hopExecutionState === HopExecutionState.Complete
+ ? TxStatus.Confirmed
+ : HOP_EXECUTION_STATE_ORDERED.indexOf(hopExecutionState) >=
+ HOP_EXECUTION_STATE_ORDERED.indexOf(HopExecutionState.AwaitingApprovalExecution)
? _approvalTxStatus
: undefined
diff --git a/src/components/MultiHopTrade/components/MultiHopTradeConfirm/components/Hop.tsx b/src/components/MultiHopTrade/components/MultiHopTradeConfirm/components/Hop.tsx
index 4d1513ab3cc..e52566bbbd7 100644
--- a/src/components/MultiHopTrade/components/MultiHopTradeConfirm/components/Hop.tsx
+++ b/src/components/MultiHopTrade/components/MultiHopTradeConfirm/components/Hop.tsx
@@ -1,4 +1,3 @@
-import { ChevronDownIcon, ChevronUpIcon } from '@chakra-ui/icons'
import {
Box,
Card,
@@ -8,7 +7,6 @@ import {
Divider,
Flex,
HStack,
- IconButton,
Stepper,
useColorModeValue,
} from '@chakra-ui/react'
@@ -17,7 +15,6 @@ import { getDefaultSlippageDecimalPercentageForSwapper } from 'constants/constan
import prettyMilliseconds from 'pretty-ms'
import { useMemo, useState } from 'react'
import { FaAdjust, FaGasPump, FaProcedures } from 'react-icons/fa'
-import { useTranslate } from 'react-polyglot'
import { Amount } from 'components/Amount/Amount'
import { RawText } from 'components/Text'
import type { SwapperName, TradeQuoteStep } from 'lib/swapper/types'
@@ -40,6 +37,7 @@ import { DonationStep } from './DonationStep'
import { HopTransactionStep } from './HopTransactionStep'
import { JuicyGreenCheck } from './JuicyGreenCheck'
import { TimeRemaining } from './TimeRemaining'
+import { TwirlyToggle } from './TwirlyToggle'
const cardBorderRadius = { base: 'xl' }
@@ -54,13 +52,10 @@ export const Hop = ({
tradeQuoteStep: TradeQuoteStep
hopIndex: number
isOpen: boolean
- onToggleIsOpen: () => void
+ onToggleIsOpen?: () => void
}) => {
- const translate = useTranslate()
const backgroundColor = useColorModeValue('gray.100', 'gray.750')
const borderColor = useColorModeValue('gray.50', 'gray.650')
- const chevronUpIcon = useMemo(() => , [])
- const chevronDownIcon = useMemo(() => , [])
const networkFeeFiatPrecision = useAppSelector(state =>
selectHopTotalNetworkFeeFiatPrecision(state, hopIndex),
)
@@ -91,32 +86,13 @@ export const Hop = ({
)
)
case TxStatus.Confirmed:
- return (
-
-
-
- )
+ return onToggleIsOpen ? (
+
+ ) : null
default:
return null
}
- }, [
- chevronDownIcon,
- chevronUpIcon,
- tradeQuoteStep.estimatedExecutionTimeMs,
- isOpen,
- onToggleIsOpen,
- translate,
- txStatus,
- ])
+ }, [tradeQuoteStep.estimatedExecutionTimeMs, isOpen, onToggleIsOpen, txStatus])
const initialApprovalRequirements = useAppSelector(selectInitialApprovalRequirements)
const isApprovalInitiallyNeeded = initialApprovalRequirements?.[hopIndex]
diff --git a/src/components/MultiHopTrade/components/MultiHopTradeConfirm/components/HopTransactionStep.tsx b/src/components/MultiHopTrade/components/MultiHopTradeConfirm/components/HopTransactionStep.tsx
index 6119af5b640..27551bf74c0 100644
--- a/src/components/MultiHopTrade/components/MultiHopTradeConfirm/components/HopTransactionStep.tsx
+++ b/src/components/MultiHopTrade/components/MultiHopTradeConfirm/components/HopTransactionStep.tsx
@@ -1,6 +1,6 @@
import { Box, Button, Link, VStack } from '@chakra-ui/react'
import type { KnownChainIds } from '@shapeshiftoss/types'
-import type { TxStatus } from '@shapeshiftoss/unchained-client'
+import { TxStatus } from '@shapeshiftoss/unchained-client'
import { useCallback, useEffect, useMemo } from 'react'
import { Row } from 'components/Row/Row'
import { RawText, Text } from 'components/Text'
@@ -87,8 +87,10 @@ export const HopTransactionStep = ({
// the txStatus needs to be undefined before the tx is executed to handle "ready" but not "executing" status
const txStatus =
- HOP_EXECUTION_STATE_ORDERED.indexOf(hopExecutionState) >=
- HOP_EXECUTION_STATE_ORDERED.indexOf(HopExecutionState.AwaitingTradeExecution)
+ hopExecutionState === HopExecutionState.Complete
+ ? TxStatus.Confirmed
+ : HOP_EXECUTION_STATE_ORDERED.indexOf(hopExecutionState) >=
+ HOP_EXECUTION_STATE_ORDERED.indexOf(HopExecutionState.AwaitingTradeExecution)
? tradeStatus
: undefined
diff --git a/src/components/MultiHopTrade/components/MultiHopTradeConfirm/components/Hops.tsx b/src/components/MultiHopTrade/components/MultiHopTradeConfirm/components/Hops.tsx
new file mode 100644
index 00000000000..c54ce52dd20
--- /dev/null
+++ b/src/components/MultiHopTrade/components/MultiHopTradeConfirm/components/Hops.tsx
@@ -0,0 +1,49 @@
+import { Stack } from '@chakra-ui/react'
+import { memo } from 'react'
+import {
+ selectActiveSwapperName,
+ selectFirstHop,
+ selectIsActiveQuoteMultiHop,
+ selectLastHop,
+} from 'state/slices/tradeQuoteSlice/selectors'
+import { useAppSelector } from 'state/store'
+
+import { Hop } from './Hop'
+
+export type HopsProps = {
+ isFirstHopOpen: boolean
+ isSecondHopOpen: boolean
+ onToggleFirstHop?: () => void
+ onToggleSecondHop?: () => void
+}
+
+export const Hops = memo((props: HopsProps) => {
+ const { isFirstHopOpen, isSecondHopOpen, onToggleFirstHop, onToggleSecondHop } = props
+ const swapperName = useAppSelector(selectActiveSwapperName)
+ const firstHop = useAppSelector(selectFirstHop)
+ const lastHop = useAppSelector(selectLastHop)
+ const isMultiHopTrade = useAppSelector(selectIsActiveQuoteMultiHop)
+
+ if (!firstHop || !swapperName) return null
+
+ return (
+
+
+ {isMultiHopTrade && lastHop && (
+
+ )}
+
+ )
+})
diff --git a/src/components/MultiHopTrade/components/MultiHopTradeConfirm/components/TwirlyToggle.tsx b/src/components/MultiHopTrade/components/MultiHopTradeConfirm/components/TwirlyToggle.tsx
new file mode 100644
index 00000000000..a2ec273c83c
--- /dev/null
+++ b/src/components/MultiHopTrade/components/MultiHopTradeConfirm/components/TwirlyToggle.tsx
@@ -0,0 +1,39 @@
+import { ChevronUpIcon } from '@chakra-ui/icons'
+import type { BoxProps } from '@chakra-ui/react'
+import { Box, Circle, IconButton, useColorModeValue } from '@chakra-ui/react'
+import { useMemo } from 'react'
+import { useTranslate } from 'react-polyglot'
+
+export type TwirlyToggleProps = { isOpen: boolean; onToggle: () => void } & BoxProps
+
+export const TwirlyToggle = ({ isOpen, onToggle, ...boxProps }: TwirlyToggleProps) => {
+ const translate = useTranslate()
+ const backgroundColor = useColorModeValue('gray.100', 'gray.750')
+
+ const icon = useMemo(
+ () => (
+
+
+
+ ),
+ [backgroundColor, isOpen],
+ )
+
+ return (
+
+
+
+ )
+}
diff --git a/src/components/MultiHopTrade/components/TradeSuccess/TradeSuccess.tsx b/src/components/MultiHopTrade/components/TradeSuccess/TradeSuccess.tsx
new file mode 100644
index 00000000000..6213a37b038
--- /dev/null
+++ b/src/components/MultiHopTrade/components/TradeSuccess/TradeSuccess.tsx
@@ -0,0 +1,97 @@
+import {
+ Box,
+ Button,
+ CardBody,
+ CardFooter,
+ Collapse,
+ HStack,
+ useDisclosure,
+} from '@chakra-ui/react'
+import { useMemo } from 'react'
+import { Amount } from 'components/Amount/Amount'
+import { AssetIcon } from 'components/AssetIcon'
+import { SlideTransition } from 'components/SlideTransition'
+import { RawText } from 'components/Text'
+import { getChainAdapterManager } from 'context/PluginProvider/chainAdapterSingleton'
+import { useLocaleFormatter } from 'hooks/useLocaleFormatter/useLocaleFormatter'
+import { selectEquityTotalBalance } from 'state/slices/selectors'
+import { selectLastHop, selectLastHopSellAccountId } from 'state/slices/tradeQuoteSlice/selectors'
+import { useAppSelector } from 'state/store'
+
+import { TwirlyToggle } from '../MultiHopTradeConfirm/components/TwirlyToggle'
+
+export type TradeSuccessProps = { handleBack: () => void; children: JSX.Element }
+
+export const TradeSuccess = ({ handleBack, children }: TradeSuccessProps) => {
+ const {
+ number: { toCrypto },
+ } = useLocaleFormatter()
+
+ const { isOpen, onToggle } = useDisclosure({
+ defaultIsOpen: false,
+ })
+
+ const lastHop = useAppSelector(selectLastHop)
+ const lastHopSellAssetAccountId = useAppSelector(selectLastHopSellAccountId)
+
+ const filter = useMemo(() => {
+ return {
+ assetId: lastHop?.buyAsset.assetId,
+ ...(lastHopSellAssetAccountId ? { accountId: lastHopSellAssetAccountId } : {}),
+ }
+ }, [lastHop?.buyAsset.assetId, lastHopSellAssetAccountId])
+
+ const { amountCryptoPrecision: totalCryptoHumanBalance, fiatAmount: totalFiatBalance } =
+ useAppSelector(state => selectEquityTotalBalance(state, filter))
+
+ const subText = useMemo(() => {
+ if (!lastHop) return ''
+
+ const manager = getChainAdapterManager()
+ const adapter = manager.get(lastHop.buyAsset.chainId)
+
+ if (!adapter) return ''
+
+ const cryptoAmountFormatted = toCrypto(totalCryptoHumanBalance, lastHop.buyAsset.symbol)
+ const chainName = adapter.getDisplayName()
+ return `You now have ${cryptoAmountFormatted} in your wallet on ${chainName}.`
+ }, [lastHop, toCrypto, totalCryptoHumanBalance])
+
+ if (!lastHop) return null
+
+ return (
+ <>
+
+
+
+
+
+
+
+ {subText}
+
+
+
+
+
+
+
+
+
+
+
+ {children}
+
+
+ >
+ )
+}