diff --git a/.changeset/dry-donkeys-cheat.md b/.changeset/dry-donkeys-cheat.md new file mode 100644 index 00000000..b5fe900d --- /dev/null +++ b/.changeset/dry-donkeys-cheat.md @@ -0,0 +1,5 @@ +--- +'@reservoir0x/relay-kit-ui': patch +--- + +Fix ux issues with canonical route selector diff --git a/packages/ui/src/components/widgets/FeeBreakdown.tsx b/packages/ui/src/components/widgets/FeeBreakdown.tsx index 5c3d0a1a..b2e330b7 100644 --- a/packages/ui/src/components/widgets/FeeBreakdown.tsx +++ b/packages/ui/src/components/widgets/FeeBreakdown.tsx @@ -20,6 +20,7 @@ type Props = Pick< | 'supportsExternalLiquidity' | 'useExternalLiquidity' | 'setUseExternalLiquidity' + | 'canonicalTimeEstimate' > & { toChain?: RelayChain } @@ -38,7 +39,8 @@ const FeeBreakdown: FC = ({ supportsExternalLiquidity, useExternalLiquidity, setUseExternalLiquidity, - timeEstimate + timeEstimate, + canonicalTimeEstimate }) => { const swapRate = price?.details?.rate const originGasFee = feeBreakdown?.breakdown?.find( @@ -91,6 +93,7 @@ const FeeBreakdown: FC = ({ onExternalLiquidityChange={(selected) => { setUseExternalLiquidity(selected) }} + canonicalTimeEstimate={canonicalTimeEstimate?.formattedTime} /> void chain?: RelayChain + canonicalTimeEstimate?: string } const SwapRouteSelector: FC = ({ supportsExternalLiquidity, externalLiquidtySelected, onExternalLiquidityChange, - chain + chain, + canonicalTimeEstimate }) => { const [open, setOpen] = useState(false) - const chainName = chain?.displayName ?? '' return ( = ({ } }} contentProps={{ - sideOffset: 12, + sideOffset: 8, align: 'end', css: { maxWidth: 248, @@ -49,12 +50,12 @@ const SwapRouteSelector: FC = ({ justifyContent: 'space-between', flexDirection: 'row', alignItems: 'center', - backgroundColor: 'transparent', gap: '3', px: '4', py: '3', borderRadius: 'widget-card-border-radius', border: 'widget-card-border', + '&:disabled': { cursor: 'default', backgroundColor: 'transparent', @@ -150,7 +151,9 @@ const SwapRouteSelector: FC = ({ Native - Standard time (>2m), unlimited transaction capacity + {canonicalTimeEstimate + ? `Standard time ~${canonicalTimeEstimate}, unlimited transaction capacity` + : 'Unlimited transaction capacity'} diff --git a/packages/ui/src/components/widgets/SwapWidget/index.tsx b/packages/ui/src/components/widgets/SwapWidget/index.tsx index 640b3b14..77a783bc 100644 --- a/packages/ui/src/components/widgets/SwapWidget/index.tsx +++ b/packages/ui/src/components/widgets/SwapWidget/index.tsx @@ -29,6 +29,7 @@ import type { AdaptedWallet } from '@reservoir0x/relay-sdk' import { evmDeadAddress, solDeadAddress } from '../../../constants/address.js' import { MultiWalletDropdown } from '../../common/MultiWalletDropdown.js' import { findSupportedWallet } from '../../../utils/solana.js' +import SwapRouteSelector from '../SwapRouteSelector.js' type BaseSwapWidgetProps = { defaultFromToken?: Token @@ -156,6 +157,7 @@ const SwapWidget: FC = ({ hasInsufficientBalance, isInsufficientLiquidityError, isCapacityExceededError, + isCouldNotExecuteError, maxCapacityFormatted, ctaCopy, isFromNative, @@ -165,6 +167,7 @@ const SwapWidget: FC = ({ isValidToAddress, supportsExternalLiquidity, useExternalLiquidity, + canonicalTimeEstimate, setUseExternalLiquidity, setDetails, setSwapError, @@ -223,6 +226,10 @@ const SwapWidget: FC = ({ isValidFromAddress ]) + const promptSwitchRoute = + (isCapacityExceededError || isCouldNotExecuteError) && + supportsExternalLiquidity + return ( = ({ + {error && !isFetchingPrice ? ( + + { + setUseExternalLiquidity(selected) + }} + /> + + ) : null} = ({ route: enabled ? 'canonical' : 'relay' }) }} + canonicalTimeEstimate={canonicalTimeEstimate} /> = ({ currency={toToken} isHighRelayerServiceFee={highRelayerServiceFee} isCapacityExceededError={isCapacityExceededError} + isCouldNotExecuteError={isCouldNotExecuteError} maxCapacity={maxCapacityFormatted} relayerFeeProportion={relayerFeeProportion} supportsExternalLiquidity={supportsExternalLiquidity} @@ -829,7 +860,7 @@ const SwapWidget: FC = ({ mb: '6px' }} /> - {error && supportsExternalLiquidity ? ( + {promptSwitchRoute ? ( {isCapacityExceededError && maxCapacityFormatted != '0' ? ( diff --git a/packages/ui/src/components/widgets/SwapWidgetRenderer.tsx b/packages/ui/src/components/widgets/SwapWidgetRenderer.tsx index 92f5b0d6..4002f7b7 100644 --- a/packages/ui/src/components/widgets/SwapWidgetRenderer.tsx +++ b/packages/ui/src/components/widgets/SwapWidgetRenderer.tsx @@ -101,6 +101,7 @@ export type ChildrenProps = { hasInsufficientBalance: boolean isInsufficientLiquidityError?: boolean isCapacityExceededError?: boolean + isCouldNotExecuteError?: boolean maxCapacityWei?: string maxCapacityFormatted?: string ctaCopy: string @@ -108,6 +109,7 @@ export type ChildrenProps = { useExternalLiquidity: boolean supportsExternalLiquidity: boolean timeEstimate?: { time: number; formattedTime: string } + canonicalTimeEstimate?: { time: number; formattedTime: string } fetchingExternalLiquiditySupport: boolean isSvmSwap: boolean isValidFromAddress: boolean @@ -502,9 +504,14 @@ const SwapWidgetRenderer: FC = ({ const isCapacityExceededError = fetchQuoteDataErrorMessage?.includes( 'Amount is higher than the available liquidity' ) + const isCouldNotExecuteError = + fetchQuoteDataErrorMessage?.includes('Could not execute') const highRelayerServiceFee = isHighRelayerServiceFeeUsd(price) const relayerFeeProportion = calculateRelayerFeeProportionUsd(price) const timeEstimate = calculatePriceTimeEstimate(price?.details) + const canonicalTimeEstimate = calculatePriceTimeEstimate( + externalLiquiditySupport.data?.details + ) const isFromNative = fromToken?.address === fromChain?.currency?.address @@ -639,6 +646,7 @@ const SwapWidgetRenderer: FC = ({ hasInsufficientBalance, isInsufficientLiquidityError, isCapacityExceededError, + isCouldNotExecuteError, maxCapacityFormatted, maxCapacityWei, ctaCopy, @@ -646,6 +654,7 @@ const SwapWidgetRenderer: FC = ({ useExternalLiquidity, supportsExternalLiquidity, timeEstimate, + canonicalTimeEstimate, fetchingExternalLiquiditySupport: externalLiquiditySupport.isFetching, isSvmSwap, isValidFromAddress, diff --git a/packages/ui/src/components/widgets/WidgetErrorWell.tsx b/packages/ui/src/components/widgets/WidgetErrorWell.tsx index a50bf340..c5955175 100644 --- a/packages/ui/src/components/widgets/WidgetErrorWell.tsx +++ b/packages/ui/src/components/widgets/WidgetErrorWell.tsx @@ -16,6 +16,7 @@ type Props = { relayerFeeProportion?: bigint | 0 isHighRelayerServiceFee?: boolean isCapacityExceededError?: boolean + isCouldNotExecuteError?: boolean maxCapacity?: string supportsExternalLiquidity?: boolean containerCss?: Styles @@ -29,6 +30,7 @@ export const WidgetErrorWell: FC = ({ relayerFeeProportion, isHighRelayerServiceFee, isCapacityExceededError, + isCouldNotExecuteError, maxCapacity, supportsExternalLiquidity, containerCss @@ -75,7 +77,11 @@ export const WidgetErrorWell: FC = ({ ) - } else if (supportsExternalLiquidity && currency) { + } else if ( + supportsExternalLiquidity && + isCouldNotExecuteError && + currency + ) { return (