diff --git a/apps/web/src/app/(networks)/(evm)/api/cross-chain/routes/route.ts b/apps/web/src/app/(networks)/(evm)/api/cross-chain/routes/route.ts index 7198b19dd3..280e1adb53 100644 --- a/apps/web/src/app/(networks)/(evm)/api/cross-chain/routes/route.ts +++ b/apps/web/src/app/(networks)/(evm)/api/cross-chain/routes/route.ts @@ -64,7 +64,7 @@ export async function GET(request: NextRequest) { exchanges: { allow: ['sushiswap'] }, allowSwitchChain: false, allowDestinationCall: true, - // fee: // TODO: must set up feeReceiver w/ lifi + fee: 0.0025, // 0.25% }, }), } diff --git a/apps/web/src/app/(networks)/(non-evm)/aptos/swap/ui/simple/simple-swap-trade-button.tsx b/apps/web/src/app/(networks)/(non-evm)/aptos/swap/ui/simple/simple-swap-trade-button.tsx index b49929c97e..ab0b84dd7a 100644 --- a/apps/web/src/app/(networks)/(non-evm)/aptos/swap/ui/simple/simple-swap-trade-button.tsx +++ b/apps/web/src/app/(networks)/(non-evm)/aptos/swap/ui/simple/simple-swap-trade-button.tsx @@ -13,11 +13,17 @@ export const SimpleSwapTradeButton = () => { const [checked, setChecked] = useState(false) const { data: routes } = useSwap() + const showPriceImpactWarning = useMemo(() => { + const priceImpactSeverity = warningSeverity(routes?.priceImpact) + return priceImpactSeverity > 3 + }, [routes?.priceImpact]) + + // Reset useEffect(() => { - if (warningSeverity(routes?.priceImpact) <= 3) { + if (checked && !showPriceImpactWarning) { setChecked(false) } - }, [routes]) + }, [showPriceImpactWarning, checked]) const checkerAmount = useMemo(() => { if (!token0) return [] @@ -48,9 +54,7 @@ export const SimpleSwapTradeButton = () => { 3 - } + guardWhen={!checked && showPriceImpactWarning} guardText="Price impact too high" variant="destructive" size="xl" @@ -74,7 +78,7 @@ export const SimpleSwapTradeButton = () => { - {warningSeverity(routes?.priceImpact) > 3 && ( + {showPriceImpactWarning && (
isXSwapSupportedChainId(chainId), { - message: `fromChainId must exist in XSwapChainId`, - }), - fromAmount: z.string().transform((amount) => BigInt(amount)), - fromToken: crossChainTokenSchema, - toChainId: z.coerce - .number() - .refine((chainId) => isXSwapSupportedChainId(chainId), { - message: `toChainId must exist in XSwapChainId`, - }), - toAmount: z.string().transform((amount) => BigInt(amount)), - toAmountMin: z.string().transform((amount) => BigInt(amount)), - toToken: crossChainTokenSchema, - gasCostUSD: z.string(), - steps: z.array(crossChainStepSchema), - tags: z.array(z.string()).optional(), - transactionRequest: crossChainTransactionRequestSchema.optional(), -}) +export const crossChainRouteSchema = z + .object({ + id: z.string(), + fromChainId: z.coerce + .number() + .refine((chainId) => isXSwapSupportedChainId(chainId), { + message: `fromChainId must exist in XSwapChainId`, + }), + fromAmount: z.string().transform((amount) => BigInt(amount)), + fromToken: crossChainTokenSchema, + toChainId: z.coerce + .number() + .refine((chainId) => isXSwapSupportedChainId(chainId), { + message: `toChainId must exist in XSwapChainId`, + }), + toAmount: z.string().transform((amount) => BigInt(amount)), + toAmountMin: z.string().transform((amount) => BigInt(amount)), + toToken: crossChainTokenSchema, + gasCostUSD: z.string(), + steps: z.array( + crossChainStepSchema.transform((data) => { + return { + ...data, + includedStepsWithoutFees: data.includedSteps.filter( + (step) => step.tool !== 'feeCollection', + ), + } + }), + ), + tags: z.array(z.string()).optional(), + transactionRequest: crossChainTransactionRequestSchema.optional(), + }) + .refine((data) => data.steps.length === 1, { + message: 'multi-step routes are not supported', + }) + .transform((data) => { + const { steps, ...rest } = data + return { + ...rest, + step: steps[0], + } + }) diff --git a/apps/web/src/lib/swap/cross-chain/utils.tsx b/apps/web/src/lib/swap/cross-chain/utils.tsx index f14fbd64b2..a0d4118c3e 100644 --- a/apps/web/src/lib/swap/cross-chain/utils.tsx +++ b/apps/web/src/lib/swap/cross-chain/utils.tsx @@ -3,6 +3,34 @@ import { Amount, Native, Token, Type } from 'sushi/currency' import { zeroAddress } from 'viem' import { CrossChainStep } from './types' +export const getCrossChainStepBreakdown = (step?: CrossChainStep) => { + if (!step) + return { + srcStep: undefined, + bridgeStep: undefined, + dstStep: undefined, + } + + const feeIndex = step.includedSteps.findIndex( + (_step) => _step.type === 'protocol', + ) + + const steps = + feeIndex === -1 + ? step.includedSteps + : [ + ...step.includedSteps.slice(0, feeIndex), + ...step.includedSteps.slice(feeIndex + 1), + ] + + const bridgeIndex = steps.findIndex((_step) => _step.type === 'cross') + return { + srcStep: steps[bridgeIndex - 1], + bridgeStep: steps[bridgeIndex], + dstStep: steps[bridgeIndex + 1], + } +} + interface FeeBreakdown { amount: Amount amountUSD: number @@ -11,16 +39,23 @@ interface FeeBreakdown { export interface FeesBreakdown { gas: Map protocol: Map + ui: Map } enum FeeType { GAS = 'GAS', PROTOCOL = 'PROTOCOL', + UI = 'UI', } -export const getCrossChainFeesBreakdown = (route: CrossChainStep[]) => { - const gasFeesBreakdown = getFeesBreakdown(route, FeeType.GAS) - const protocolFeesBreakdown = getFeesBreakdown(route, FeeType.PROTOCOL) +const UI_FEE_NAME = 'LIFI Shared Fee' + +export const getCrossChainFeesBreakdown = ( + _steps: CrossChainStep[] | CrossChainStep, +) => { + const steps = Array.isArray(_steps) ? _steps : [_steps] + const gasFeesBreakdown = getFeesBreakdown(steps, FeeType.GAS) + const protocolFeesBreakdown = getFeesBreakdown(steps, FeeType.PROTOCOL) const gasFeesUSD = Array.from(gasFeesBreakdown.values()).reduce( (sum, gasCost) => sum + gasCost.amountUSD, 0, @@ -29,25 +64,35 @@ export const getCrossChainFeesBreakdown = (route: CrossChainStep[]) => { (sum, feeCost) => sum + feeCost.amountUSD, 0, ) - const totalFeesUSD = gasFeesUSD + protocolFeesUSD + const totalFeesUSD = gasFeesUSD + protocolFeesUSD // does not include UI fees + + const uiFeesBreakdown = getFeesBreakdown(steps, FeeType.UI) + const uiFeesUSD = Array.from(uiFeesBreakdown.values()).reduce( + (sum, feeCost) => sum + feeCost.amountUSD, + 0, + ) return { feesBreakdown: { gas: gasFeesBreakdown, protocol: protocolFeesBreakdown, + ui: uiFeesBreakdown, }, totalFeesUSD, gasFeesUSD, protocolFeesUSD, + uiFeesUSD, } } -const getFeesBreakdown = (route: CrossChainStep[], feeType: FeeType) => { - return route.reduce((feesByChainId, step) => { +const getFeesBreakdown = (steps: CrossChainStep[], feeType: FeeType) => { + return steps.reduce((feesByChainId, step) => { const fees = feeType === FeeType.PROTOCOL ? step.estimate.feeCosts.filter((fee) => fee.included === false) - : step.estimate.gasCosts + : feeType === FeeType.UI + ? step.estimate.feeCosts.filter((fee) => fee.name === UI_FEE_NAME) + : step.estimate.gasCosts if (fees.length === 0) return feesByChainId diff --git a/apps/web/src/ui/swap/cross-chain/cross-chain-swap-confirmation-dialog.tsx b/apps/web/src/ui/swap/cross-chain/cross-chain-swap-confirmation-dialog.tsx index 3dfb59e4ff..fe36a0ce9d 100644 --- a/apps/web/src/ui/swap/cross-chain/cross-chain-swap-confirmation-dialog.tsx +++ b/apps/web/src/ui/swap/cross-chain/cross-chain-swap-confirmation-dialog.tsx @@ -32,10 +32,10 @@ export const ConfirmationDialogContent: FC = ({ const { data: trade } = useSelectedCrossChainTradeRoute() const swapOnDest = - trade?.steps[0] && + trade?.step && [ - trade.steps[0].includedSteps[1]?.type, - trade.steps[0].includedSteps[2]?.type, + trade.step.includedStepsWithoutFees[1]?.type, + trade.step.includedStepsWithoutFees[2]?.type, ].includes('swap') ? true : false @@ -98,12 +98,13 @@ export const ConfirmationDialogContent: FC = ({ if (dialogState.dest === StepState.PartialSuccess) { const fromTokenSymbol = - routeRef?.current?.steps?.[0]?.includedSteps?.[1]?.type === 'swap' - ? routeRef?.current?.steps?.[0]?.includedSteps?.[1]?.action?.fromToken - ?.symbol - : routeRef?.current?.steps?.[0]?.includedSteps?.[2]?.type === 'swap' - ? routeRef?.current?.steps?.[0]?.includedSteps?.[2]?.action?.fromToken - ?.symbol + routeRef?.current?.step?.includedStepsWithoutFees?.[1]?.type === 'swap' + ? routeRef?.current?.step?.includedStepsWithoutFees?.[1]?.action + ?.fromToken?.symbol + : routeRef?.current?.step?.includedStepsWithoutFees?.[2]?.type === + 'swap' + ? routeRef?.current?.step?.includedStepsWithoutFees?.[2]?.action + ?.fromToken?.symbol : undefined return ( diff --git a/apps/web/src/ui/swap/cross-chain/cross-chain-swap-route-card.tsx b/apps/web/src/ui/swap/cross-chain/cross-chain-swap-route-card.tsx index 203bb65aaf..4f50c90993 100644 --- a/apps/web/src/ui/swap/cross-chain/cross-chain-swap-route-card.tsx +++ b/apps/web/src/ui/swap/cross-chain/cross-chain-swap-route-card.tsx @@ -41,7 +41,7 @@ export const CrossChainSwapRouteCard: FC = ({ state: { token1, chainId0, chainId1 }, } = useDerivedStateCrossChainSwap() - const { data: price } = usePrice({ + const { data: price, isLoading: isPriceLoading } = usePrice({ chainId: token1?.chainId, address: token1?.wrapped.address, }) @@ -73,7 +73,7 @@ export const CrossChainSwapRouteCard: FC = ({ protocolFeesUSD, totalFeesUSD, } = useMemo(() => { - const step = route.steps[0] + const step = route.step const executionDurationSeconds = step.estimate.executionDuration const executionDurationMinutes = Math.floor(executionDurationSeconds / 60) @@ -83,7 +83,7 @@ export const CrossChainSwapRouteCard: FC = ({ : `${executionDurationMinutes} minutes` const { feesBreakdown, totalFeesUSD, gasFeesUSD, protocolFeesUSD } = - getCrossChainFeesBreakdown(route.steps) + getCrossChainFeesBreakdown(step) return { step, @@ -93,7 +93,7 @@ export const CrossChainSwapRouteCard: FC = ({ gasFeesUSD, protocolFeesUSD, } - }, [route?.steps]) + }, [route.step]) return ( = ({ {amountOutUSD ? ( - {`≈ ${amountOutUSD} after fees`} + ≈ ${amountOutUSD} after fees ) : ( - + )} @@ -145,7 +150,7 @@ export const CrossChainSwapRouteCard: FC = ({
- {isSelected && step.includedSteps.length > 1 ? ( + {isSelected && step.includedStepsWithoutFees.length > 1 ? ( <> diff --git a/apps/web/src/ui/swap/cross-chain/cross-chain-swap-route-mobile-card.tsx b/apps/web/src/ui/swap/cross-chain/cross-chain-swap-route-mobile-card.tsx index 429ae5d56f..878c872da3 100644 --- a/apps/web/src/ui/swap/cross-chain/cross-chain-swap-route-mobile-card.tsx +++ b/apps/web/src/ui/swap/cross-chain/cross-chain-swap-route-mobile-card.tsx @@ -27,7 +27,7 @@ export const CrossChainSwapRouteMobileCard: FC< state: { token1, chainId0, chainId1 }, } = useDerivedStateCrossChainSwap() - const { data: price } = usePrice({ + const { data: price, isLoading: isPriceLoading } = usePrice({ chainId: token1?.chainId, address: token1?.wrapped.address, }) @@ -59,7 +59,7 @@ export const CrossChainSwapRouteMobileCard: FC< protocolFeesUSD, totalFeesUSD, } = useMemo(() => { - const step = route?.steps[0] + const step = route?.step if (!step) return { step, @@ -79,7 +79,7 @@ export const CrossChainSwapRouteMobileCard: FC< : `${executionDurationMinutes} minutes` const { feesBreakdown, totalFeesUSD, gasFeesUSD, protocolFeesUSD } = - getCrossChainFeesBreakdown(route.steps) + getCrossChainFeesBreakdown(step) return { step, @@ -89,7 +89,7 @@ export const CrossChainSwapRouteMobileCard: FC< gasFeesUSD, protocolFeesUSD, } - }, [route?.steps]) + }, [route?.step]) return ( ) : ( -
+ -
+ )} {amountOutUSD ? ( ≈ ${amountOutUSD} after fees ) : ( -
+ -
+ )} {route?.tags?.includes(order) ? (
diff --git a/apps/web/src/ui/swap/cross-chain/cross-chain-swap-route-selector.tsx b/apps/web/src/ui/swap/cross-chain/cross-chain-swap-route-selector.tsx index 22672c080d..b5c7dfa3d2 100644 --- a/apps/web/src/ui/swap/cross-chain/cross-chain-swap-route-selector.tsx +++ b/apps/web/src/ui/swap/cross-chain/cross-chain-swap-route-selector.tsx @@ -119,8 +119,8 @@ const DesktopRouteSelector: FC = ({ key={`route-${route.id}`} route={route} order={routeOrder} - isSelected={route.steps[0].tool === selectedBridge} - onSelect={() => setSelectedBridge(route.steps[0].tool)} + isSelected={route.step.tool === selectedBridge} + onSelect={() => setSelectedBridge(route.step.tool)} /> ))} @@ -210,8 +210,8 @@ const MobileRouteSelector: FC = ({ key={`route-${route.id}`} route={route} order={routeOrder} - isSelected={route.steps[0].tool === selectedBridge} - onSelect={() => setSelectedBridge(route.steps[0].tool)} + isSelected={route.step.tool === selectedBridge} + onSelect={() => setSelectedBridge(route.step.tool)} /> ))}
@@ -239,7 +239,7 @@ const MobileRouteSelector: FC = ({ route.steps[0].tool === selectedBridge, + (route) => route.step.tool === selectedBridge, )} order={routeOrder} isSelected={true} @@ -329,18 +329,18 @@ const NoRoutesFound = () => { fill="none" xmlns="http://www.w3.org/2000/svg" > - + diff --git a/apps/web/src/ui/swap/cross-chain/cross-chain-swap-route-view.tsx b/apps/web/src/ui/swap/cross-chain/cross-chain-swap-route-view.tsx index 093bf65995..9c9850515c 100644 --- a/apps/web/src/ui/swap/cross-chain/cross-chain-swap-route-view.tsx +++ b/apps/web/src/ui/swap/cross-chain/cross-chain-swap-route-view.tsx @@ -2,6 +2,7 @@ import { classNames } from '@sushiswap/ui' import { NetworkIcon } from '@sushiswap/ui/icons/NetworkIcon' import React, { useMemo } from 'react' import { FC } from 'react' +import { getCrossChainStepBreakdown } from 'src/lib/swap/cross-chain' import type { CrossChainAction, CrossChainEstimate, @@ -20,16 +21,10 @@ interface CrossChainSwapRouteViewProps { export const CrossChainSwapRouteView: FC = ({ step, }) => { - const { srcStep, bridgeStep, dstStep } = useMemo(() => { - const bridgeIndex = step.includedSteps.findIndex( - (_step) => _step.type === 'cross', - ) - return { - srcStep: step.includedSteps[bridgeIndex - 1], - bridgeStep: step.includedSteps[bridgeIndex], - dstStep: step.includedSteps[bridgeIndex + 1], - } - }, [step]) + const { srcStep, bridgeStep, dstStep } = useMemo( + () => getCrossChainStepBreakdown(step), + [step], + ) return (
@@ -56,7 +51,7 @@ export const CrossChainSwapRouteView: FC = ({ )} /> )} - + {dstStep ? ( { const { data: route, isError } = useSelectedCrossChainTradeRoute() const [checked, setChecked] = useState(false) + const showPriceImpactWarning = useMemo(() => { + const priceImpactSeverity = warningSeverity(route?.priceImpact) + return priceImpactSeverity > 3 + }, [route?.priceImpact]) + // Reset useEffect(() => { - if (warningSeverity(route?.priceImpact) <= 3) { + if (checked && !showPriceImpactWarning) { setChecked(false) } - }, [route]) + }, [showPriceImpactWarning, checked]) return ( @@ -43,7 +48,7 @@ export const CrossChainSwapTradeButton: FC = () => { id="approve-erc20" fullWidth amount={swapAmount} - contract={route?.steps?.[0]?.estimate?.approvalAddress} + contract={route?.step?.estimate?.approvalAddress} > @@ -52,19 +57,14 @@ export const CrossChainSwapTradeButton: FC = () => { !route?.amountOut?.greaterThan(ZERO) || isError || +swapAmountString === 0 || - (!checked && - warningSeverity(route?.priceImpact) > 3), + (!checked && showPriceImpactWarning), )} - color={ - warningSeverity(route?.priceImpact) >= 3 - ? 'red' - : 'blue' - } + color={showPriceImpactWarning ? 'red' : 'blue'} fullWidth size="xl" testId="swap" > - {!checked && warningSeverity(route?.priceImpact) >= 3 + {!checked && showPriceImpactWarning ? 'Price impact too high' : isError ? 'No trade found' @@ -78,7 +78,7 @@ export const CrossChainSwapTradeButton: FC = () => {
- {warningSeverity(route?.priceImpact) > 3 && ( + {showPriceImpactWarning && (
_step ?? - (selectedRoute?.steps?.[0] + (selectedRoute?.step ? { - ...selectedRoute.steps[0], + ...selectedRoute.step, tokenIn: selectedRoute?.tokenIn, tokenOut: selectedRoute?.tokenOut, amountIn: selectedRoute?.amountIn, @@ -232,7 +233,8 @@ const _CrossChainSwapTradeReviewDialog: FC<{ txHash: hash, promise: receiptPromise, summary: - routeRef?.current?.steps?.[0]?.includedSteps?.[0]?.type === 'cross' + routeRef?.current?.step?.includedStepsWithoutFees?.[0]?.type === + 'cross' ? { pending: `Sending ${routeRef?.current?.amountIn?.toSignificant( 6, @@ -254,7 +256,7 @@ const _CrossChainSwapTradeReviewDialog: FC<{ )} ${ routeRef?.current?.amountIn?.currency.symbol } to bridge token ${ - routeRef?.current?.steps?.[0]?.includedSteps?.[0]?.action + routeRef?.current?.step?.includedStepsWithoutFees?.[0]?.action .toToken.symbol }`, completed: `Swapped ${routeRef?.current?.amountIn?.toSignificant( @@ -262,7 +264,7 @@ const _CrossChainSwapTradeReviewDialog: FC<{ )} ${ routeRef?.current?.amountIn?.currency.symbol } to bridge token ${ - routeRef?.current?.steps?.[0]?.includedSteps?.[0]?.action + routeRef?.current?.step?.includedStepsWithoutFees?.[0]?.action .toToken.symbol }`, failed: `Something went wrong when trying to swap ${routeRef?.current?.amountIn?.currency.symbol} to bridge token`, @@ -457,23 +459,25 @@ const _CrossChainSwapTradeReviewDialog: FC<{ }) .then(reset), summary: - routeRef?.current?.steps?.[0]?.includedSteps?.[1]?.type === 'swap' || - routeRef?.current?.steps?.[0]?.includedSteps?.[2]?.type === 'swap' + routeRef?.current?.step?.includedStepsWithoutFees?.[1]?.type === + 'swap' || + routeRef?.current?.step?.includedStepsWithoutFees?.[2]?.type === + 'swap' ? { pending: `Swapping ${ - routeRef?.current?.steps?.[0]?.includedSteps[2]?.action + routeRef?.current?.step?.includedStepsWithoutFees[2]?.action .fromToken?.symbol } to ${routeRef?.current?.amountOut?.toSignificant(6)} ${ routeRef?.current?.amountOut?.currency.symbol }`, completed: `Swapped ${ - routeRef?.current?.steps?.[0]?.includedSteps[2]?.action + routeRef?.current?.step?.includedStepsWithoutFees[2]?.action .fromToken?.symbol } to ${routeRef?.current?.amountOut?.toSignificant(6)} ${ routeRef?.current?.amountOut?.currency.symbol }`, failed: `Something went wrong when trying to swap ${ - routeRef?.current?.steps?.[0]?.includedSteps[2]?.action + routeRef?.current?.step?.includedStepsWithoutFees[2]?.action .fromToken?.symbol } to ${routeRef?.current?.amountOut?.toSignificant(6)} ${ routeRef?.current?.amountOut?.currency.symbol @@ -521,7 +525,8 @@ const _CrossChainSwapTradeReviewDialog: FC<{ ? `${executionDurationSeconds} seconds` : `${executionDurationMinutes} minutes` - const { feesBreakdown, totalFeesUSD } = getCrossChainFeesBreakdown([step]) + const { feesBreakdown, totalFeesUSD, uiFeesUSD } = + getCrossChainFeesBreakdown([step]) const chainId0Fees = ( feesBreakdown.gas.get(step.tokenIn.chainId)?.amount ?? @@ -536,12 +541,12 @@ const _CrossChainSwapTradeReviewDialog: FC<{ return { executionDuration, feesBreakdown, - totalFeesUSD, + totalFeesUSD: totalFeesUSD + uiFeesUSD, chainId0Fees, } }, [step]) - const { data: price } = usePrice({ + const { data: price, isLoading: isPriceLoading } = usePrice({ chainId: token1?.chainId, address: token1?.wrapped.address, }) @@ -568,6 +573,11 @@ const _CrossChainSwapTradeReviewDialog: FC<{ [step?.amountOutMin, price], ) + const showPriceImpactWarning = useMemo(() => { + const priceImpactSeverity = warningSeverity(step?.priceImpact) + return priceImpactSeverity > 3 + }, [step?.priceImpact]) + return ( <> @@ -701,7 +711,7 @@ const _CrossChainSwapTradeReviewDialog: FC<{ {feesBreakdown && feesBreakdown.protocol.size > 0 ? (
{feesBreakdown.protocol.get(chainId0) ? ( @@ -749,6 +759,55 @@ const _CrossChainSwapTradeReviewDialog: FC<{
) : null} + {feesBreakdown && feesBreakdown.ui.size > 0 ? ( + +
+ {feesBreakdown.ui.get(chainId0) ? ( + + {formatNumber( + feesBreakdown.ui + .get(chainId0)! + .amount.toExact(), + )}{' '} + { + feesBreakdown.ui.get(chainId0)!.amount + .currency.symbol + }{' '} + + ( + {formatUSD( + feesBreakdown.ui.get(chainId0)!.amountUSD, + )} + ) + + + ) : null} + {feesBreakdown.ui.get(chainId1) ? ( + + {formatNumber( + feesBreakdown.ui + .get(chainId1)! + .amount.toExact(), + )}{' '} + { + feesBreakdown.ui.get(chainId1)!.amount + .currency.symbol + }{' '} + + ( + {formatUSD( + feesBreakdown.ui.get(chainId1)!.amountUSD, + )} + ) + + + ) : null} +
+
+ ) : null} ) : ( @@ -798,7 +860,10 @@ const _CrossChainSwapTradeReviewDialog: FC<{ ) : ( @@ -852,7 +917,10 @@ const _CrossChainSwapTradeReviewDialog: FC<{ ) : ( @@ -929,11 +997,11 @@ const _CrossChainSwapTradeReviewDialog: FC<{ isStepQueryError } color={ - isEstGasError || isStepQueryError + isEstGasError || + isStepQueryError || + showPriceImpactWarning ? 'red' - : warningSeverity(step?.priceImpact) >= 3 - ? 'red' - : 'blue' + : 'blue' } testId="confirm-swap" > diff --git a/apps/web/src/ui/swap/cross-chain/cross-chain-swap-trade-review-route.tsx b/apps/web/src/ui/swap/cross-chain/cross-chain-swap-trade-review-route.tsx index 9a86a1a0de..6534021b7a 100644 --- a/apps/web/src/ui/swap/cross-chain/cross-chain-swap-trade-review-route.tsx +++ b/apps/web/src/ui/swap/cross-chain/cross-chain-swap-trade-review-route.tsx @@ -18,13 +18,13 @@ export const CrossChainSwapTradeReviewRoute = () => {
- {trade?.steps?.[0]?.type === 'lifi' && - trade.steps[0].includedSteps.map((step, i) => { + {trade?.step?.type === 'lifi' && + trade.step.includedStepsWithoutFees.map((step, i) => { return ( {i > 0 ? ( diff --git a/apps/web/src/ui/swap/cross-chain/cross-chain-swap-trade-stats.tsx b/apps/web/src/ui/swap/cross-chain/cross-chain-swap-trade-stats.tsx index a72c66da30..577a97458a 100644 --- a/apps/web/src/ui/swap/cross-chain/cross-chain-swap-trade-stats.tsx +++ b/apps/web/src/ui/swap/cross-chain/cross-chain-swap-trade-stats.tsx @@ -28,8 +28,8 @@ export const CrossChainSwapTradeStats: FC = () => { const { isLoading, data: trade, isError } = useSelectedCrossChainTradeRoute() const feeData = useMemo( - () => (trade?.steps ? getCrossChainFeesBreakdown(trade.steps) : undefined), - [trade?.steps], + () => (trade?.step ? getCrossChainFeesBreakdown(trade.step) : undefined), + [trade?.step], ) return ( @@ -89,6 +89,19 @@ export const CrossChainSwapTradeStats: FC = () => {
+
+ + Fee (0.25%) + + + {isLoading || !feeData ? ( + + ) : ( + `${formatUSD(feeData.uiFeesUSD)}` + )} + +
+
Network fee diff --git a/apps/web/src/ui/swap/cross-chain/derivedstate-cross-chain-swap-provider.tsx b/apps/web/src/ui/swap/cross-chain/derivedstate-cross-chain-swap-provider.tsx index 95435987a3..57b158daac 100644 --- a/apps/web/src/ui/swap/cross-chain/derivedstate-cross-chain-swap-provider.tsx +++ b/apps/web/src/ui/swap/cross-chain/derivedstate-cross-chain-swap-provider.tsx @@ -400,9 +400,9 @@ const useCrossChainTradeRoutes = () => { if ( query.data?.length && (typeof selectedBridge === 'undefined' || - !query.data.find((route) => route.steps[0].tool === selectedBridge)) + !query.data.find((route) => route.step.tool === selectedBridge)) ) { - setSelectedBridge(query.data[0].steps[0].tool) + setSelectedBridge(query.data[0].step.tool) } }, [query.data, selectedBridge, setSelectedBridge]) @@ -428,7 +428,7 @@ const useSelectedCrossChainTradeRoute = () => { const route: UseSelectedCrossChainTradeRouteReturn | undefined = useMemo(() => { const route = routesQuery.data?.find( - (route) => route.steps[0].tool === selectedBridge, + (route) => route.step.tool === selectedBridge, ) if (!route) return undefined @@ -460,19 +460,6 @@ const useSelectedCrossChainTradeRoute = () => { 10_000, ) - // const gasSpent = Amount.fromRawAmount( - // Native.onChain(route.fromChainId), - // route.steps.reduce( - // (total, step) => - // total + - // step.estimate.gasCosts.reduce( - // (total, gasCost) => total + gasCost.amount, - // 0n, - // ), - // 0n, - // ), - // ).toFixed(6) - return { ...route, tokenIn, @@ -481,7 +468,6 @@ const useSelectedCrossChainTradeRoute = () => { amountOut, amountOutMin, priceImpact, - // gasSpent, } }, [routesQuery.data, selectedBridge]) diff --git a/apps/web/src/ui/swap/simple/simple-swap-trade-button.tsx b/apps/web/src/ui/swap/simple/simple-swap-trade-button.tsx index 97f8a5f23b..4ab6b25dfa 100644 --- a/apps/web/src/ui/swap/simple/simple-swap-trade-button.tsx +++ b/apps/web/src/ui/swap/simple/simple-swap-trade-button.tsx @@ -8,7 +8,7 @@ import { HoverCardTrigger, } from '@sushiswap/ui' import { Button } from '@sushiswap/ui' -import React, { FC, useEffect, useState } from 'react' +import React, { FC, useEffect, useMemo, useState } from 'react' import { UseTradeReturn } from 'src/lib/hooks/react-query' import { Checker } from 'src/lib/wagmi/systems/Checker' import { @@ -99,12 +99,17 @@ const _SimpleSwapTradeButton: FC = ({ token1?.isNative && token0?.wrapped.address === Native.onChain(chainId).wrapped.address + const showPriceImpactWarning = useMemo(() => { + const priceImpactSeverity = warningSeverity(trade?.priceImpact) + return priceImpactSeverity > 3 + }, [trade?.priceImpact]) + // Reset useEffect(() => { - if (warningSeverity(trade?.priceImpact) <= 3) { + if (checked && !showPriceImpactWarning) { setChecked(false) } - }, [trade?.priceImpact]) + }, [showPriceImpactWarning, checked]) return ( <> @@ -136,18 +141,13 @@ const _SimpleSwapTradeButton: FC = ({ !trade?.amountOut?.greaterThan(ZERO) || trade?.route?.status === 'NoWay' || +swapAmountString === 0 || - (!checked && - warningSeverity(trade?.priceImpact) > 3), + (!checked && showPriceImpactWarning), )} - color={ - warningSeverity(trade?.priceImpact) >= 3 - ? 'red' - : 'blue' - } + color={showPriceImpactWarning ? 'red' : 'blue'} fullWidth testId="swap" > - {!checked && warningSeverity(trade?.priceImpact) >= 3 + {!checked && showPriceImpactWarning ? 'Price impact too high' : trade?.route?.status === 'NoWay' ? 'No trade found' @@ -166,7 +166,7 @@ const _SimpleSwapTradeButton: FC = ({
- {warningSeverity(trade?.priceImpact) > 3 && ( + {showPriceImpactWarning && (
{ + const priceImpactSeverity = warningSeverity(trade?.priceImpact) + return { + showPriceImpactWarning: priceImpactSeverity > 3, + priceImpactSeverity, + } + }, [trade?.priceImpact]) + return ( @@ -308,7 +316,7 @@ export const SimpleSwapTradeReviewDialog: FC<{
- {warningSeverity(trade?.priceImpact) >= 3 && ( + {showPriceImpactWarning && (
High price impact. You will lose a significant portion @@ -328,9 +336,7 @@ export const SimpleSwapTradeReviewDialog: FC<{ > @@ -451,11 +457,7 @@ export const SimpleSwapTradeReviewDialog: FC<{ isError, )} color={ - isError - ? 'red' - : warningSeverity(trade?.priceImpact) >= 3 - ? 'red' - : 'blue' + isError || showPriceImpactWarning ? 'red' : 'blue' } testId="confirm-swap" >