diff --git a/src/app/pages/swap/components/swap-asset-dialog/components/use-swap-asset-list.tsx b/src/app/pages/swap/components/swap-asset-dialog/components/use-swap-asset-list.tsx index 0d226e2c14..e648025b7e 100644 --- a/src/app/pages/swap/components/swap-asset-dialog/components/use-swap-asset-list.tsx +++ b/src/app/pages/swap/components/swap-asset-dialog/components/use-swap-asset-list.tsx @@ -15,6 +15,7 @@ import { import type { SwapFormValues } from '@shared/models/form.model'; import { RouteUrls } from '@shared/route-urls'; +// import { bitflow } from '@shared/utils/bitflow-sdk'; import { useSwapContext } from '@app/pages/swap/swap.context'; import type { SwapAssetListProps } from './swap-asset-list'; @@ -28,24 +29,36 @@ export function useSwapAssetList({ assets, type }: SwapAssetListProps) { const isBaseList = type === 'base'; const isQuoteList = type === 'quote'; - const selectableAssets = assets.filter( - asset => - (isBaseList && asset.name !== values.swapAssetQuote?.name) || - (isQuoteList && asset.name !== values.swapAssetBase?.name) - ); + // async function getBtcToken() { + // // const tokens = await bitflow.getAvailableTokens(); + // // console.log(tokens.filter(token => token.tokenId === 'token-xbtc')); + // const result = await bitflow.getQuoteForRoute('token-stx', 'token-xbtc', 1); + // console.log(result); + // } + + // Filter out selected asset from selectable assets + const selectableAssets = assets + .filter( + asset => + (isBaseList && asset.name !== values.swapAssetQuote?.name) || + (isQuoteList && asset.name !== values.swapAssetBase?.name) + ) + // Only show sBTC as quote option if BTC is selected as base + .filter( + asset => + isBaseList || + (isQuoteList && values.swapAssetBase?.name !== 'BTC') || + (isQuoteList && values.swapAssetBase?.name === 'BTC' && asset.name === 'sBTC') + ); const onSelectBaseAsset = useCallback( - (baseAsset: SwapAsset, quoteAsset?: SwapAsset) => { + (baseAsset: SwapAsset) => { void setFieldValue('swapAssetBase', baseAsset); // Handle bridge assets if (baseAsset.name === 'BTC') { onSetIsCrossChainSwap(true); return navigate(RouteUrls.Swap.replace(':base', baseAsset.name).replace(':quote', 'sBTC')); } - if (quoteAsset?.name === 'sBTC') { - void setFieldValue('swapAssetQuote', undefined); - return navigate(RouteUrls.Swap.replace(':base', baseAsset.name).replace(':quote', '')); - } // Handle swap assets onSetIsCrossChainSwap(false); navigate(RouteUrls.Swap.replace(':base', baseAsset.name).replace(':quote', quote ?? '')); @@ -54,29 +67,20 @@ export function useSwapAssetList({ assets, type }: SwapAssetListProps) { ); const onSelectQuoteAsset = useCallback( - (quoteAsset: SwapAsset, baseAsset?: SwapAsset) => { + (quoteAsset: SwapAsset) => { void setFieldValue('swapAssetQuote', quoteAsset); setFieldError('swapAssetQuote', undefined); - // Handle bridge assets - if (isQuoteList && quoteAsset.name === 'sBTC') { - onSetIsCrossChainSwap(true); - return navigate(RouteUrls.Swap.replace(':base', 'BTC').replace(':quote', quoteAsset.name)); - } - if (isQuoteList && baseAsset?.name === 'BTC') { - return navigate(RouteUrls.Swap.replace(':base', 'STX').replace(':quote', quoteAsset.name)); - } - // Handle swap assets - onSetIsCrossChainSwap(false); navigate(RouteUrls.Swap.replace(':base', base ?? '').replace(':quote', quoteAsset.name)); }, - [base, isQuoteList, navigate, onSetIsCrossChainSwap, setFieldError, setFieldValue] + [base, navigate, setFieldError, setFieldValue] ); const onFetchQuoteAmount = useCallback( async (baseAsset: SwapAsset, quoteAsset: SwapAsset) => { + // await getBtcToken(); const quoteAmount = await fetchQuoteAmount(baseAsset, quoteAsset, values.swapAmountBase); - // Handle race condition; make sure quote amount is 1:1 - if (baseAsset.name === 'BTC' || quoteAsset.name === 'sBTC') { + // Handle race condition; make sure quote amount is 1:1 for BTC swap + if (baseAsset.name === 'BTC') { void setFieldValue('swapAmountQuote', values.swapAmountBase); return; } @@ -103,12 +107,12 @@ export function useSwapAssetList({ assets, type }: SwapAssetListProps) { if (isBaseList) { baseAsset = asset; quoteAsset = values.swapAssetQuote; - onSelectBaseAsset(baseAsset, quoteAsset); + onSelectBaseAsset(baseAsset); } if (isQuoteList) { baseAsset = values.swapAssetBase; quoteAsset = asset; - onSelectQuoteAsset(quoteAsset, baseAsset); + onSelectQuoteAsset(quoteAsset); } if (baseAsset && quoteAsset && values.swapAmountBase) { await onFetchQuoteAmount(baseAsset, quoteAsset); diff --git a/src/app/pages/swap/components/swap-asset-select/swap-asset-select-quote.tsx b/src/app/pages/swap/components/swap-asset-select/swap-asset-select-quote.tsx index f5a8b27683..d9cda93070 100644 --- a/src/app/pages/swap/components/swap-asset-select/swap-asset-select-quote.tsx +++ b/src/app/pages/swap/components/swap-asset-select/swap-asset-select-quote.tsx @@ -18,7 +18,7 @@ import { SwapAmountField } from './components/swap-amount-field'; import { SwapAssetSelectLayout } from './components/swap-asset-select.layout'; export function SwapAssetSelectQuote() { - const { isFetchingExchangeRate } = useSwapContext(); + const { isCrossChainSwap, isFetchingExchangeRate } = useSwapContext(); const [amountField] = useField('swapAmountQuote'); const [assetField] = useField('swapAssetQuote'); const navigate = useSwapNavigate(); @@ -37,7 +37,7 @@ export function SwapAssetSelectQuote() { icon={assetField.value?.icon} name="swapAmountQuote" onSelectAsset={() => navigate(RouteUrls.SwapAssetSelectQuote)} - showToggle={assetField.value?.name !== 'sBTC'} + showToggle={!isCrossChainSwap} swapAmountInput={ isFetchingExchangeRate ? ( diff --git a/src/app/pages/swap/hooks/use-bitflow-swap.tsx b/src/app/pages/swap/hooks/use-bitflow-swap.tsx index 4e2185ad92..8aced38615 100644 --- a/src/app/pages/swap/hooks/use-bitflow-swap.tsx +++ b/src/app/pages/swap/hooks/use-bitflow-swap.tsx @@ -2,7 +2,7 @@ import { useCallback, useMemo, useState } from 'react'; import type { RouteQuote } from 'bitflow-sdk'; -import type { SwapAsset } from '@leather.io/query'; +import { type SwapAsset } from '@leather.io/query'; import { migratePositiveAssetBalancesToTop } from '@leather.io/utils'; import { logger } from '@shared/logger'; @@ -22,17 +22,19 @@ export function useBitflowSwap() { const address = useCurrentStacksAccountAddress(); const { data: bitflowSwapAssets = [] } = useBitflowSwappableAssets(address); - // Bridge assets + // Bridge assets; to remove once supported by Bitflow api const createBtcAsset = useBtcSwapAsset(); const createSbtcAsset = useSbtcSwapAsset(); + const btcAsset = createBtcAsset(); + const sbtcAsset = createSbtcAsset(); const swappableAssetsBase = useMemo( - () => [createBtcAsset(), ...migratePositiveAssetBalancesToTop(bitflowSwapAssets)], - [bitflowSwapAssets, createBtcAsset] + () => [btcAsset, sbtcAsset, ...migratePositiveAssetBalancesToTop(bitflowSwapAssets)], + [bitflowSwapAssets, btcAsset, sbtcAsset] ); const swappableAssetsQuote = useMemo( - () => [createSbtcAsset(), ...bitflowSwapAssets], - [bitflowSwapAssets, createSbtcAsset] + () => [sbtcAsset, ...bitflowSwapAssets], + [bitflowSwapAssets, sbtcAsset] ); const fetchRouteQuote = useCallback( @@ -42,10 +44,20 @@ export function useBitflowSwap() { baseAmount: string ): Promise => { if (!baseAmount || !base || !quote || isCrossChainSwap) return; + let baseTokenId = base.tokenId; + let quoteTokenId = quote.tokenId; + // Temporarily handle sBTC exchange rate; force as BTC + // TODO: Remove once Bitflow supports sBTC exchange rate + if (base.tokenId === 'token-sbtc') { + baseTokenId = 'token-xbtc'; + } + if (quote.tokenId === 'token-sbtc') { + quoteTokenId = 'token-xbtc'; + } try { const result = await bitflow.getQuoteForRoute( - base.tokenId, - quote.tokenId, + baseTokenId, + quoteTokenId, Number(baseAmount) ); if (!result.bestRoute) {