diff --git a/src/assets/translations/en/main.json b/src/assets/translations/en/main.json
index c324d311341..0415b54a656 100644
--- a/src/assets/translations/en/main.json
+++ b/src/assets/translations/en/main.json
@@ -2819,7 +2819,11 @@
"otherYields": "Other %{symbol} Yields",
"availableToDeposit": "Available to Deposit",
"availableToDepositTooltip": "This is the amount of %{symbol} in your wallet that you can deposit into this yield opportunity.",
- "getAsset": "Get %{symbol}"
+ "getAsset": "Get %{symbol}",
+ "potentialEarningsAmount": "%{amount}/yr at %{apy}% APY",
+ "depositNow": "Deposit Now",
+ "strategyInfo": "Strategy Info",
+ "overview": "Overview"
},
"earn": {
"enterFrom": "Enter from",
diff --git a/src/components/MultiHopTrade/StandaloneMultiHopTrade.tsx b/src/components/MultiHopTrade/StandaloneMultiHopTrade.tsx
index ef9664699e7..8af0115d6ba 100644
--- a/src/components/MultiHopTrade/StandaloneMultiHopTrade.tsx
+++ b/src/components/MultiHopTrade/StandaloneMultiHopTrade.tsx
@@ -25,7 +25,10 @@ import {
import { tradeInput } from '@/state/slices/tradeInputSlice/tradeInputSlice'
import { useAppDispatch, useAppSelector } from '@/state/store'
-export type StandaloneTradeCardProps = TradeCardProps
+export type StandaloneTradeCardProps = TradeCardProps & {
+ onSuccess?: () => void
+ isModal?: boolean
+}
const GetTradeRates = () => {
useGetTradeRates()
@@ -38,7 +41,9 @@ export const StandaloneMultiHopTrade = memo(
defaultSellAssetId,
isCompact,
onChangeTab,
+ onSuccess,
isStandalone,
+ isModal,
}: StandaloneTradeCardProps) => {
const dispatch = useAppDispatch()
const location = useLocation()
@@ -129,7 +134,9 @@ export const StandaloneMultiHopTrade = memo(
)
},
@@ -138,11 +145,13 @@ export const StandaloneMultiHopTrade = memo(
type StandaloneTradeRoutesProps = {
isCompact?: boolean
isStandalone?: boolean
+ isModal?: boolean
onChangeTab: (newTab: TradeInputTab) => void
+ onSuccess?: () => void
}
const StandaloneTradeRoutes = memo(
- ({ isCompact, isStandalone, onChangeTab }: StandaloneTradeRoutesProps) => {
+ ({ isCompact, isStandalone, isModal, onChangeTab, onSuccess }: StandaloneTradeRoutesProps) => {
const location = useLocation()
const tradeInputRef = useRef(null)
@@ -165,7 +174,10 @@ const StandaloneTradeRoutes = memo(
}, [location.pathname])
// Create memoized elements for each route
- const tradeConfirmElement = useMemo(() => , [isCompact])
+ const tradeConfirmElement = useMemo(
+ () => ,
+ [isCompact, isModal, onSuccess],
+ )
const verifyAddressesElement = useMemo(() => , [])
@@ -192,12 +204,13 @@ const StandaloneTradeRoutes = memo(
() => (
),
- [isCompact, onChangeTab, isStandalone],
+ [isCompact, isModal, onChangeTab, isStandalone],
)
return (
diff --git a/src/components/MultiHopTrade/components/SharedConfirm/SharedConfirm.tsx b/src/components/MultiHopTrade/components/SharedConfirm/SharedConfirm.tsx
index 01d678d0397..7813f225b7c 100644
--- a/src/components/MultiHopTrade/components/SharedConfirm/SharedConfirm.tsx
+++ b/src/components/MultiHopTrade/components/SharedConfirm/SharedConfirm.tsx
@@ -2,7 +2,7 @@ import type { CardFooterProps } from '@chakra-ui/react'
import { Card, CardBody, CardFooter, CardHeader, Heading } from '@chakra-ui/react'
import type { JSX } from 'react'
-import { cardstyles } from '../../const'
+import { cardstyles, modalCardStyles } from '../../const'
import { WithBackButton } from '../WithBackButton'
import { TradeSlideTransition } from '@/components/MultiHopTrade/TradeSlideTransition'
@@ -15,6 +15,7 @@ type SharedConfirmProps = {
onBack: () => void
headerTranslation: TextPropTypes['translation']
isLoading?: boolean
+ isModal?: boolean
}
const cardMinHeight = { base: 'calc(100vh - var(--mobile-nav-offset))', md: 'initial' }
@@ -25,10 +26,17 @@ export const SharedConfirm = ({
footerContent,
onBack,
headerTranslation,
+ isModal,
}: SharedConfirmProps) => {
return (
-
+
diff --git a/src/components/MultiHopTrade/components/SharedTradeInput/SharedTradeInput.tsx b/src/components/MultiHopTrade/components/SharedTradeInput/SharedTradeInput.tsx
index 352a1eb3302..de9057c3d55 100644
--- a/src/components/MultiHopTrade/components/SharedTradeInput/SharedTradeInput.tsx
+++ b/src/components/MultiHopTrade/components/SharedTradeInput/SharedTradeInput.tsx
@@ -2,7 +2,7 @@ import type { CardProps } from '@chakra-ui/react'
import { Box, Card, Center, Flex, useMediaQuery } from '@chakra-ui/react'
import type { FormEvent, JSX } from 'react'
-import { cardstyles } from '../../const'
+import { cardstyles, modalCardStyles } from '../../const'
import { SharedTradeInputHeader } from '../SharedTradeInput/SharedTradeInputHeader'
import { useSharedWidth } from '../TradeInput/hooks/useSharedWidth'
@@ -32,6 +32,7 @@ type SharedTradeInputProps = {
onChangeTab: (newTab: TradeInputTab) => void
onSubmit: (e: FormEvent) => void
isStandalone?: boolean
+ isModal?: boolean
}
const cardBorderRadius = { base: '0', md: '2xl' }
@@ -53,6 +54,7 @@ export const SharedTradeInput: React.FC = ({
onChangeTab,
onSubmit,
isStandalone,
+ isModal,
}) => {
const [isSmallerThanMd] = useMediaQuery(`(max-width: ${breakpoints.md})`, { ssr: false })
const [isSmallerThanXl] = useMediaQuery(`(max-width: ${breakpoints.xl})`, { ssr: false })
@@ -79,7 +81,7 @@ export const SharedTradeInput: React.FC = ({
borderRadius={cardBorderRadius}
minHeight={cardMinHeight}
height={!hasUserEnteredAmount && isSmallerThanMd ? cardMinHeight.base : 'initial'}
- {...cardstyles}
+ {...(isModal ? modalCardStyles : cardstyles)}
>
{
+type TradeConfirmProps = {
+ isCompact?: boolean
+ isModal?: boolean
+ onSuccess?: () => void
+}
+
+export const TradeConfirm = ({ isCompact, isModal, onSuccess }: TradeConfirmProps) => {
const navigate = useNavigate()
const { isLoading } = useIsApprovalInitiallyNeeded()
const dispatch = useAppDispatch()
@@ -90,9 +96,18 @@ export const TradeConfirm = ({ isCompact }: { isCompact: boolean | undefined })
isCompact={isCompact}
tradeQuoteStep={tradeQuoteStep}
activeTradeId={activeTradeId}
+ onSwapTxBroadcast={onSuccess}
/>
)
- }, [isTradeComplete, activeQuote, tradeQuoteLastHop, tradeQuoteStep, activeTradeId, isCompact])
+ }, [
+ isTradeComplete,
+ activeQuote,
+ tradeQuoteLastHop,
+ tradeQuoteStep,
+ activeTradeId,
+ isCompact,
+ onSuccess,
+ ])
const isArbitrumBridgeWithdraw = useMemo(() => {
return isArbitrumBridgeTradeQuoteOrRate(activeQuote) && activeQuote.direction === 'withdrawal'
@@ -141,6 +156,7 @@ export const TradeConfirm = ({ isCompact }: { isCompact: boolean | undefined })
isLoading={isLoading}
onBack={handleBack}
headerTranslation={headerTranslation}
+ isModal={isModal}
/>
)
}
diff --git a/src/components/MultiHopTrade/components/TradeConfirm/TradeConfirmFooter.tsx b/src/components/MultiHopTrade/components/TradeConfirm/TradeConfirmFooter.tsx
index 3ac576301e0..b8717cab179 100644
--- a/src/components/MultiHopTrade/components/TradeConfirm/TradeConfirmFooter.tsx
+++ b/src/components/MultiHopTrade/components/TradeConfirm/TradeConfirmFooter.tsx
@@ -51,11 +51,13 @@ type TradeConfirmFooterProps = {
tradeQuoteStep: TradeQuoteStep
activeTradeId: string
isCompact: boolean | undefined
+ onSwapTxBroadcast?: () => void
}
export const TradeConfirmFooter: FC = ({
tradeQuoteStep,
activeTradeId,
+ onSwapTxBroadcast,
}) => {
const [isExactAllowance, toggleIsExactAllowance] = useToggle(true)
const translate = useTranslate()
@@ -412,6 +414,7 @@ export const TradeConfirmFooter: FC = ({
activeTradeId={activeTradeId}
isExactAllowance={isExactAllowance}
isLoading={isNetworkFeeCryptoBaseUnitLoading || isNetworkFeeCryptoBaseUnitRefetching}
+ onSwapTxBroadcast={onSwapTxBroadcast}
/>
)
}, [
@@ -421,6 +424,7 @@ export const TradeConfirmFooter: FC = ({
isExactAllowance,
isNetworkFeeCryptoBaseUnitLoading,
isNetworkFeeCryptoBaseUnitRefetching,
+ onSwapTxBroadcast,
])
return (
diff --git a/src/components/MultiHopTrade/components/TradeConfirm/TradeFooterButton.tsx b/src/components/MultiHopTrade/components/TradeConfirm/TradeFooterButton.tsx
index aeeed78ef01..44f854e94c7 100644
--- a/src/components/MultiHopTrade/components/TradeConfirm/TradeFooterButton.tsx
+++ b/src/components/MultiHopTrade/components/TradeConfirm/TradeFooterButton.tsx
@@ -56,6 +56,7 @@ type TradeFooterButtonProps = {
activeTradeId: string
isExactAllowance: boolean
isLoading?: boolean
+ onSwapTxBroadcast?: () => void
}
export const TradeFooterButton: FC = ({
@@ -64,6 +65,7 @@ export const TradeFooterButton: FC = ({
activeTradeId,
isExactAllowance,
isLoading = false,
+ onSwapTxBroadcast,
}) => {
const [isSubmitting, setIsSubmitting] = useState(false)
const [shouldShowWarningAcknowledgement, setShouldShowWarningAcknowledgement] = useState(false)
@@ -76,6 +78,7 @@ export const TradeFooterButton: FC = ({
currentHopIndex,
activeTradeId,
isExactAllowance,
+ onSwapTxBroadcast,
})
const translate = useTranslate()
const swapperName = useAppSelector(selectActiveSwapperName)
diff --git a/src/components/MultiHopTrade/components/TradeConfirm/hooks/useTradeButtonProps.tsx b/src/components/MultiHopTrade/components/TradeConfirm/hooks/useTradeButtonProps.tsx
index 28071152f94..67177f64ffb 100644
--- a/src/components/MultiHopTrade/components/TradeConfirm/hooks/useTradeButtonProps.tsx
+++ b/src/components/MultiHopTrade/components/TradeConfirm/hooks/useTradeButtonProps.tsx
@@ -31,6 +31,7 @@ type UseTradeButtonPropsProps = {
currentHopIndex: SupportedTradeQuoteStepIndex
activeTradeId: string
isExactAllowance: boolean
+ onSwapTxBroadcast?: () => void
}
type TradeButtonProps = {
@@ -45,6 +46,7 @@ export const useTradeButtonProps = ({
currentHopIndex,
activeTradeId,
isExactAllowance,
+ onSwapTxBroadcast,
}: UseTradeButtonPropsProps): TradeButtonProps | undefined => {
const dispatch = useAppDispatch()
const navigate = useNavigate()
@@ -143,7 +145,7 @@ export const useTradeButtonProps = ({
relayerTxHash,
])
- const executeTrade = useTradeExecution(currentHopIndex, activeTradeId)
+ const executeTrade = useTradeExecution(currentHopIndex, activeTradeId, onSwapTxBroadcast)
const handleSignTx = useCallback(() => {
if (
diff --git a/src/components/MultiHopTrade/components/TradeConfirm/hooks/useTradeExecution.tsx b/src/components/MultiHopTrade/components/TradeConfirm/hooks/useTradeExecution.tsx
index 6d6bda0d11e..1d13e537b61 100644
--- a/src/components/MultiHopTrade/components/TradeConfirm/hooks/useTradeExecution.tsx
+++ b/src/components/MultiHopTrade/components/TradeConfirm/hooks/useTradeExecution.tsx
@@ -67,6 +67,7 @@ import { store, useAppDispatch, useAppSelector } from '@/state/store'
export const useTradeExecution = (
hopIndex: SupportedTradeQuoteStepIndex,
confirmedTradeId: TradeQuote['id'],
+ onSwapTxBroadcast?: () => void,
) => {
const translate = useTranslate()
const dispatch = useAppDispatch()
@@ -238,6 +239,8 @@ export const useTradeExecution = (
})
}
+ onSwapTxBroadcast?.()
+
// Don't navigate away during QuickBuy - let the QuickBuy component handle the success state
if (!isQuickBuy) {
navigate(TradeRoutePaths.Input)
@@ -772,6 +775,7 @@ export const useTradeExecution = (
swapsById,
toast,
isQuickBuy,
+ onSwapTxBroadcast,
])
return executeTrade
diff --git a/src/components/MultiHopTrade/components/TradeInput/TradeInput.tsx b/src/components/MultiHopTrade/components/TradeInput/TradeInput.tsx
index 939f09e303d..b6bcb71f232 100644
--- a/src/components/MultiHopTrade/components/TradeInput/TradeInput.tsx
+++ b/src/components/MultiHopTrade/components/TradeInput/TradeInput.tsx
@@ -86,12 +86,14 @@ type TradeInputProps = {
tradeInputRef: React.MutableRefObject
isCompact?: boolean
isStandalone?: boolean
+ isModal?: boolean
onChangeTab: (newTab: TradeInputTab) => void
}
export const TradeInput = ({
isCompact,
isStandalone,
+ isModal,
tradeInputRef,
onChangeTab,
}: TradeInputProps) => {
@@ -609,6 +611,7 @@ export const TradeInput = ({
onSubmit={handleTradeQuoteConfirm}
onChangeTab={onChangeTab}
isStandalone={isStandalone}
+ isModal={isModal}
/>
>
)
diff --git a/src/components/MultiHopTrade/const.ts b/src/components/MultiHopTrade/const.ts
index ed1689dd332..85aaa6d714a 100644
--- a/src/components/MultiHopTrade/const.ts
+++ b/src/components/MultiHopTrade/const.ts
@@ -27,3 +27,16 @@ export const cardstyles: CardProps = {
boxShadow: '0 1px 0 rgba(255,255,255,0.05) inset, 0 2px 5px rgba(0,0,0,.2)',
},
}
+
+export const modalCardStyles: CardProps = {
+ bg: 'transparent',
+ borderColor: 'transparent',
+ boxShadow: 'none',
+ borderWidth: 0,
+ borderRadius: 0,
+ _dark: {
+ bg: 'transparent',
+ borderColor: 'transparent',
+ boxShadow: 'none',
+ },
+}
diff --git a/src/components/SwapperModal/SwapperModal.tsx b/src/components/SwapperModal/SwapperModal.tsx
new file mode 100644
index 00000000000..6f6bacd0f82
--- /dev/null
+++ b/src/components/SwapperModal/SwapperModal.tsx
@@ -0,0 +1,76 @@
+import type { AssetId } from '@shapeshiftoss/caip'
+import { memo, useCallback } from 'react'
+import { MemoryRouter } from 'react-router-dom'
+
+import { SwapperModalContent } from './SwapperModalContent'
+
+import { Display } from '@/components/Display'
+import { Dialog } from '@/components/Modal/components/Dialog'
+import { DialogBody } from '@/components/Modal/components/DialogBody'
+import { DialogCloseButton } from '@/components/Modal/components/DialogCloseButton'
+import { DialogHeader } from '@/components/Modal/components/DialogHeader'
+import { DialogTitle } from '@/components/Modal/components/DialogTitle'
+import { TradeRoutePaths } from '@/components/MultiHopTrade/types'
+import { tradeInput } from '@/state/slices/tradeInputSlice/tradeInputSlice'
+import { useAppDispatch } from '@/state/store'
+
+type SwapperModalProps = {
+ isOpen: boolean
+ onClose: () => void
+ onSuccess?: () => void
+ defaultBuyAssetId?: AssetId
+ defaultSellAssetId?: AssetId
+}
+
+const initialEntries = [
+ { pathname: TradeRoutePaths.Input },
+ { pathname: TradeRoutePaths.Confirm },
+ { pathname: TradeRoutePaths.VerifyAddresses },
+ { pathname: TradeRoutePaths.QuoteList },
+]
+
+export const SwapperModal = memo(
+ ({ isOpen, onClose, onSuccess, defaultBuyAssetId, defaultSellAssetId }: SwapperModalProps) => {
+ const dispatch = useAppDispatch()
+
+ const handleClose = useCallback(() => {
+ dispatch(tradeInput.actions.clear())
+ onClose()
+ }, [dispatch, onClose])
+
+ return (
+
+ )
+ },
+)
diff --git a/src/components/SwapperModal/SwapperModalContent.tsx b/src/components/SwapperModal/SwapperModalContent.tsx
new file mode 100644
index 00000000000..d307456906c
--- /dev/null
+++ b/src/components/SwapperModal/SwapperModalContent.tsx
@@ -0,0 +1,69 @@
+import type { AssetId } from '@shapeshiftoss/caip'
+import { memo, useCallback, useLayoutEffect, useRef } from 'react'
+import { FormProvider, useForm } from 'react-hook-form'
+import { useNavigate } from 'react-router-dom'
+
+import { TradingErrorBoundary } from '@/components/ErrorBoundary'
+import { StandaloneMultiHopTrade } from '@/components/MultiHopTrade/StandaloneMultiHopTrade'
+import { TradeInputTab, TradeRoutePaths } from '@/components/MultiHopTrade/types'
+import { selectAssetById } from '@/state/slices/assetsSlice/selectors'
+import { tradeInput } from '@/state/slices/tradeInputSlice/tradeInputSlice'
+import { useAppDispatch, useAppSelector } from '@/state/store'
+
+type SwapperModalContentProps = {
+ defaultBuyAssetId?: AssetId
+ defaultSellAssetId?: AssetId
+ onSuccess?: () => void
+}
+
+export const SwapperModalContent = memo(function SwapperModalContent({
+ defaultBuyAssetId,
+ defaultSellAssetId,
+ onSuccess,
+}: SwapperModalContentProps) {
+ const methods = useForm({ mode: 'onChange' })
+ const navigate = useNavigate()
+ const dispatch = useAppDispatch()
+ const hasInitialized = useRef(false)
+
+ const defaultBuyAsset = useAppSelector(state => selectAssetById(state, defaultBuyAssetId ?? ''))
+ const defaultSellAsset = useAppSelector(state => selectAssetById(state, defaultSellAssetId ?? ''))
+
+ useLayoutEffect(() => {
+ if (hasInitialized.current) return
+ hasInitialized.current = true
+
+ dispatch(tradeInput.actions.clear())
+ if (defaultBuyAsset) {
+ dispatch(tradeInput.actions.setBuyAsset(defaultBuyAsset))
+ }
+ if (defaultSellAsset) {
+ dispatch(tradeInput.actions.setSellAsset(defaultSellAsset))
+ }
+ }, [dispatch, defaultBuyAsset, defaultSellAsset])
+
+ const handleChangeTab = useCallback(
+ (newTab: TradeInputTab) => {
+ if (newTab === TradeInputTab.Trade) {
+ navigate(TradeRoutePaths.Input)
+ }
+ },
+ [navigate],
+ )
+
+ return (
+
+
+
+
+
+ )
+})
diff --git a/src/pages/Yields/components/YieldAvailableToDeposit.tsx b/src/pages/Yields/components/YieldAvailableToDeposit.tsx
index fe0b5675e47..8f0b5b60882 100644
--- a/src/pages/Yields/components/YieldAvailableToDeposit.tsx
+++ b/src/pages/Yields/components/YieldAvailableToDeposit.tsx
@@ -11,11 +11,11 @@ import {
Tooltip,
VStack,
} from '@chakra-ui/react'
-import { memo, useCallback, useMemo } from 'react'
+import { memo, useCallback, useMemo, useState } from 'react'
import { useTranslate } from 'react-polyglot'
import { Amount } from '@/components/Amount/Amount'
-import { useTradeNavigation } from '@/components/MultiHopTrade/hooks/useTradeNavigation'
+import { SwapperModal } from '@/components/SwapperModal/SwapperModal'
import { KeyManager } from '@/context/WalletProvider/KeyManager'
import { useFeatureFlag } from '@/hooks/useFeatureFlag/useFeatureFlag'
import { useWallet } from '@/hooks/useWallet/useWallet'
@@ -33,7 +33,7 @@ type YieldAvailableToDepositProps = {
export const YieldAvailableToDeposit = memo(
({ yieldItem, inputTokenMarketData }: YieldAvailableToDepositProps) => {
const translate = useTranslate()
- const { navigateToTrade } = useTradeNavigation()
+ const [isSwapperModalOpen, setIsSwapperModalOpen] = useState(false)
const {
state: { isConnected },
} = useWallet()
@@ -75,9 +75,8 @@ export const YieldAvailableToDeposit = memo(
const hasAvailableBalance = availableBalance.gt(0)
- const handleGetAsset = useCallback(() => {
- navigateToTrade(inputTokenAssetId)
- }, [navigateToTrade, inputTokenAssetId])
+ const handleOpenSwapperModal = useCallback(() => setIsSwapperModalOpen(true), [])
+ const handleCloseSwapperModal = useCallback(() => setIsSwapperModalOpen(false), [])
if (!inputTokenPrecision || !hasWallet) return null
@@ -87,49 +86,57 @@ export const YieldAvailableToDeposit = memo(
if (!hasAvailableBalance) {
return (
-
-
-
-
-
-
- {translate('yieldXYZ.availableToDeposit')}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ <>
+
+
+
+
+
+
+ {translate('yieldXYZ.availableToDeposit')}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ >
)
}