From ec2f17ff40c14af12816ec05bdfde4db5c007b14 Mon Sep 17 00:00:00 2001 From: viet-nv Date: Mon, 13 Jan 2025 08:11:41 +0700 Subject: [PATCH 1/5] temp --- src/components/TradeRouting/RouteRowV3.tsx | 13 + src/components/TradeRouting/index.tsx | 33 +- src/pages/Earns/useLiquidityWidget.tsx | 3 - src/pages/SwapV3/index.tsx | 9 +- src/services/route/index.ts | 411 +++++++++++++++++++-- src/utils/aggregationRouting.ts | 72 +++- 6 files changed, 475 insertions(+), 66 deletions(-) create mode 100644 src/components/TradeRouting/RouteRowV3.tsx diff --git a/src/components/TradeRouting/RouteRowV3.tsx b/src/components/TradeRouting/RouteRowV3.tsx new file mode 100644 index 0000000000..e4db13591f --- /dev/null +++ b/src/components/TradeRouting/RouteRowV3.tsx @@ -0,0 +1,13 @@ +import { SwapRouteV3 } from 'utils/aggregationRouting' + +export const RouteRowV3 = ({ tradeComposition }: { tradeComposition: SwapRouteV3[] }) => { + const firstSwaps = tradeComposition.filter(item => item.depth === 1) + + return ( + <> + {firstSwaps.map((swap, index) => { + return
{swap.exchange}
+ })} + + ) +} diff --git a/src/components/TradeRouting/index.tsx b/src/components/TradeRouting/index.tsx index 4b95a5261b..f2707461fa 100644 --- a/src/components/TradeRouting/index.tsx +++ b/src/components/TradeRouting/index.tsx @@ -28,7 +28,9 @@ import { useActiveWeb3React } from 'hooks' import { useCurrencyV2 } from 'hooks/Tokens' import { useAllDexes } from 'state/customizeDexes/hooks' import { getEtherscanLink, isAddress } from 'utils' -import { SwapRouteV2 } from 'utils/aggregationRouting' +import { SwapRouteV2, SwapRouteV3 } from 'utils/aggregationRouting' + +import { RouteRowV3 } from './RouteRowV3' interface RouteRowProps { route: SwapRouteV2 @@ -117,7 +119,7 @@ const RouteRow = ({ route, chainId, backgroundColor }: RouteRowProps) => { interface RoutingProps { maxHeight?: string - tradeComposition: SwapRouteV2[] | undefined + tradeComposition: SwapRouteV2[] | SwapRouteV3[] | undefined currencyIn: Currency | undefined currencyOut: Currency | undefined inputAmount: CurrencyAmount | undefined @@ -171,6 +173,7 @@ const Routing = ({ useEffect(() => { handleScroll() }, [tradeComposition, maxHeight, handleScroll]) + const isSwapRouteV3 = tradeComposition?.every(item => 'pool' in item) return ( @@ -187,16 +190,22 @@ const Routing = ({ - {tradeComposition.map(route => ( - - {getSwapPercent(route.swapPercentage, tradeComposition.length)} - - - - - - - ))} + {isSwapRouteV3 ? ( + + ) : ( + (tradeComposition as SwapRouteV2[]).map(route => { + return ( + + {getSwapPercent(route.swapPercentage, tradeComposition.length)} + + + + + + + ) + }) + )} ) : null} diff --git a/src/pages/Earns/useLiquidityWidget.tsx b/src/pages/Earns/useLiquidityWidget.tsx index c21275088a..060a212257 100644 --- a/src/pages/Earns/useLiquidityWidget.tsx +++ b/src/pages/Earns/useLiquidityWidget.tsx @@ -199,10 +199,7 @@ const useLiquidityWidget = () => { onOpenZapMigration: handleOpenZapMigrationWidget, onSubmitTx: async (txData: { from: string; to: string; data: string; value: string; gasLimit: string }) => { try { - console.log('txData', txData) if (!library) throw new Error('Library is not ready!') - const gas = await library.estimateGas(txData) - console.log(gas) const res = await library?.getSigner().sendTransaction(txData) if (!res) throw new Error('Transaction failed') return res.hash diff --git a/src/pages/SwapV3/index.tsx b/src/pages/SwapV3/index.tsx index df31e9aec2..b66c5a8fc5 100644 --- a/src/pages/SwapV3/index.tsx +++ b/src/pages/SwapV3/index.tsx @@ -153,7 +153,14 @@ export default function Swap() { }, [tabFromUrl, searchParams, setSearchParams]) const tradeRouteComposition = useMemo(() => { - return getTradeComposition(chainId, routeSummary?.parsedAmountIn, undefined, routeSummary?.route, defaultTokens) + return getTradeComposition( + chainId, + routeSummary?.parsedAmountIn, + undefined, + routeSummary?.route, + defaultTokens, + routeSummary?.tokenOut || '', + ) }, [chainId, defaultTokens, routeSummary]) const onBackToSwapTab = () => setActiveTab(getDefaultTab()) diff --git a/src/services/route/index.ts b/src/services/route/index.ts index 1058d0f0d7..12e56260ff 100644 --- a/src/services/route/index.ts +++ b/src/services/route/index.ts @@ -37,44 +37,389 @@ const routeApi = createApi({ const { chainId, tokenInDecimals, tokenOutDecimals, tokenIn, tokenOut } = params || {} // Ensure all necessary data is available - if (baseResponse?.data?.routeSummary && routeSummary && chainId && tokenInDecimals && tokenOutDecimals) { - const { amountIn, amountOut } = routeSummary - - // Build the URL for the price impact API request - const priceImpactUrl = new URL(`${BFF_API}/v1/price-impact`) - priceImpactUrl.searchParams.append('tokenIn', tokenIn) - priceImpactUrl.searchParams.append('tokenInDecimal', tokenInDecimals.toString()) - priceImpactUrl.searchParams.append('tokenOut', tokenOut) - priceImpactUrl.searchParams.append('tokenOutDecimal', tokenOutDecimals.toString()) - priceImpactUrl.searchParams.append('amountIn', amountIn) - priceImpactUrl.searchParams.append('amountOut', amountOut) - priceImpactUrl.searchParams.append('chainId', chainId.toString()) + //if (baseResponse?.data?.routeSummary && routeSummary && chainId && tokenInDecimals && tokenOutDecimals) { + // const { amountIn, amountOut } = routeSummary + // + // // Build the URL for the price impact API request + // const priceImpactUrl = new URL(`${BFF_API}/v1/price-impact`) + // priceImpactUrl.searchParams.append('tokenIn', tokenIn) + // priceImpactUrl.searchParams.append('tokenInDecimal', tokenInDecimals.toString()) + // priceImpactUrl.searchParams.append('tokenOut', tokenOut) + // priceImpactUrl.searchParams.append('tokenOutDecimal', tokenOutDecimals.toString()) + // priceImpactUrl.searchParams.append('amountIn', amountIn) + // priceImpactUrl.searchParams.append('amountOut', amountOut) + // priceImpactUrl.searchParams.append('chainId', chainId.toString()) + // + // try { + // // Fetch price impact data + // const priceImpactResponse = await fetch(priceImpactUrl.toString()).then(res => res.json()) + // const { amountInUSD, amountOutUSD } = priceImpactResponse?.data || {} + // + // // Update routeSummary with USD values if available + // if (amountInUSD && amountOutUSD) { + // return { + // ...baseResponse, + // data: { + // ...baseResponse.data, + // routeSummary: { + // ...routeSummary, + // amountInUsd: amountInUSD, + // amountOutUsd: amountOutUSD, + // }, + // }, + // } + // } + // } catch (error) { + // console.error('Failed to fetch price impact:', error) + // } + //} - try { - // Fetch price impact data - const priceImpactResponse = await fetch(priceImpactUrl.toString()).then(res => res.json()) - const { amountInUSD, amountOutUSD } = priceImpactResponse?.data || {} + // Return original response if conditions are not met or request fails + //return baseResponse - // Update routeSummary with USD values if available - if (amountInUSD && amountOutUSD) { - return { - ...baseResponse, - data: { - ...baseResponse.data, - routeSummary: { - ...routeSummary, - amountInUsd: amountInUSD, - amountOutUsd: amountOutUSD, - }, + if (!baseResponse.data) return baseResponse + baseResponse.data.routeSummary = { + tokenIn: '0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0', + amountIn: '1000000000000000000000', + amountInUsd: '4352018.709992444', + tokenOut: '0x2260fac5e5542a773aa44fbcfedf7c193bc2c599', + amountOut: '4531365902', + amountOutUsd: '4382943.981611451', + gasUsd: '116.68998577404858', + route: [ + [ + { + pool: 'bebop_0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0_0x9f8f72aa9304c8b593d555f12ef6589cc3a579a2', + tokenIn: '0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0', + tokenOut: '0x9f8f72aa9304c8b593d555f12ef6589cc3a579a2', + limitReturnAmount: '0', + swapAmount: '50000000000000000000', + amountOut: '114442213243747581952', + exchange: 'bebop', + poolLength: 2, + poolType: 'bebop', + poolExtra: null, + }, + ], + [ + { + pool: '0x93d199263632a4ef4bb438f1feb99e57b4b5f0bd', + tokenIn: '0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0', + tokenOut: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', + limitReturnAmount: '0', + swapAmount: '250000000000000000000', + amountOut: '296542467421645344516', + exchange: 'balancer-v2-composable-stable', + poolLength: 3, + poolType: 'balancer-v2-composable-stable', + poolExtra: { + blockNumber: 21319890, + poolId: '0x93d199263632a4ef4bb438f1feb99e57b4b5f0bd0000000000000000000005c2', + tokenOutIndex: 2, + vault: '0xba12222222228d8ba445958a75a0704d566bf2c8', }, - } - } - } catch (error) { - console.error('Failed to fetch price impact:', error) - } + }, + ], + [ + { + pool: '0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0', + tokenIn: '0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0', + tokenOut: '0xae7ab96520de3a18e5e111b5eaab095312d7fe84', + limitReturnAmount: '0', + swapAmount: '400000000000000000000', + amountOut: '474626027414909852000', + exchange: 'lido', + poolLength: 2, + poolType: 'lido', + poolExtra: null, + }, + ], + [ + { + pool: '0x109830a1aaad605bbf02a9dfa7b0b92ec2fb7daa', + tokenIn: '0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0', + tokenOut: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', + limitReturnAmount: '0', + swapAmount: '150000000000000000000', + amountOut: '177919417229229300643', + exchange: 'uniswapv3', + poolLength: 2, + poolType: 'uniswapv3', + poolExtra: { + blockNumber: 0, + priceLimit: 4295128740, + }, + }, + ], + [ + { + pool: '0x0b1a513ee24972daef112bc777a5610d4325c9e7', + tokenIn: '0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0', + tokenOut: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', + limitReturnAmount: '0', + swapAmount: '150000000000000000000', + amountOut: '177927048177000000000', + exchange: 'fluid-dex-t1', + poolLength: 2, + poolType: 'fluid-dex-t1', + poolExtra: { + blockNumber: 21319890, + }, + }, + ], + [ + { + pool: '0xe8c6c9227491c0a8156a0106a0204d881bb7e531', + tokenIn: '0x9f8f72aa9304c8b593d555f12ef6589cc3a579a2', + tokenOut: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', + limitReturnAmount: '0', + swapAmount: '114442213243747581952', + amountOut: '66944420731774458985', + exchange: 'uniswapv3', + poolLength: 2, + poolType: 'uniswapv3', + poolExtra: { + blockNumber: 0, + priceLimit: 4306310044, + }, + }, + ], + [ + { + pool: '0xdc24316b9ae028f1497c275eb9192a3ea0f67022', + tokenIn: '0xae7ab96520de3a18e5e111b5eaab095312d7fe84', + tokenOut: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', + limitReturnAmount: '0', + swapAmount: '415297773988046120500', + amountOut: '415162858681867357755', + exchange: 'curve-stable-plain', + poolLength: 2, + poolType: 'curve-stable-plain', + poolExtra: { + TokenInIsNative: false, + TokenOutIsNative: true, + tokenInIndex: 1, + tokenOutIndex: 0, + underlying: false, + }, + }, + ], + [ + { + pool: 'kyber_pmm_0x2260fac5e5542a773aa44fbcfedf7c193bc2c599_0xae7ab96520de3a18e5e111b5eaab095312d7fe84', + tokenIn: '0xae7ab96520de3a18e5e111b5eaab095312d7fe84', + tokenOut: '0x2260fac5e5542a773aa44fbcfedf7c193bc2c599', + limitReturnAmount: '0', + swapAmount: '59328253426863731500', + amountOut: '225526762', + exchange: 'kyber-pmm', + poolLength: 2, + poolType: 'kyber-pmm', + poolExtra: { + timestamp: 1733203751, + }, + }, + ], + [ + { + pool: '0x4585fe77225b41b697c938b018e2ac67ac5a20c0', + tokenIn: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', + tokenOut: '0x2260fac5e5542a773aa44fbcfedf7c193bc2c599', + limitReturnAmount: '0', + swapAmount: '422796963697320913295', + amountOut: '1605175859', + exchange: 'uniswapv3', + poolLength: 2, + poolType: 'uniswapv3', + poolExtra: { + blockNumber: 0, + priceLimit: 1.4613005734278674e48, + }, + }, + ], + [ + { + pool: '0x11b815efb8f581194ae79006d24e0d814b7697f6', + tokenIn: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', + tokenOut: '0xdac17f958d2ee523a2206206994597c13d831ec7', + limitReturnAmount: '0', + swapAmount: '118619237329356120072', + amountOut: '433089706619', + exchange: 'uniswapv3', + poolLength: 2, + poolType: 'uniswapv3', + poolExtra: { + blockNumber: 0, + priceLimit: 4295558252, + }, + }, + ], + [ + { + pool: 'kyber_pmm_0x2260fac5e5542a773aa44fbcfedf7c193bc2c599_0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', + tokenIn: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', + tokenOut: '0x2260fac5e5542a773aa44fbcfedf7c193bc2c599', + limitReturnAmount: '0', + swapAmount: '118619536988929547734', + amountOut: '450987594', + exchange: 'kyber-pmm', + poolLength: 2, + poolType: 'kyber-pmm', + poolExtra: { + timestamp: 1733203751, + }, + }, + ], + [ + { + pool: '0x88e6a0c2ddd26feeb64f039a2c41296fcb3f5640', + tokenIn: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', + tokenOut: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', + limitReturnAmount: '0', + swapAmount: '177924678792959222266', + amountOut: '649603082233', + exchange: 'uniswapv3', + poolLength: 2, + poolType: 'uniswapv3', + poolExtra: { + blockNumber: 0, + priceLimit: 1.4613005734278674e48, + }, + }, + ], + [ + { + pool: 'kyber_pmm_0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2_0xdac17f958d2ee523a2206206994597c13d831ec7', + tokenIn: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', + tokenOut: '0xdac17f958d2ee523a2206206994597c13d831ec7', + limitReturnAmount: '0', + swapAmount: '177919417229229300642', + amountOut: '649229108600', + exchange: 'kyber-pmm', + poolLength: 2, + poolType: 'kyber-pmm', + poolExtra: { + timestamp: 1733203751, + }, + }, + ], + [ + { + pool: '0xcbcdf9626bc03e24f779434178a73a0b4bad62ed', + tokenIn: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', + tokenOut: '0x2260fac5e5542a773aa44fbcfedf7c193bc2c599', + limitReturnAmount: '0', + swapAmount: '118616378203721357890', + amountOut: '449504431', + exchange: 'uniswapv3', + poolLength: 2, + poolType: 'uniswapv3', + poolExtra: { + blockNumber: 0, + priceLimit: 1.4576520669498474e48, + }, + }, + ], + [ + { + pool: 'kyber_pmm_0x2260fac5e5542a773aa44fbcfedf7c193bc2c599_0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', + tokenIn: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', + tokenOut: '0x2260fac5e5542a773aa44fbcfedf7c193bc2c599', + limitReturnAmount: '0', + swapAmount: '216663329831', + amountOut: '225532718', + exchange: 'kyber-pmm', + poolLength: 2, + poolType: 'kyber-pmm', + poolExtra: { + timestamp: 1733203751, + }, + }, + ], + [ + { + pool: '0x9a772018fbd77fcd2d25657e5c547baff3fd7d16', + tokenIn: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', + tokenOut: '0x2260fac5e5542a773aa44fbcfedf7c193bc2c599', + limitReturnAmount: '0', + swapAmount: '216537519276', + amountOut: '225146915', + exchange: 'uniswapv3', + poolLength: 2, + poolType: 'uniswapv3', + poolExtra: { + blockNumber: 0, + }, + }, + ], + [ + { + pool: '0x99ac8ca7087fa4a2a1fb6357269965a2014abc35', + tokenIn: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', + tokenOut: '0x2260fac5e5542a773aa44fbcfedf7c193bc2c599', + limitReturnAmount: '0', + swapAmount: '216402233126', + amountOut: '224649112', + exchange: 'uniswapv3', + poolLength: 2, + poolType: 'uniswapv3', + poolExtra: { + blockNumber: 0, + }, + }, + ], + [ + { + pool: 'kyber_pmm_0x2260fac5e5542a773aa44fbcfedf7c193bc2c599_0xdac17f958d2ee523a2206206994597c13d831ec7', + tokenIn: '0xdac17f958d2ee523a2206206994597c13d831ec7', + tokenOut: '0x2260fac5e5542a773aa44fbcfedf7c193bc2c599', + limitReturnAmount: '0', + swapAmount: '216623912419', + amountOut: '225658416', + exchange: 'kyber-pmm', + poolLength: 2, + poolType: 'kyber-pmm', + poolExtra: { + timestamp: 1733203751, + }, + }, + ], + [ + { + pool: '0x9db9e0e53058c89e5b94e29621a205198648425b', + tokenIn: '0xdac17f958d2ee523a2206206994597c13d831ec7', + tokenOut: '0x2260fac5e5542a773aa44fbcfedf7c193bc2c599', + limitReturnAmount: '0', + swapAmount: '649327036433', + amountOut: '674681659', + exchange: 'uniswapv3', + poolLength: 2, + poolType: 'uniswapv3', + poolExtra: { + blockNumber: 0, + }, + }, + ], + [ + { + pool: '0x56534741cd8b152df6d48adf7ac51f75169a83b2', + tokenIn: '0xdac17f958d2ee523a2206206994597c13d831ec7', + tokenOut: '0x2260fac5e5542a773aa44fbcfedf7c193bc2c599', + limitReturnAmount: '0', + swapAmount: '216367866367', + amountOut: '224502436', + exchange: 'uniswapv3', + poolLength: 2, + poolType: 'uniswapv3', + poolExtra: { + blockNumber: 0, + }, + }, + ], + ], } + console.log(baseResponse) - // Return original response if conditions are not met or request fails return baseResponse }, }), diff --git a/src/utils/aggregationRouting.ts b/src/utils/aggregationRouting.ts index 6f9ba04dd4..e16b664729 100644 --- a/src/utils/aggregationRouting.ts +++ b/src/utils/aggregationRouting.ts @@ -46,15 +46,15 @@ function formatRoutesV2(routes: SwapRoute[]): SwapRouteV2[] { let itemIndex = -1 const routesGroup = routes.reduce((a, b) => { let index: number - let subRoutes: any[][] = [] + let subRoutes: SwapPool[][] = [] let swapPercentage: number = (b.pools && b.pools[0]?.swapPercentage) || 0 if (a[b.slug]) { const route: any = a[b.slug] || {} index = route.index const temp = route.subRoutes || [] swapPercentage += route.swapPercentage || 0 - temp.forEach((sub: any[], ind: number) => { - const swapPool: any = (b.pools && b.pools[ind]) || ({} as any) + temp.forEach((sub: SwapPool[], ind: number) => { + const swapPool: SwapPool = (b.pools && b.pools[ind]) || ({} as any) const totalSwapAmount = JSBI.add( sub.reduce((sum, x2) => JSBI.add(sum, x2.swapAmount || ZERO), ZERO), swapPool.swapAmount || ZERO, @@ -116,22 +116,72 @@ function formatRoutesV2(routes: SwapRoute[]): SwapRouteV2[] { } } +export interface SwapRouteV3 { + tokenIn: PathItem + tokenOut: PathItem + pool: string + swapAmount: string + amountOut: string + exchange: string + depth: number +} + export function getTradeComposition( chainId: ChainId, inputAmount: CurrencyAmount | undefined, involvingTokens: { [address: string]: Token | undefined } | undefined, swaps: Swap[][] | undefined, allTokens: { [address: string]: Token } | undefined, -): SwapRouteV2[] | undefined { +): SwapRouteV2[] | SwapRouteV3[] | undefined { if (!inputAmount || !swaps) { return undefined } const inputTokenAmount = inputAmount.wrapped - const tokens = involvingTokens || ({} as any) + const tokens = involvingTokens || {} const defaultToken = new Token(chainId, WETH[chainId].address, 0, '--', '--') const routes: SwapRoute[] = [] + const getTokenFromAddress = (address: string): PathItem => { + if (address.toLowerCase() === ETHER_ADDRESS.toLowerCase()) { + return NativeCurrencies[chainId].wrapped + } + + return ( + allTokens?.[isAddressString(address)] || + tokens[address] || + (isAddressString(address) ? new Token(chainId, isAddressString(address), 0, '--', '--') : defaultToken) + ) + } + + if (swaps.every(swap => swap.length === 1)) { + const tokenIn = inputAmount.currency.wrapped.address + const swapsWithDepth = swaps.map(s => ({ + ...s[0], + depth: s[0].tokenIn.toLowerCase() === tokenIn.toLowerCase() ? 1 : -1, + })) + + let currentLevel = 1 + while (swapsWithDepth.some(m => m.depth === -1)) { + const nextInputs = swapsWithDepth.filter(s => s.depth === currentLevel).map(item => item.tokenOut) + + for (const inputToken of nextInputs) { + for (let i = 0; i < swapsWithDepth.length; i++) { + if (swapsWithDepth[i].depth !== -1) continue + if (swapsWithDepth[i].tokenIn.toLowerCase() === inputToken.toLowerCase()) { + swapsWithDepth[i].depth = currentLevel + 1 + } + } + } + currentLevel++ + } + + return swapsWithDepth.map(swap => ({ + ...swap, + tokenIn: getTokenFromAddress(swap.tokenIn), + tokenOut: getTokenFromAddress(swap.tokenOut), + })) + } const calcSwapPercentage = function (tokenIn: string, amount: string): number | undefined { if (!tokenIn || !amount) { return undefined @@ -144,18 +194,6 @@ export function getTradeComposition( return undefined } - const getTokenFromAddress = (address: string) => { - if (address.toLowerCase() === ETHER_ADDRESS.toLowerCase()) { - return NativeCurrencies[chainId] - } - - return ( - allTokens?.[isAddressString(address)] || - tokens[address] || - (isAddressString(address) ? new Token(chainId, isAddressString(address), 0, '--', '--') : defaultToken) - ) - } - // Convert all Swaps to ChartSwaps swaps.forEach(sorMultiSwap => { if (!sorMultiSwap.length || sorMultiSwap.length < 1) { From 6b89c9d6f8394836752e764749d21aa68afab718 Mon Sep 17 00:00:00 2001 From: viet-nv Date: Mon, 13 Jan 2025 18:29:46 +0700 Subject: [PATCH 2/5] tmp --- package.json | 7 +- src/components/TradeRouting/RouteRowV3.tsx | 184 ++++++++++++++++++++- src/components/TradeRouting/index.tsx | 6 +- src/index.tsx | 4 +- yarn.lock | 111 ++++++++++++- 5 files changed, 296 insertions(+), 16 deletions(-) diff --git a/package.json b/package.json index 44e830110f..b775f7db18 100644 --- a/package.json +++ b/package.json @@ -51,8 +51,8 @@ "@kyberswap/ks-sdk-core": "1.1.7", "@kyberswap/ks-sdk-elastic": "^1.1.2", "@kyberswap/liquidity-widgets": "1.1.2", - "@kyberswap/zap-migration-widgets": "1.0.2", "@kyberswap/oauth2": "1.0.2", + "@kyberswap/zap-migration-widgets": "1.0.2", "@lingui/macro": "^4.6.0", "@lingui/react": "^4.6.0", "@popperjs/core": "^2.11.6", @@ -63,12 +63,15 @@ "@sentry/react": "^8.34.0", "@tanstack/react-query": "^5.52.1", "@use-gesture/react": "^10.2.27", + "@xyflow/react": "^12.3.6", "@zkmelabs/widget": "^0.1.3", "aos": "^2.3.4", "axios": "1.2.1", "base64-js": "^1.5.1", "buffer": "^6.0.3", "crypto-js": "4.1.1", + "cytoscape": "^3.30.4", + "cytoscape-dagre": "^2.5.0", "d3": "^7.6.1", "dayjs": "^1.11.6", "dompurify": "^3.0.6", @@ -152,6 +155,8 @@ "@types/aos": "^3.0.4", "@types/big.js": "^6.0.0", "@types/crypto-js": "4.1.1", + "@types/cytoscape": "^3.21.8", + "@types/cytoscape-dagre": "^2.3.3", "@types/d3": "^7.1.0", "@types/dompurify": "^3.0.3", "@types/mixpanel-browser": "^2.38.0", diff --git a/src/components/TradeRouting/RouteRowV3.tsx b/src/components/TradeRouting/RouteRowV3.tsx index e4db13591f..10adcd3bea 100644 --- a/src/components/TradeRouting/RouteRowV3.tsx +++ b/src/components/TradeRouting/RouteRowV3.tsx @@ -1,13 +1,179 @@ +import { Token } from '@kyberswap/ks-sdk-core' +import '@xyflow/react/dist/style.css' +import cytoscape from 'cytoscape' +import dagre from 'cytoscape-dagre' +import { useEffect } from 'react' + +import useTheme from 'hooks/useTheme' import { SwapRouteV3 } from 'utils/aggregationRouting' -export const RouteRowV3 = ({ tradeComposition }: { tradeComposition: SwapRouteV3[] }) => { - const firstSwaps = tradeComposition.filter(item => item.depth === 1) +cytoscape.use(dagre) + +type Node = { + id: string + data: { + label: string + } +} + +type Edge = { + id: string + source: string + target: string + animated: boolean +} + +export const RouteRowV3 = ({ + tradeComposition, + tokenIn, + tokenOut, +}: { + tradeComposition: SwapRouteV3[] + tokenIn: Token | undefined + tokenOut: Token | undefined +}) => { + const theme = useTheme() + useEffect(() => { + if (!tokenIn || !tokenOut) return + const tokens: { [key: string]: Token } = {} + tradeComposition.forEach(trade => { + tokens[trade.tokenIn.address.toLowerCase()] = trade.tokenIn + tokens[trade.tokenOut.address.toLowerCase()] = trade.tokenOut + }) + + const initNodes: Node[] = [ + { + id: tokenIn.address.toLowerCase(), + data: { label: tokenIn.symbol || '' }, + }, + ...Object.keys(tokens) + .filter( + addr => + addr.toLowerCase() !== tokenIn.address.toLowerCase() && + addr.toLowerCase() !== tokenOut.address.toLowerCase(), + ) + .map(addr => ({ + id: addr, + type: '', + data: { label: tokens[addr].symbol || '' }, + })), + { + id: tokenOut.address.toLowerCase(), + data: { label: tokenOut.symbol || '' }, + }, + ] + + const initEdges: Edge[] = [] + for (let i = 0; i < initNodes.length; i++) { + for (let j = i + 1; j < initNodes.length; j++) { + const swaps = tradeComposition.filter( + swap => + swap.tokenIn.address.toLowerCase() === initNodes[i].id && + swap.tokenOut.address.toLowerCase() === initNodes[j].id, + ) + + if (swaps.length) { + initEdges.push({ + id: `${initNodes[i].id}-${initNodes[j].id}`, + source: initNodes[i].id, + target: initNodes[j].id, + animated: true, + }) + } + } + } + + const cy = cytoscape({ + container: document.getElementById('cy'), // Specify the container ID + elements: [ + ...initNodes.map(item => ({ + data: { id: item.id, label: item.data.label }, + })), + + ...initEdges.map(item => ({ + data: { id: item.id, source: item.source, target: item.target }, + })), + ], + layout: { + name: 'dagre', + rankDir: 'LR', + } as any, + style: [ + { + selector: 'node', + style: { + 'background-color': theme.background, + color: theme.subText, + 'border-color': theme.border, + 'border-width': 1, + 'padding-top': '8px', + 'padding-left': '8px', + 'padding-bottom': '8px', + 'padding-right': '8px', + 'font-size': 12, + label: 'data(label)', + 'text-wrap': 'wrap', + 'text-valign': 'center', + 'text-halign': 'center', + width: 'max-content', + shape: 'roundrectangle', + }, + }, + { + selector: 'edge', + style: { + width: 3, + 'line-color': '#888', + 'target-arrow-color': '#888', + 'target-arrow-shape': 'triangle', + 'curve-style': 'bezier', // Taxi edge style + }, + }, + { + selector: 'edge.round-taxi', + style: { + 'curve-style': 'taxi', + 'taxi-direction': 'horizontal', + 'taxi-turn': 20, + 'taxi-turn-min-distance': 5, + }, + }, + ], + }) + + // Clean up Cytoscape instance on unmount + return () => cy.destroy() + }, [tradeComposition]) + + if (!tokenIn || !tokenOut) return null + + return
- return ( - <> - {firstSwaps.map((swap, index) => { - return
{swap.exchange}
- })} - - ) + //return ( + // <> + // {groups.map((group, index) => { + // if (group.tokenOut.toLowerCase() === tokenOut.address.toLowerCase()) + // return ( + //
+ // {tokenOut.symbol} {level} + //
+ // ) + // + // return ( + //
+ //
+ // {group.swaps[0].tokenOut.symbol} {' -> '}{' '} + //
+ // + // + //
+ // ) + // })} + // + //) } diff --git a/src/components/TradeRouting/index.tsx b/src/components/TradeRouting/index.tsx index f2707461fa..c7d4120367 100644 --- a/src/components/TradeRouting/index.tsx +++ b/src/components/TradeRouting/index.tsx @@ -191,7 +191,11 @@ const Routing = ({ {isSwapRouteV3 ? ( - + ) : ( (tradeComposition as SwapRouteV2[]).map(route => { return ( diff --git a/src/index.tsx b/src/index.tsx index 1627347244..723c88050b 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -116,7 +116,7 @@ const hideLoader = () => { const ReactApp = () => { useEffect(hideLoader, []) return ( - + <> @@ -131,7 +131,7 @@ const ReactApp = () => { - + ) } diff --git a/yarn.lock b/yarn.lock index 3846aa0b12..d7c0887866 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6609,6 +6609,18 @@ resolved "https://registry.yarnpkg.com/@types/crypto-js/-/crypto-js-4.1.1.tgz#602859584cecc91894eb23a4892f38cfa927890d" integrity sha512-BG7fQKZ689HIoc5h+6D2Dgq1fABRa0RbBWKBd9SP/MVRVXROflpm5fhwyATX5duFmbStzyzyycPB8qUYKDH3NA== +"@types/cytoscape-dagre@^2.3.3": + version "2.3.3" + resolved "https://registry.yarnpkg.com/@types/cytoscape-dagre/-/cytoscape-dagre-2.3.3.tgz#27f7c57e6e055a05d8f971cb09e76e11c837734c" + integrity sha512-FJBsNMbBZpqNwT6rp5leVYMevWUjnyD1QS8erNMAMWoBifvaVUklXIjE+bllLDSowjM3abXuRvljliSXUU+d1A== + dependencies: + "@types/cytoscape" "*" + +"@types/cytoscape@*", "@types/cytoscape@^3.21.8": + version "3.21.8" + resolved "https://registry.yarnpkg.com/@types/cytoscape/-/cytoscape-3.21.8.tgz#6166a2eabd66d3ae3259024875e037492adb8db6" + integrity sha512-6Bo9ZDrv0vfwe8Sg/ERc5VL0yU0gYvP4dgZi0fAXYkKHfyHaNqWRMcwYm3mu4sLsXbB8ZuXE75sR7qnaOL5JgQ== + "@types/d3-array@*": version "3.0.3" resolved "https://registry.yarnpkg.com/@types/d3-array/-/d3-array-3.0.3.tgz#87d990bf504d14ad6b16766979d04e943c046dac" @@ -6668,6 +6680,13 @@ dependencies: "@types/d3-selection" "*" +"@types/d3-drag@^3.0.7": + version "3.0.7" + resolved "https://registry.yarnpkg.com/@types/d3-drag/-/d3-drag-3.0.7.tgz#b13aba8b2442b4068c9a9e6d1d82f8bcea77fc02" + integrity sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ== + dependencies: + "@types/d3-selection" "*" + "@types/d3-dsv@*": version "3.0.0" resolved "https://registry.yarnpkg.com/@types/d3-dsv/-/d3-dsv-3.0.0.tgz#f3c61fb117bd493ec0e814856feb804a14cfc311" @@ -6763,6 +6782,11 @@ resolved "https://registry.yarnpkg.com/@types/d3-selection/-/d3-selection-3.0.3.tgz#57be7da68e7d9c9b29efefd8ea5a9ef1171e42ba" integrity sha512-Mw5cf6nlW1MlefpD9zrshZ+DAWL4IQ5LnWfRheW6xwsdaWOb6IRRu2H7XPAQcyXEx1D7XQWgdoKR83ui1/HlEA== +"@types/d3-selection@^3.0.10": + version "3.0.11" + resolved "https://registry.yarnpkg.com/@types/d3-selection/-/d3-selection-3.0.11.tgz#bd7a45fc0a8c3167a631675e61bc2ca2b058d4a3" + integrity sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w== + "@types/d3-shape@*": version "3.1.0" resolved "https://registry.yarnpkg.com/@types/d3-shape/-/d3-shape-3.1.0.tgz#1d87a6ddcf28285ef1e5c278ca4bdbc0658f3505" @@ -6806,6 +6830,13 @@ dependencies: "@types/d3-selection" "*" +"@types/d3-transition@^3.0.8": + version "3.0.9" + resolved "https://registry.yarnpkg.com/@types/d3-transition/-/d3-transition-3.0.9.tgz#1136bc57e9ddb3c390dccc9b5ff3b7d2b8d94706" + integrity sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg== + dependencies: + "@types/d3-selection" "*" + "@types/d3-zoom@*": version "3.0.1" resolved "https://registry.yarnpkg.com/@types/d3-zoom/-/d3-zoom-3.0.1.tgz#4bfc7e29625c4f79df38e2c36de52ec3e9faf826" @@ -6814,6 +6845,14 @@ "@types/d3-interpolate" "*" "@types/d3-selection" "*" +"@types/d3-zoom@^3.0.8": + version "3.0.8" + resolved "https://registry.yarnpkg.com/@types/d3-zoom/-/d3-zoom-3.0.8.tgz#dccb32d1c56b1e1c6e0f1180d994896f038bc40b" + integrity sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw== + dependencies: + "@types/d3-interpolate" "*" + "@types/d3-selection" "*" + "@types/d3@^7.1.0": version "7.4.0" resolved "https://registry.yarnpkg.com/@types/d3/-/d3-7.4.0.tgz#fc5cac5b1756fc592a3cf1f3dc881bf08225f515" @@ -8114,6 +8153,28 @@ resolved "https://registry.yarnpkg.com/@xobotyi/scrollbar-width/-/scrollbar-width-1.9.5.tgz#80224a6919272f405b87913ca13b92929bdf3c4d" integrity sha512-N8tkAACJx2ww8vFMneJmaAgmjAG1tnVBZJRLRcx061tmsLRZHSEZSLuGWnwPtunsSLvSqXQ2wfp7Mgqg1I+2dQ== +"@xyflow/react@^12.3.6": + version "12.3.6" + resolved "https://registry.yarnpkg.com/@xyflow/react/-/react-12.3.6.tgz#b122af1be35fcaaf78da79a3538f67fdd12b694e" + integrity sha512-9GS+cz8hDZahpvTrVCmySAEgKUL8oN4b2q1DluHrKtkqhAMWfH2s7kblhbM4Y4Y4SUnH2lt4drXKZ/4/Lot/2Q== + dependencies: + "@xyflow/system" "0.0.47" + classcat "^5.0.3" + zustand "^4.4.0" + +"@xyflow/system@0.0.47": + version "0.0.47" + resolved "https://registry.yarnpkg.com/@xyflow/system/-/system-0.0.47.tgz#c2443e6778ffae9af05b2dc61cb2145be5803405" + integrity sha512-aUXJPIvsCFxGX70ccRG8LPsR+A8ExYXfh/noYNpqn8udKerrLdSHxMG2VsvUrQ1PGex10fOpbJwFU4A+I/Xv8w== + dependencies: + "@types/d3-drag" "^3.0.7" + "@types/d3-selection" "^3.0.10" + "@types/d3-transition" "^3.0.8" + "@types/d3-zoom" "^3.0.8" + d3-drag "^3.0.0" + d3-selection "^3.0.0" + d3-zoom "^3.0.0" + "@yarnpkg/esbuild-plugin-pnp@^3.0.0-rc.10": version "3.0.0-rc.15" resolved "https://registry.yarnpkg.com/@yarnpkg/esbuild-plugin-pnp/-/esbuild-plugin-pnp-3.0.0-rc.15.tgz#4e40e7d2eb28825c9a35ab9d04c363931d7c0e67" @@ -9427,6 +9488,11 @@ class-variance-authority@^0.7.0: dependencies: clsx "2.0.0" +classcat@^5.0.3: + version "5.0.5" + resolved "https://registry.yarnpkg.com/classcat/-/classcat-5.0.5.tgz#8c209f359a93ac302404a10161b501eba9c09c77" + integrity sha512-JhZUT7JFcQy/EzW605k/ktHtncoo9vnyW/2GspNYwFlN1C/WmjuV/xtS04e9SOkL2sTdw0VAZ2UGCcQ9lR6p6w== + classlist-polyfill@^1.0.3: version "1.2.0" resolved "https://registry.yarnpkg.com/classlist-polyfill/-/classlist-polyfill-1.2.0.tgz#935bc2dfd9458a876b279617514638bcaa964a2e" @@ -10046,6 +10112,18 @@ cypress@12.17.3: untildify "^4.0.0" yauzl "^2.10.0" +cytoscape-dagre@^2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/cytoscape-dagre/-/cytoscape-dagre-2.5.0.tgz#47d9835ab64dd0b596d9c94731f070282f82fc5a" + integrity sha512-VG2Knemmshop4kh5fpLO27rYcyUaaDkRw+6PiX4bstpB+QFt0p2oauMrsjVbUamGWQ6YNavh7x2em2uZlzV44g== + dependencies: + dagre "^0.8.5" + +cytoscape@^3.30.4: + version "3.30.4" + resolved "https://registry.yarnpkg.com/cytoscape/-/cytoscape-3.30.4.tgz#3404da0a159c00a1a3df2c85b2b43fdc66a0e28e" + integrity sha512-OxtlZwQl1WbwMmLiyPSEBuzeTIQnwZhJYYWFzZ2PhEHVFwpeaqNIkUzSiso00D98qk60l8Gwon2RP304d3BJ1A== + "d3-array@2 - 3", "d3-array@2.10.0 - 3", "d3-array@2.5.0 - 3", d3-array@3, d3-array@^3.2.0: version "3.2.1" resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-3.2.1.tgz#39331ea706f5709417d31bbb6ec152e0328b39b3" @@ -10107,7 +10185,7 @@ d3-delaunay@6: resolved "https://registry.yarnpkg.com/d3-dispatch/-/d3-dispatch-3.0.1.tgz#5fc75284e9c2375c36c839411a0cf550cbfc4d5e" integrity sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg== -"d3-drag@2 - 3", d3-drag@3: +"d3-drag@2 - 3", d3-drag@3, d3-drag@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/d3-drag/-/d3-drag-3.0.0.tgz#994aae9cd23c719f53b5e10e3a0a6108c69607ba" integrity sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg== @@ -10213,7 +10291,7 @@ d3-scale@4, d3-scale@^4.0.2: d3-time "2.1.1 - 3" d3-time-format "2 - 4" -"d3-selection@2 - 3", d3-selection@3: +"d3-selection@2 - 3", d3-selection@3, d3-selection@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/d3-selection/-/d3-selection-3.0.0.tgz#c25338207efa72cc5b9bd1458a1a41901f1e1b31" integrity sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ== @@ -10262,7 +10340,7 @@ d3-shape@^3.1.0: d3-interpolate "1 - 3" d3-timer "1 - 3" -d3-zoom@3: +d3-zoom@3, d3-zoom@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/d3-zoom/-/d3-zoom-3.0.0.tgz#d13f4165c73217ffeaa54295cd6969b3e7aee8f3" integrity sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw== @@ -10345,6 +10423,14 @@ d3@^7.9.0: d3-transition "3" d3-zoom "3" +dagre@^0.8.5: + version "0.8.5" + resolved "https://registry.yarnpkg.com/dagre/-/dagre-0.8.5.tgz#ba30b0055dac12b6c1fcc247817442777d06afee" + integrity sha512-/aTqmnRta7x7MCCpExk7HQL2O4owCT2h8NT//9I1OQ9vt29Pa0BzSAkR5lwFUcQ7491yVi/3CXU9jQ5o0Mn2Sw== + dependencies: + graphlib "^2.1.8" + lodash "^4.17.15" + damerau-levenshtein@^1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz#b43d286ccbd36bc5b2f7ed41caf2d0aba1f8a6e7" @@ -12716,6 +12802,13 @@ grapheme-splitter@^1.0.4: resolved "https://registry.yarnpkg.com/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz#9cf3a665c6247479896834af35cf1dbb4400767e" integrity sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ== +graphlib@^2.1.8: + version "2.1.8" + resolved "https://registry.yarnpkg.com/graphlib/-/graphlib-2.1.8.tgz#5761d414737870084c92ec7b5dbcb0592c9d35da" + integrity sha512-jcLLfkpoVGmH7/InMC/1hIvOPSUh38oJtGhvrOFGzioE1DZ+0YW16RgmOJhHiuWTvGiJQ9Z1Ik43JvkRPRvE+A== + dependencies: + lodash "^4.17.15" + graphql-tag@^2.12.6: version "2.12.6" resolved "https://registry.yarnpkg.com/graphql-tag/-/graphql-tag-2.12.6.tgz#d441a569c1d2537ef10ca3d1633b48725329b5f1" @@ -19304,6 +19397,11 @@ use-sync-external-store@1.2.0: resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz#7dbefd6ef3fe4e767a0cf5d7287aacfb5846928a" integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA== +use-sync-external-store@^1.2.2: + version "1.4.0" + resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.4.0.tgz#adbc795d8eeb47029963016cefdf89dc799fcebc" + integrity sha512-9WXSPC5fMv61vaupRkCKCxsPxBocVnwakBEkMIHHpkTTg6icbJtg6jzgtLDm4bl3cSHAca52rYWih0k4K3PfHw== + user-home@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/user-home/-/user-home-2.0.0.tgz#9c70bfd8169bc1dcbf48604e0f04b8b49cde9e9f" @@ -20174,6 +20272,13 @@ zustand@5.0.0: resolved "https://registry.yarnpkg.com/zustand/-/zustand-5.0.0.tgz#71f8aaecf185592a3ba2743d7516607361899da9" integrity sha512-LE+VcmbartOPM+auOjCCLQOsQ05zUTp8RkgwRzefUk+2jISdMMFnxvyTjA4YNWr5ZGXYbVsEMZosttuxUBkojQ== +zustand@^4.4.0: + version "4.5.6" + resolved "https://registry.yarnpkg.com/zustand/-/zustand-4.5.6.tgz#6857d52af44874a79fb3408c9473f78367255c96" + integrity sha512-ibr/n1hBzLLj5Y+yUcU7dYw8p6WnIVzdJbnX+1YpaScvZVF2ziugqHs+LAmHw4lWO9c/zRj+K1ncgWDQuthEdQ== + dependencies: + use-sync-external-store "^1.2.2" + zustand@^5.0.0: version "5.0.2" resolved "https://registry.yarnpkg.com/zustand/-/zustand-5.0.2.tgz#f7595ada55a565f1fd6464f002a91e701ee0cfca" From 55a70d07096c42ee457eaceb040664b24e3bbbdb Mon Sep 17 00:00:00 2001 From: viet-nv Date: Wed, 15 Jan 2025 12:38:12 +0700 Subject: [PATCH 3/5] tmp --- package.json | 2 +- src/components/TradeRouting/RouteRowV3.tsx | 49 ++------------ yarn.lock | 77 +++------------------- 3 files changed, 16 insertions(+), 112 deletions(-) diff --git a/package.json b/package.json index b775f7db18..9ba4b16240 100644 --- a/package.json +++ b/package.json @@ -63,7 +63,6 @@ "@sentry/react": "^8.34.0", "@tanstack/react-query": "^5.52.1", "@use-gesture/react": "^10.2.27", - "@xyflow/react": "^12.3.6", "@zkmelabs/widget": "^0.1.3", "aos": "^2.3.4", "axios": "1.2.1", @@ -72,6 +71,7 @@ "crypto-js": "4.1.1", "cytoscape": "^3.30.4", "cytoscape-dagre": "^2.5.0", + "cytoscape-html": "^0.1.2", "d3": "^7.6.1", "dayjs": "^1.11.6", "dompurify": "^3.0.6", diff --git a/src/components/TradeRouting/RouteRowV3.tsx b/src/components/TradeRouting/RouteRowV3.tsx index 10adcd3bea..ac7ad812c0 100644 --- a/src/components/TradeRouting/RouteRowV3.tsx +++ b/src/components/TradeRouting/RouteRowV3.tsx @@ -1,13 +1,14 @@ import { Token } from '@kyberswap/ks-sdk-core' -import '@xyflow/react/dist/style.css' import cytoscape from 'cytoscape' import dagre from 'cytoscape-dagre' +import cytoscapeHTML from 'cytoscape-html' import { useEffect } from 'react' import useTheme from 'hooks/useTheme' import { SwapRouteV3 } from 'utils/aggregationRouting' cytoscape.use(dagre) +cytoscape.use(cytoscapeHTML) type Node = { id: string @@ -65,7 +66,7 @@ export const RouteRowV3 = ({ const initEdges: Edge[] = [] for (let i = 0; i < initNodes.length; i++) { - for (let j = i + 1; j < initNodes.length; j++) { + for (let j = 0; j < initNodes.length; j++) { const swaps = tradeComposition.filter( swap => swap.tokenIn.address.toLowerCase() === initNodes[i].id && @@ -122,20 +123,12 @@ export const RouteRowV3 = ({ { selector: 'edge', style: { - width: 3, - 'line-color': '#888', - 'target-arrow-color': '#888', + width: 1, + 'line-color': '#505050', + 'target-arrow-color': theme.primary, 'target-arrow-shape': 'triangle', 'curve-style': 'bezier', // Taxi edge style - }, - }, - { - selector: 'edge.round-taxi', - style: { - 'curve-style': 'taxi', - 'taxi-direction': 'horizontal', - 'taxi-turn': 20, - 'taxi-turn-min-distance': 5, + 'arrow-scale': 0.8, }, }, ], @@ -148,32 +141,4 @@ export const RouteRowV3 = ({ if (!tokenIn || !tokenOut) return null return
- - //return ( - // <> - // {groups.map((group, index) => { - // if (group.tokenOut.toLowerCase() === tokenOut.address.toLowerCase()) - // return ( - //
- // {tokenOut.symbol} {level} - //
- // ) - // - // return ( - //
- //
- // {group.swaps[0].tokenOut.symbol} {' -> '}{' '} - //
- // - // - //
- // ) - // })} - // - //) } diff --git a/yarn.lock b/yarn.lock index d7c0887866..6c84c5d555 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6680,13 +6680,6 @@ dependencies: "@types/d3-selection" "*" -"@types/d3-drag@^3.0.7": - version "3.0.7" - resolved "https://registry.yarnpkg.com/@types/d3-drag/-/d3-drag-3.0.7.tgz#b13aba8b2442b4068c9a9e6d1d82f8bcea77fc02" - integrity sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ== - dependencies: - "@types/d3-selection" "*" - "@types/d3-dsv@*": version "3.0.0" resolved "https://registry.yarnpkg.com/@types/d3-dsv/-/d3-dsv-3.0.0.tgz#f3c61fb117bd493ec0e814856feb804a14cfc311" @@ -6782,11 +6775,6 @@ resolved "https://registry.yarnpkg.com/@types/d3-selection/-/d3-selection-3.0.3.tgz#57be7da68e7d9c9b29efefd8ea5a9ef1171e42ba" integrity sha512-Mw5cf6nlW1MlefpD9zrshZ+DAWL4IQ5LnWfRheW6xwsdaWOb6IRRu2H7XPAQcyXEx1D7XQWgdoKR83ui1/HlEA== -"@types/d3-selection@^3.0.10": - version "3.0.11" - resolved "https://registry.yarnpkg.com/@types/d3-selection/-/d3-selection-3.0.11.tgz#bd7a45fc0a8c3167a631675e61bc2ca2b058d4a3" - integrity sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w== - "@types/d3-shape@*": version "3.1.0" resolved "https://registry.yarnpkg.com/@types/d3-shape/-/d3-shape-3.1.0.tgz#1d87a6ddcf28285ef1e5c278ca4bdbc0658f3505" @@ -6830,13 +6818,6 @@ dependencies: "@types/d3-selection" "*" -"@types/d3-transition@^3.0.8": - version "3.0.9" - resolved "https://registry.yarnpkg.com/@types/d3-transition/-/d3-transition-3.0.9.tgz#1136bc57e9ddb3c390dccc9b5ff3b7d2b8d94706" - integrity sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg== - dependencies: - "@types/d3-selection" "*" - "@types/d3-zoom@*": version "3.0.1" resolved "https://registry.yarnpkg.com/@types/d3-zoom/-/d3-zoom-3.0.1.tgz#4bfc7e29625c4f79df38e2c36de52ec3e9faf826" @@ -6845,14 +6826,6 @@ "@types/d3-interpolate" "*" "@types/d3-selection" "*" -"@types/d3-zoom@^3.0.8": - version "3.0.8" - resolved "https://registry.yarnpkg.com/@types/d3-zoom/-/d3-zoom-3.0.8.tgz#dccb32d1c56b1e1c6e0f1180d994896f038bc40b" - integrity sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw== - dependencies: - "@types/d3-interpolate" "*" - "@types/d3-selection" "*" - "@types/d3@^7.1.0": version "7.4.0" resolved "https://registry.yarnpkg.com/@types/d3/-/d3-7.4.0.tgz#fc5cac5b1756fc592a3cf1f3dc881bf08225f515" @@ -8153,28 +8126,6 @@ resolved "https://registry.yarnpkg.com/@xobotyi/scrollbar-width/-/scrollbar-width-1.9.5.tgz#80224a6919272f405b87913ca13b92929bdf3c4d" integrity sha512-N8tkAACJx2ww8vFMneJmaAgmjAG1tnVBZJRLRcx061tmsLRZHSEZSLuGWnwPtunsSLvSqXQ2wfp7Mgqg1I+2dQ== -"@xyflow/react@^12.3.6": - version "12.3.6" - resolved "https://registry.yarnpkg.com/@xyflow/react/-/react-12.3.6.tgz#b122af1be35fcaaf78da79a3538f67fdd12b694e" - integrity sha512-9GS+cz8hDZahpvTrVCmySAEgKUL8oN4b2q1DluHrKtkqhAMWfH2s7kblhbM4Y4Y4SUnH2lt4drXKZ/4/Lot/2Q== - dependencies: - "@xyflow/system" "0.0.47" - classcat "^5.0.3" - zustand "^4.4.0" - -"@xyflow/system@0.0.47": - version "0.0.47" - resolved "https://registry.yarnpkg.com/@xyflow/system/-/system-0.0.47.tgz#c2443e6778ffae9af05b2dc61cb2145be5803405" - integrity sha512-aUXJPIvsCFxGX70ccRG8LPsR+A8ExYXfh/noYNpqn8udKerrLdSHxMG2VsvUrQ1PGex10fOpbJwFU4A+I/Xv8w== - dependencies: - "@types/d3-drag" "^3.0.7" - "@types/d3-selection" "^3.0.10" - "@types/d3-transition" "^3.0.8" - "@types/d3-zoom" "^3.0.8" - d3-drag "^3.0.0" - d3-selection "^3.0.0" - d3-zoom "^3.0.0" - "@yarnpkg/esbuild-plugin-pnp@^3.0.0-rc.10": version "3.0.0-rc.15" resolved "https://registry.yarnpkg.com/@yarnpkg/esbuild-plugin-pnp/-/esbuild-plugin-pnp-3.0.0-rc.15.tgz#4e40e7d2eb28825c9a35ab9d04c363931d7c0e67" @@ -9488,11 +9439,6 @@ class-variance-authority@^0.7.0: dependencies: clsx "2.0.0" -classcat@^5.0.3: - version "5.0.5" - resolved "https://registry.yarnpkg.com/classcat/-/classcat-5.0.5.tgz#8c209f359a93ac302404a10161b501eba9c09c77" - integrity sha512-JhZUT7JFcQy/EzW605k/ktHtncoo9vnyW/2GspNYwFlN1C/WmjuV/xtS04e9SOkL2sTdw0VAZ2UGCcQ9lR6p6w== - classlist-polyfill@^1.0.3: version "1.2.0" resolved "https://registry.yarnpkg.com/classlist-polyfill/-/classlist-polyfill-1.2.0.tgz#935bc2dfd9458a876b279617514638bcaa964a2e" @@ -10119,6 +10065,11 @@ cytoscape-dagre@^2.5.0: dependencies: dagre "^0.8.5" +cytoscape-html@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/cytoscape-html/-/cytoscape-html-0.1.2.tgz#0197a7651786b2adfd93f52d269866284f27ece8" + integrity sha512-NtWYg70dZMpQKpP214UcGaSwfOYBqWxP7/1x06j+C8/FSDDfanbL83ri2dfvouDXpmDCI0XRfpj9Bl5qHsCufQ== + cytoscape@^3.30.4: version "3.30.4" resolved "https://registry.yarnpkg.com/cytoscape/-/cytoscape-3.30.4.tgz#3404da0a159c00a1a3df2c85b2b43fdc66a0e28e" @@ -10185,7 +10136,7 @@ d3-delaunay@6: resolved "https://registry.yarnpkg.com/d3-dispatch/-/d3-dispatch-3.0.1.tgz#5fc75284e9c2375c36c839411a0cf550cbfc4d5e" integrity sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg== -"d3-drag@2 - 3", d3-drag@3, d3-drag@^3.0.0: +"d3-drag@2 - 3", d3-drag@3: version "3.0.0" resolved "https://registry.yarnpkg.com/d3-drag/-/d3-drag-3.0.0.tgz#994aae9cd23c719f53b5e10e3a0a6108c69607ba" integrity sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg== @@ -10291,7 +10242,7 @@ d3-scale@4, d3-scale@^4.0.2: d3-time "2.1.1 - 3" d3-time-format "2 - 4" -"d3-selection@2 - 3", d3-selection@3, d3-selection@^3.0.0: +"d3-selection@2 - 3", d3-selection@3: version "3.0.0" resolved "https://registry.yarnpkg.com/d3-selection/-/d3-selection-3.0.0.tgz#c25338207efa72cc5b9bd1458a1a41901f1e1b31" integrity sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ== @@ -10340,7 +10291,7 @@ d3-shape@^3.1.0: d3-interpolate "1 - 3" d3-timer "1 - 3" -d3-zoom@3, d3-zoom@^3.0.0: +d3-zoom@3: version "3.0.0" resolved "https://registry.yarnpkg.com/d3-zoom/-/d3-zoom-3.0.0.tgz#d13f4165c73217ffeaa54295cd6969b3e7aee8f3" integrity sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw== @@ -19397,11 +19348,6 @@ use-sync-external-store@1.2.0: resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz#7dbefd6ef3fe4e767a0cf5d7287aacfb5846928a" integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA== -use-sync-external-store@^1.2.2: - version "1.4.0" - resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.4.0.tgz#adbc795d8eeb47029963016cefdf89dc799fcebc" - integrity sha512-9WXSPC5fMv61vaupRkCKCxsPxBocVnwakBEkMIHHpkTTg6icbJtg6jzgtLDm4bl3cSHAca52rYWih0k4K3PfHw== - user-home@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/user-home/-/user-home-2.0.0.tgz#9c70bfd8169bc1dcbf48604e0f04b8b49cde9e9f" @@ -20272,13 +20218,6 @@ zustand@5.0.0: resolved "https://registry.yarnpkg.com/zustand/-/zustand-5.0.0.tgz#71f8aaecf185592a3ba2743d7516607361899da9" integrity sha512-LE+VcmbartOPM+auOjCCLQOsQ05zUTp8RkgwRzefUk+2jISdMMFnxvyTjA4YNWr5ZGXYbVsEMZosttuxUBkojQ== -zustand@^4.4.0: - version "4.5.6" - resolved "https://registry.yarnpkg.com/zustand/-/zustand-4.5.6.tgz#6857d52af44874a79fb3408c9473f78367255c96" - integrity sha512-ibr/n1hBzLLj5Y+yUcU7dYw8p6WnIVzdJbnX+1YpaScvZVF2ziugqHs+LAmHw4lWO9c/zRj+K1ncgWDQuthEdQ== - dependencies: - use-sync-external-store "^1.2.2" - zustand@^5.0.0: version "5.0.2" resolved "https://registry.yarnpkg.com/zustand/-/zustand-5.0.2.tgz#f7595ada55a565f1fd6464f002a91e701ee0cfca" From c9d11203e0f43e1b6f45924c034ec764e77bded3 Mon Sep 17 00:00:00 2001 From: Nguyen Van Viet Date: Thu, 16 Jan 2025 13:53:02 +0700 Subject: [PATCH 4/5] Update .env.production Signed-off-by: Nguyen Van Viet --- .env.production | 3 +- src/components/TradeRouting/RouteRowV3.tsx | 2 + src/index.tsx | 4 +- src/pages/SwapV3/index.tsx | 9 +- src/services/route/index.ts | 413 ++------------------- 5 files changed, 41 insertions(+), 390 deletions(-) diff --git a/.env.production b/.env.production index f100f0f62b..dd62dc3472 100644 --- a/.env.production +++ b/.env.production @@ -1,4 +1,5 @@ -VITE_AGGREGATOR_API=https://aggregator-api.kyberswap.com +# VITE_AGGREGATOR_API=https://aggregator-api.kyberswap.com +VITE_AGGREGATOR_API=https://pre-router-api.kyberengineering.io VITE_AGGREGATOR_STATS_API=https://aggregator-stats.kyberswap.com VITE_SENTRY_DNS=https://264bd123c207e0c1c1edf63fdbdd86c1@sentry.ops.kyberengineering.io/3 diff --git a/src/components/TradeRouting/RouteRowV3.tsx b/src/components/TradeRouting/RouteRowV3.tsx index ac7ad812c0..b061e52156 100644 --- a/src/components/TradeRouting/RouteRowV3.tsx +++ b/src/components/TradeRouting/RouteRowV3.tsx @@ -136,6 +136,8 @@ export const RouteRowV3 = ({ // Clean up Cytoscape instance on unmount return () => cy.destroy() + + // eslint-disable-next-line }, [tradeComposition]) if (!tokenIn || !tokenOut) return null diff --git a/src/index.tsx b/src/index.tsx index 723c88050b..1627347244 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -116,7 +116,7 @@ const hideLoader = () => { const ReactApp = () => { useEffect(hideLoader, []) return ( - <> + @@ -131,7 +131,7 @@ const ReactApp = () => { - + ) } diff --git a/src/pages/SwapV3/index.tsx b/src/pages/SwapV3/index.tsx index b66c5a8fc5..df31e9aec2 100644 --- a/src/pages/SwapV3/index.tsx +++ b/src/pages/SwapV3/index.tsx @@ -153,14 +153,7 @@ export default function Swap() { }, [tabFromUrl, searchParams, setSearchParams]) const tradeRouteComposition = useMemo(() => { - return getTradeComposition( - chainId, - routeSummary?.parsedAmountIn, - undefined, - routeSummary?.route, - defaultTokens, - routeSummary?.tokenOut || '', - ) + return getTradeComposition(chainId, routeSummary?.parsedAmountIn, undefined, routeSummary?.route, defaultTokens) }, [chainId, defaultTokens, routeSummary]) const onBackToSwapTab = () => setActiveTab(getDefaultTab()) diff --git a/src/services/route/index.ts b/src/services/route/index.ts index 12e56260ff..6901a4c77e 100644 --- a/src/services/route/index.ts +++ b/src/services/route/index.ts @@ -36,390 +36,45 @@ const routeApi = createApi({ const { routeSummary } = baseResponse?.data || {} const { chainId, tokenInDecimals, tokenOutDecimals, tokenIn, tokenOut } = params || {} - // Ensure all necessary data is available - //if (baseResponse?.data?.routeSummary && routeSummary && chainId && tokenInDecimals && tokenOutDecimals) { - // const { amountIn, amountOut } = routeSummary - // - // // Build the URL for the price impact API request - // const priceImpactUrl = new URL(`${BFF_API}/v1/price-impact`) - // priceImpactUrl.searchParams.append('tokenIn', tokenIn) - // priceImpactUrl.searchParams.append('tokenInDecimal', tokenInDecimals.toString()) - // priceImpactUrl.searchParams.append('tokenOut', tokenOut) - // priceImpactUrl.searchParams.append('tokenOutDecimal', tokenOutDecimals.toString()) - // priceImpactUrl.searchParams.append('amountIn', amountIn) - // priceImpactUrl.searchParams.append('amountOut', amountOut) - // priceImpactUrl.searchParams.append('chainId', chainId.toString()) - // - // try { - // // Fetch price impact data - // const priceImpactResponse = await fetch(priceImpactUrl.toString()).then(res => res.json()) - // const { amountInUSD, amountOutUSD } = priceImpactResponse?.data || {} - // - // // Update routeSummary with USD values if available - // if (amountInUSD && amountOutUSD) { - // return { - // ...baseResponse, - // data: { - // ...baseResponse.data, - // routeSummary: { - // ...routeSummary, - // amountInUsd: amountInUSD, - // amountOutUsd: amountOutUSD, - // }, - // }, - // } - // } - // } catch (error) { - // console.error('Failed to fetch price impact:', error) - // } - //} + //Ensure all necessary data is available + if (baseResponse?.data?.routeSummary && routeSummary && chainId && tokenInDecimals && tokenOutDecimals) { + const { amountIn, amountOut } = routeSummary - // Return original response if conditions are not met or request fails - //return baseResponse + // Build the URL for the price impact API request + const priceImpactUrl = new URL(`${BFF_API}/v1/price-impact`) + priceImpactUrl.searchParams.append('tokenIn', tokenIn) + priceImpactUrl.searchParams.append('tokenInDecimal', tokenInDecimals.toString()) + priceImpactUrl.searchParams.append('tokenOut', tokenOut) + priceImpactUrl.searchParams.append('tokenOutDecimal', tokenOutDecimals.toString()) + priceImpactUrl.searchParams.append('amountIn', amountIn) + priceImpactUrl.searchParams.append('amountOut', amountOut) + priceImpactUrl.searchParams.append('chainId', chainId.toString()) - if (!baseResponse.data) return baseResponse - baseResponse.data.routeSummary = { - tokenIn: '0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0', - amountIn: '1000000000000000000000', - amountInUsd: '4352018.709992444', - tokenOut: '0x2260fac5e5542a773aa44fbcfedf7c193bc2c599', - amountOut: '4531365902', - amountOutUsd: '4382943.981611451', - gasUsd: '116.68998577404858', - route: [ - [ - { - pool: 'bebop_0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0_0x9f8f72aa9304c8b593d555f12ef6589cc3a579a2', - tokenIn: '0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0', - tokenOut: '0x9f8f72aa9304c8b593d555f12ef6589cc3a579a2', - limitReturnAmount: '0', - swapAmount: '50000000000000000000', - amountOut: '114442213243747581952', - exchange: 'bebop', - poolLength: 2, - poolType: 'bebop', - poolExtra: null, - }, - ], - [ - { - pool: '0x93d199263632a4ef4bb438f1feb99e57b4b5f0bd', - tokenIn: '0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0', - tokenOut: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', - limitReturnAmount: '0', - swapAmount: '250000000000000000000', - amountOut: '296542467421645344516', - exchange: 'balancer-v2-composable-stable', - poolLength: 3, - poolType: 'balancer-v2-composable-stable', - poolExtra: { - blockNumber: 21319890, - poolId: '0x93d199263632a4ef4bb438f1feb99e57b4b5f0bd0000000000000000000005c2', - tokenOutIndex: 2, - vault: '0xba12222222228d8ba445958a75a0704d566bf2c8', - }, - }, - ], - [ - { - pool: '0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0', - tokenIn: '0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0', - tokenOut: '0xae7ab96520de3a18e5e111b5eaab095312d7fe84', - limitReturnAmount: '0', - swapAmount: '400000000000000000000', - amountOut: '474626027414909852000', - exchange: 'lido', - poolLength: 2, - poolType: 'lido', - poolExtra: null, - }, - ], - [ - { - pool: '0x109830a1aaad605bbf02a9dfa7b0b92ec2fb7daa', - tokenIn: '0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0', - tokenOut: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', - limitReturnAmount: '0', - swapAmount: '150000000000000000000', - amountOut: '177919417229229300643', - exchange: 'uniswapv3', - poolLength: 2, - poolType: 'uniswapv3', - poolExtra: { - blockNumber: 0, - priceLimit: 4295128740, - }, - }, - ], - [ - { - pool: '0x0b1a513ee24972daef112bc777a5610d4325c9e7', - tokenIn: '0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0', - tokenOut: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', - limitReturnAmount: '0', - swapAmount: '150000000000000000000', - amountOut: '177927048177000000000', - exchange: 'fluid-dex-t1', - poolLength: 2, - poolType: 'fluid-dex-t1', - poolExtra: { - blockNumber: 21319890, - }, - }, - ], - [ - { - pool: '0xe8c6c9227491c0a8156a0106a0204d881bb7e531', - tokenIn: '0x9f8f72aa9304c8b593d555f12ef6589cc3a579a2', - tokenOut: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', - limitReturnAmount: '0', - swapAmount: '114442213243747581952', - amountOut: '66944420731774458985', - exchange: 'uniswapv3', - poolLength: 2, - poolType: 'uniswapv3', - poolExtra: { - blockNumber: 0, - priceLimit: 4306310044, - }, - }, - ], - [ - { - pool: '0xdc24316b9ae028f1497c275eb9192a3ea0f67022', - tokenIn: '0xae7ab96520de3a18e5e111b5eaab095312d7fe84', - tokenOut: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', - limitReturnAmount: '0', - swapAmount: '415297773988046120500', - amountOut: '415162858681867357755', - exchange: 'curve-stable-plain', - poolLength: 2, - poolType: 'curve-stable-plain', - poolExtra: { - TokenInIsNative: false, - TokenOutIsNative: true, - tokenInIndex: 1, - tokenOutIndex: 0, - underlying: false, - }, - }, - ], - [ - { - pool: 'kyber_pmm_0x2260fac5e5542a773aa44fbcfedf7c193bc2c599_0xae7ab96520de3a18e5e111b5eaab095312d7fe84', - tokenIn: '0xae7ab96520de3a18e5e111b5eaab095312d7fe84', - tokenOut: '0x2260fac5e5542a773aa44fbcfedf7c193bc2c599', - limitReturnAmount: '0', - swapAmount: '59328253426863731500', - amountOut: '225526762', - exchange: 'kyber-pmm', - poolLength: 2, - poolType: 'kyber-pmm', - poolExtra: { - timestamp: 1733203751, - }, - }, - ], - [ - { - pool: '0x4585fe77225b41b697c938b018e2ac67ac5a20c0', - tokenIn: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', - tokenOut: '0x2260fac5e5542a773aa44fbcfedf7c193bc2c599', - limitReturnAmount: '0', - swapAmount: '422796963697320913295', - amountOut: '1605175859', - exchange: 'uniswapv3', - poolLength: 2, - poolType: 'uniswapv3', - poolExtra: { - blockNumber: 0, - priceLimit: 1.4613005734278674e48, - }, - }, - ], - [ - { - pool: '0x11b815efb8f581194ae79006d24e0d814b7697f6', - tokenIn: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', - tokenOut: '0xdac17f958d2ee523a2206206994597c13d831ec7', - limitReturnAmount: '0', - swapAmount: '118619237329356120072', - amountOut: '433089706619', - exchange: 'uniswapv3', - poolLength: 2, - poolType: 'uniswapv3', - poolExtra: { - blockNumber: 0, - priceLimit: 4295558252, - }, - }, - ], - [ - { - pool: 'kyber_pmm_0x2260fac5e5542a773aa44fbcfedf7c193bc2c599_0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', - tokenIn: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', - tokenOut: '0x2260fac5e5542a773aa44fbcfedf7c193bc2c599', - limitReturnAmount: '0', - swapAmount: '118619536988929547734', - amountOut: '450987594', - exchange: 'kyber-pmm', - poolLength: 2, - poolType: 'kyber-pmm', - poolExtra: { - timestamp: 1733203751, - }, - }, - ], - [ - { - pool: '0x88e6a0c2ddd26feeb64f039a2c41296fcb3f5640', - tokenIn: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', - tokenOut: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', - limitReturnAmount: '0', - swapAmount: '177924678792959222266', - amountOut: '649603082233', - exchange: 'uniswapv3', - poolLength: 2, - poolType: 'uniswapv3', - poolExtra: { - blockNumber: 0, - priceLimit: 1.4613005734278674e48, - }, - }, - ], - [ - { - pool: 'kyber_pmm_0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2_0xdac17f958d2ee523a2206206994597c13d831ec7', - tokenIn: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', - tokenOut: '0xdac17f958d2ee523a2206206994597c13d831ec7', - limitReturnAmount: '0', - swapAmount: '177919417229229300642', - amountOut: '649229108600', - exchange: 'kyber-pmm', - poolLength: 2, - poolType: 'kyber-pmm', - poolExtra: { - timestamp: 1733203751, - }, - }, - ], - [ - { - pool: '0xcbcdf9626bc03e24f779434178a73a0b4bad62ed', - tokenIn: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', - tokenOut: '0x2260fac5e5542a773aa44fbcfedf7c193bc2c599', - limitReturnAmount: '0', - swapAmount: '118616378203721357890', - amountOut: '449504431', - exchange: 'uniswapv3', - poolLength: 2, - poolType: 'uniswapv3', - poolExtra: { - blockNumber: 0, - priceLimit: 1.4576520669498474e48, - }, - }, - ], - [ - { - pool: 'kyber_pmm_0x2260fac5e5542a773aa44fbcfedf7c193bc2c599_0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', - tokenIn: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', - tokenOut: '0x2260fac5e5542a773aa44fbcfedf7c193bc2c599', - limitReturnAmount: '0', - swapAmount: '216663329831', - amountOut: '225532718', - exchange: 'kyber-pmm', - poolLength: 2, - poolType: 'kyber-pmm', - poolExtra: { - timestamp: 1733203751, - }, - }, - ], - [ - { - pool: '0x9a772018fbd77fcd2d25657e5c547baff3fd7d16', - tokenIn: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', - tokenOut: '0x2260fac5e5542a773aa44fbcfedf7c193bc2c599', - limitReturnAmount: '0', - swapAmount: '216537519276', - amountOut: '225146915', - exchange: 'uniswapv3', - poolLength: 2, - poolType: 'uniswapv3', - poolExtra: { - blockNumber: 0, - }, - }, - ], - [ - { - pool: '0x99ac8ca7087fa4a2a1fb6357269965a2014abc35', - tokenIn: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', - tokenOut: '0x2260fac5e5542a773aa44fbcfedf7c193bc2c599', - limitReturnAmount: '0', - swapAmount: '216402233126', - amountOut: '224649112', - exchange: 'uniswapv3', - poolLength: 2, - poolType: 'uniswapv3', - poolExtra: { - blockNumber: 0, - }, - }, - ], - [ - { - pool: 'kyber_pmm_0x2260fac5e5542a773aa44fbcfedf7c193bc2c599_0xdac17f958d2ee523a2206206994597c13d831ec7', - tokenIn: '0xdac17f958d2ee523a2206206994597c13d831ec7', - tokenOut: '0x2260fac5e5542a773aa44fbcfedf7c193bc2c599', - limitReturnAmount: '0', - swapAmount: '216623912419', - amountOut: '225658416', - exchange: 'kyber-pmm', - poolLength: 2, - poolType: 'kyber-pmm', - poolExtra: { - timestamp: 1733203751, - }, - }, - ], - [ - { - pool: '0x9db9e0e53058c89e5b94e29621a205198648425b', - tokenIn: '0xdac17f958d2ee523a2206206994597c13d831ec7', - tokenOut: '0x2260fac5e5542a773aa44fbcfedf7c193bc2c599', - limitReturnAmount: '0', - swapAmount: '649327036433', - amountOut: '674681659', - exchange: 'uniswapv3', - poolLength: 2, - poolType: 'uniswapv3', - poolExtra: { - blockNumber: 0, - }, - }, - ], - [ - { - pool: '0x56534741cd8b152df6d48adf7ac51f75169a83b2', - tokenIn: '0xdac17f958d2ee523a2206206994597c13d831ec7', - tokenOut: '0x2260fac5e5542a773aa44fbcfedf7c193bc2c599', - limitReturnAmount: '0', - swapAmount: '216367866367', - amountOut: '224502436', - exchange: 'uniswapv3', - poolLength: 2, - poolType: 'uniswapv3', - poolExtra: { - blockNumber: 0, + try { + // Fetch price impact data + const priceImpactResponse = await fetch(priceImpactUrl.toString()).then(res => res.json()) + const { amountInUSD, amountOutUSD } = priceImpactResponse?.data || {} + + // Update routeSummary with USD values if available + if (amountInUSD && amountOutUSD) { + return { + ...baseResponse, + data: { + ...baseResponse.data, + routeSummary: { + ...routeSummary, + amountInUsd: amountInUSD, + amountOutUsd: amountOutUSD, + }, }, - }, - ], - ], + } + } + } catch (error) { + console.error('Failed to fetch price impact:', error) + } } - console.log(baseResponse) + //Return original response if conditions are not met or request fails return baseResponse }, }), From aa6768c2543ff83a2f943a43f820c8b062d47843 Mon Sep 17 00:00:00 2001 From: viet-nv Date: Thu, 20 Feb 2025 01:31:27 +0700 Subject: [PATCH 5/5] feat: handle merge route --- package.json | 5 +- src/components/TradeRouting/RouteRowV3.tsx | 206 ++++++++++++++++++--- src/components/TradeRouting/helpers.ts | 13 +- src/components/TradeRouting/index.tsx | 10 +- src/vite-env.d.ts | 11 ++ yarn.lock | 10 +- 6 files changed, 212 insertions(+), 43 deletions(-) diff --git a/package.json b/package.json index adb2a533b5..9a2ececb23 100644 --- a/package.json +++ b/package.json @@ -46,13 +46,14 @@ "@apollo/client": "^3.7.1", "@blocto/wagmi-connector": "^2.0.4", "@esbuild-plugins/node-globals-polyfill": "^0.2.3", + "@floating-ui/dom": "1.5.3", "@holdstation/paymaster-helper": "^2.0.20", "@kyberswap/ks-sdk-classic": "^1.0.3", "@kyberswap/ks-sdk-core": "1.1.8", "@kyberswap/ks-sdk-elastic": "^1.1.2", "@kyberswap/liquidity-widgets": "1.1.17", - "@kyberswap/zap-migration-widgets": "1.0.7", "@kyberswap/oauth2": "1.0.2", + "@kyberswap/zap-migration-widgets": "1.0.7", "@lingui/macro": "^4.6.0", "@lingui/react": "^4.6.0", "@popperjs/core": "^2.11.6", @@ -71,7 +72,7 @@ "crypto-js": "4.1.1", "cytoscape": "^3.30.4", "cytoscape-dagre": "^2.5.0", - "cytoscape-html": "^0.1.2", + "cytoscape-popper": "^4.0.1", "d3": "^7.6.1", "dayjs": "^1.11.6", "dompurify": "^3.0.6", diff --git a/src/components/TradeRouting/RouteRowV3.tsx b/src/components/TradeRouting/RouteRowV3.tsx index b061e52156..13f419e898 100644 --- a/src/components/TradeRouting/RouteRowV3.tsx +++ b/src/components/TradeRouting/RouteRowV3.tsx @@ -1,19 +1,53 @@ -import { Token } from '@kyberswap/ks-sdk-core' +import { computePosition, flip, limitShift, shift } from '@floating-ui/dom' +import { Percent, Token } from '@kyberswap/ks-sdk-core' import cytoscape from 'cytoscape' import dagre from 'cytoscape-dagre' -import cytoscapeHTML from 'cytoscape-html' +import cytoscapePopper from 'cytoscape-popper' +import { BigNumber } from 'ethers' import { useEffect } from 'react' +import { renderToString } from 'react-dom/server' +import { Flex } from 'rebass' +import { useActiveWeb3React } from 'hooks' import useTheme from 'hooks/useTheme' +import { useAllDexes } from 'state/customizeDexes/hooks' +import { WrappedTokenInfo } from 'state/lists/wrappedTokenInfo' +import { getEtherscanLink, isAddress } from 'utils' import { SwapRouteV3 } from 'utils/aggregationRouting' +import { getDexInfoByPool } from './helpers' + cytoscape.use(dagre) -cytoscape.use(cytoscapeHTML) + +cytoscape.use( + cytoscapePopper((ref, content, opts) => { + // see https://floating-ui.com/docs/computePosition#options + const popperOptions = { + // matching the default behaviour from Popper@2 + // https://floating-ui.com/docs/migration#configure-middleware + middleware: [flip(), shift({ limiter: limitShift() })], + ...opts, + } + + function update() { + computePosition(ref, content, popperOptions).then(({ x, y }) => { + Object.assign(content.style, { + left: `${x}px`, + top: `${y}px`, + transform: `translateY(-50%)`, + }) + }) + } + update() + return { update } + }), +) type Node = { id: string data: { label: string + logo?: string } } @@ -22,6 +56,8 @@ type Edge = { source: string target: string animated: boolean + label: string + swaps: SwapRouteV3[] } export const RouteRowV3 = ({ @@ -34,6 +70,9 @@ export const RouteRowV3 = ({ tokenOut: Token | undefined }) => { const theme = useTheme() + const { chainId } = useActiveWeb3React() + const allDexes = useAllDexes(chainId) + useEffect(() => { if (!tokenIn || !tokenOut) return const tokens: { [key: string]: Token } = {} @@ -45,7 +84,7 @@ export const RouteRowV3 = ({ const initNodes: Node[] = [ { id: tokenIn.address.toLowerCase(), - data: { label: tokenIn.symbol || '' }, + data: { label: tokenIn.symbol || '', logo: (tokenIn as WrappedTokenInfo).logoURI }, }, ...Object.keys(tokens) .filter( @@ -56,16 +95,20 @@ export const RouteRowV3 = ({ .map(addr => ({ id: addr, type: '', - data: { label: tokens[addr].symbol || '' }, + data: { label: tokens[addr].symbol || '', logo: (tokens[addr] as WrappedTokenInfo).logoURI }, })), { id: tokenOut.address.toLowerCase(), - data: { label: tokenOut.symbol || '' }, + data: { label: tokenOut.symbol || '', logo: (tokenOut as WrappedTokenInfo).logoURI }, }, ] const initEdges: Edge[] = [] for (let i = 0; i < initNodes.length; i++) { + const totalAmount = tradeComposition + .filter(swap => swap.tokenIn.address.toLowerCase() === initNodes[i].id) + .reduce((total, item) => total.add(BigNumber.from(item.swapAmount)), BigNumber.from(0)) + for (let j = 0; j < initNodes.length; j++) { const swaps = tradeComposition.filter( swap => @@ -73,12 +116,17 @@ export const RouteRowV3 = ({ swap.tokenOut.address.toLowerCase() === initNodes[j].id, ) + const swapAmount = swaps.reduce((total, item) => total.add(BigNumber.from(item.swapAmount)), BigNumber.from(0)) + const percent = new Percent(swapAmount.toString(), totalAmount.toString()) + if (swaps.length) { initEdges.push({ id: `${initNodes[i].id}-${initNodes[j].id}`, source: initNodes[i].id, target: initNodes[j].id, animated: true, + label: percent.toFixed(0) + '%', + swaps, }) } } @@ -86,13 +134,19 @@ export const RouteRowV3 = ({ const cy = cytoscape({ container: document.getElementById('cy'), // Specify the container ID + userZoomingEnabled: false, elements: [ ...initNodes.map(item => ({ - data: { id: item.id, label: item.data.label }, + data: { + id: item.id, + label: item.data.label, + href: getEtherscanLink(chainId, item.id, 'token'), + logo: item.data.logo, + }, })), ...initEdges.map(item => ({ - data: { id: item.id, source: item.source, target: item.target }, + data: { id: item.id, source: item.source, target: item.target, label: item.label, swaps: item.swaps }, })), ], layout: { @@ -103,22 +157,17 @@ export const RouteRowV3 = ({ { selector: 'node', style: { + label: 'data(label)', 'background-color': theme.background, color: theme.subText, - 'border-color': theme.border, - 'border-width': 1, - 'padding-top': '8px', - 'padding-left': '8px', - 'padding-bottom': '8px', - 'padding-right': '8px', - 'font-size': 12, - label: 'data(label)', - 'text-wrap': 'wrap', - 'text-valign': 'center', - 'text-halign': 'center', - width: 'max-content', - shape: 'roundrectangle', - }, + 'border-width': 0, + 'font-size': '6px', + 'background-image': 'data(logo)', + 'background-fit': 'contain', + 'background-image-crossorigin': 'null', + width: '16px', + height: '16px', + } as any, }, { selector: 'edge', @@ -127,20 +176,125 @@ export const RouteRowV3 = ({ 'line-color': '#505050', 'target-arrow-color': theme.primary, 'target-arrow-shape': 'triangle', - 'curve-style': 'bezier', // Taxi edge style - 'arrow-scale': 0.8, + 'arrow-scale': 0.6, + 'curve-style': 'straight', // Straight edges instead of bezier + }, + }, + + { + selector: 'edge[label]', + style: { + 'source-label': 'data(label)', + color: theme.primary, + 'font-size': '6px', + 'text-justification': 'left', + 'source-text-offset': 12, + 'source-text-rotation': 'autorotate', }, }, ], }) + const makeDiv = (data: Edge) => { + let div = document.getElementById(data.id) + if (!div) div = document.createElement('div') + div.style.border = `1px solid ${theme.border}` + div.style.background = theme.background + div.style.borderRadius = '8px' + div.style.padding = '0.25rem' + div.style.width = 'fit-content' + div.style.position = 'absolute' + div.style.zIndex = '2' + + div.id = data.id + + const totalAmount = data.swaps.reduce( + (total, item) => total.add(BigNumber.from(item.swapAmount)), + BigNumber.from(0), + ) + + div.innerHTML = renderToString( +
+ {data.swaps.map((swap, index) => { + const percent = new Percent(swap.swapAmount, totalAmount.toString()) + const dex = getDexInfoByPool(swap.exchange, allDexes) + const isStatic = !isAddress(chainId, swap.pool) + + return ( + + +
{dex?.name || swap.exchange}:
+
{percent.toFixed(0)}%
+
+ ) + })} +
, + ) + + const container = cy.container() + if (container) container.appendChild(div) + + return div + } + + cy.edges().forEach(edge => { + const popper = edge.popper({ + content: () => { + return makeDiv(edge.data()) + }, + }) + const updateAB = function () { + popper.update() + } + + edge.connectedNodes().on('position', updateAB) + cy.on('pan zoom resize', updateAB) + }) + cy.on('tap', 'node', event => { + try { + // your browser may block popups + window.open(event.target.data('href')) + } catch (e) { + // fall back on url change + window.location.href = event.target.data('href') + } + }) + cy.on('mouseover', 'node', evt => { + evt.target.css('color', theme.primary) + const c = evt.cy.container() + if (c) { + c.style.cursor = 'pointer' + } + }) + + cy.on('mouseout', 'node', evt => { + evt.target.css('color', theme.subText) + const c = evt.cy.container() + if (c) { + c.style.cursor = 'default' + } + }) + + cy.fit() + // Clean up Cytoscape instance on unmount return () => cy.destroy() // eslint-disable-next-line - }, [tradeComposition]) + }, [JSON.stringify(tradeComposition)]) if (!tokenIn || !tokenOut) return null - return
+ return
} diff --git a/src/components/TradeRouting/helpers.ts b/src/components/TradeRouting/helpers.ts index 29021f79a6..dae72d48f8 100644 --- a/src/components/TradeRouting/helpers.ts +++ b/src/components/TradeRouting/helpers.ts @@ -1,24 +1,23 @@ import useThrottle from 'hooks/useThrottle' import { Dex } from 'state/customizeDexes' -import { SwapPool } from 'utils/aggregationRouting' -export const getDexInfoByPool = (pool: SwapPool, allDexes?: Dex[]) => { - if (pool.exchange === '1inch') { +export const getDexInfoByPool = (exchange: string, allDexes?: Dex[]) => { + if (exchange === '1inch') { return { name: '1inch', logoURL: 'https://s2.coinmarketcap.com/static/img/coins/64x64/8104.png' } } - if (pool.exchange === 'paraswap') { + if (exchange === 'paraswap') { return { name: 'Paraswap', logoURL: 'https://s2.coinmarketcap.com/static/img/coins/64x64/14534.png' } } - if (pool.exchange === '0x') { + if (exchange === '0x') { return { name: '0x', logoURL: 'https://s2.coinmarketcap.com/static/img/coins/64x64/1896.png' } } return allDexes?.find( dex => - dex.id === pool.exchange || - ((pool.exchange === 'kyberswap' || pool.exchange === 'kyberswap-static') && dex.id === 'kyberswapv1'), // Mapping for kyberswap classic dex + dex.id === exchange || + ((exchange === 'kyberswap' || exchange === 'kyberswap-static') && dex.id === 'kyberswapv1'), // Mapping for kyberswap classic dex ) } diff --git a/src/components/TradeRouting/index.tsx b/src/components/TradeRouting/index.tsx index c7d4120367..38c0d45707 100644 --- a/src/components/TradeRouting/index.tsx +++ b/src/components/TradeRouting/index.tsx @@ -73,7 +73,7 @@ const RouteRow = ({ route, chainId, backgroundColor }: RouteRowProps) => { {Array.isArray(subRoute) ? subRoute.map(pool => { - const dex = getDexInfoByPool(pool, allDexes) + const dex = getDexInfoByPool(pool.exchange, allDexes) const poolId = pool.id.split('-')?.[0] const link = (i => { // TODO: Dungz remove condition @@ -188,8 +188,12 @@ const Routing = ({ {hasRoutes ? (
- - + {!isSwapRouteV3 && ( + <> + + + + )} {isSwapRouteV3 ? ( /// +import { ComputePositionConfig } from '@floating-ui/dom' + +declare module 'cytoscape-popper' { + interface PopperOptions extends ComputePositionConfig { + 'this param to prevent no-empty-interface eslint': false + } + + interface PopperInstance { + update(): void + } +} diff --git a/yarn.lock b/yarn.lock index 7c0c85f614..117c07060f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3316,7 +3316,7 @@ dependencies: "@floating-ui/utils" "^0.1.3" -"@floating-ui/dom@^1.5.1": +"@floating-ui/dom@1.5.3", "@floating-ui/dom@^1.5.1": version "1.5.3" resolved "https://registry.yarnpkg.com/@floating-ui/dom/-/dom-1.5.3.tgz#54e50efcb432c06c23cd33de2b575102005436fa" integrity sha512-ClAbQnEqJAKCJOEbbLo5IUlZHkNszqhuxS4fHAVxRPXPya6Ysf2G8KypnYcOTpx6I8xcgF9bbHb6g/2KpbV8qA== @@ -10108,10 +10108,10 @@ cytoscape-dagre@^2.5.0: dependencies: dagre "^0.8.5" -cytoscape-html@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/cytoscape-html/-/cytoscape-html-0.1.2.tgz#0197a7651786b2adfd93f52d269866284f27ece8" - integrity sha512-NtWYg70dZMpQKpP214UcGaSwfOYBqWxP7/1x06j+C8/FSDDfanbL83ri2dfvouDXpmDCI0XRfpj9Bl5qHsCufQ== +cytoscape-popper@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/cytoscape-popper/-/cytoscape-popper-4.0.1.tgz#259a582878621b7f68258e881e05d22c93f02c01" + integrity sha512-u2gYDMMvGSlAJbNfKvMljOM/p+BrHu0A4VcpELa+xJf54HoMn+nV0iuhALZx+O89b74SKJRy7jYo2WfkD5uvsw== cytoscape@^3.30.4: version "3.30.4"