Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/cowswap-frontend/src/entities/bridgeProvider/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export { useBridgeSupportedNetworks, useBridgeSupportedNetwork } from './useBridgeSupportedNetworks'
export { useBridgeSupportedTokens } from './useBridgeSupportedTokens'
export { useRoutesAvailability } from './useRoutesAvailability'
export { BridgeProvidersUpdater } from './BridgeProvidersUpdater'
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import { useMemo } from 'react'

import { SWR_NO_REFRESH_OPTIONS } from '@cowprotocol/common-const'
import { useIsBridgingEnabled } from '@cowprotocol/common-hooks'
import { SupportedChainId } from '@cowprotocol/cow-sdk'

import useSWR from 'swr'
import { bridgingSdk } from 'tradingSdk/bridgingSdk'

import { useBridgeProvidersIds } from './useBridgeProvidersIds'

export interface RoutesAvailabilityResult {
unavailableChainIds: Set<number>
loadingChainIds: Set<number>
isLoading: boolean
}

const EMPTY_RESULT: RoutesAvailabilityResult = {
unavailableChainIds: new Set(),
loadingChainIds: new Set(),
isLoading: false,
}

interface RouteCheckResult {
chainId: number
isAvailable: boolean
}

/**
* Pre-checks route availability for multiple destination chains from a source chain.
* Returns which chains have unavailable routes and which are still loading.
*
* Note: Fires parallel requests for all destination chains without throttling.
* This is acceptable because:
* - SWR caches results, so repeated opens don't re-fetch
* - Chain count is limited (~10-15 max)
* - Requests are lightweight (token existence checks)
* If this becomes a bottleneck, consider batching or sequential fetching.
*/
export function useRoutesAvailability(
sourceChainId: SupportedChainId | undefined,
destinationChainIds: number[],
): RoutesAvailabilityResult {
const isBridgingEnabled = useIsBridgingEnabled()
const providerIds = useBridgeProvidersIds()
const providersKey = providerIds.join('|')

// Filter out the source chain (same-chain swaps are always available)
const chainsToCheck = useMemo(
() => destinationChainIds.filter((id) => id !== sourceChainId),
[destinationChainIds, sourceChainId],
)

// Create a stable key for the SWR request
const swrKey = useMemo(() => {
if (!isBridgingEnabled || !sourceChainId || chainsToCheck.length === 0) {
return null
}
return [sourceChainId, chainsToCheck.sort().join(','), providersKey, 'useRoutesAvailability']
}, [isBridgingEnabled, sourceChainId, chainsToCheck, providersKey])

const { data, isLoading } = useSWR<RouteCheckResult[]>(
swrKey,
async (key) => {
const [sellChainId, chainIdsString] = key as [SupportedChainId, string, string, string]
const chainIds = chainIdsString.split(',').map(Number)

// Check routes in parallel for all destination chains
const results = await Promise.all(
chainIds.map(async (buyChainId: number): Promise<RouteCheckResult> => {
try {
const result = await bridgingSdk.getBuyTokens({ sellChainId, buyChainId })
const isAvailable = result.tokens.length > 0 && result.isRouteAvailable
return { chainId: buyChainId, isAvailable }
} catch (error) {
console.warn(`[useRoutesAvailability] Failed to check route ${sellChainId} -> ${buyChainId}`, error)
// Treat errors as unavailable routes
return { chainId: buyChainId, isAvailable: false }
}
}),
)

return results
},
SWR_NO_REFRESH_OPTIONS,
)

return useMemo(() => {
if (!swrKey) {
return EMPTY_RESULT
}

if (isLoading || !data) {
// While loading, mark all chains being checked as loading
return {
unavailableChainIds: new Set(),
loadingChainIds: new Set(chainsToCheck),
isLoading: true,
}
}

const unavailableChainIds = new Set<number>(
data.filter((result) => !result.isAvailable).map((result) => result.chainId),
)

return {
unavailableChainIds,
loadingChainIds: new Set(),
isLoading: false,
}
}, [swrKey, isLoading, data, chainsToCheck])
}
4 changes: 4 additions & 0 deletions apps/cowswap-frontend/src/locales/es-ES.po
Original file line number Diff line number Diff line change
Expand Up @@ -6379,3 +6379,7 @@ msgstr "Ver todas las {totalChains} redes"
#: apps/cowswap-frontend/src/modules/tokensList/pure/SelectTokenModal/MobileChainSelector.tsx
msgid "Selected network {activeChainLabel}"
msgstr "Red seleccionada {activeChainLabel}"

#: apps/cowswap-frontend/src/modules/tokensList/pure/ChainsSelector/index.tsx
msgid "This destination is not supported for this source chain"
msgstr "Este destino no es compatible con esta red de origen"
4 changes: 4 additions & 0 deletions apps/cowswap-frontend/src/locales/ru-RU.po
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Сеть назначения не доступна для выбранной исходной сети"

Original file line number Diff line number Diff line change
Expand Up @@ -6379,3 +6379,7 @@ msgstr "Показать все сети ({totalChains})"
#: apps/cowswap-frontend/src/modules/tokensList/pure/SelectTokenModal/MobileChainSelector.tsx
msgid "Selected network {activeChainLabel}"
msgstr "Выбранная сеть {activeChainLabel}"

#: apps/cowswap-frontend/src/modules/tokensList/pure/ChainsSelector/index.tsx
msgid "This destination is not supported for this source chain"
msgstr "Сеть назначения не доступна для выбранной исходной сети"
Loading