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
5 changes: 3 additions & 2 deletions src/hooks/useBridgeFees.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ export function useBridgeFees(
externalProjectId?: string,
_recipientAddress?: string,
isUniversalSwap?: boolean,
universalSwapQuote?: UniversalSwapQuote
universalSwapQuote?: UniversalSwapQuote,
enabled: boolean = true
) {
const didUniversalSwapLoad = isUniversalSwap && !!universalSwapQuote;
const bridgeInputTokenSymbol = didUniversalSwapLoad
Expand Down Expand Up @@ -87,7 +88,7 @@ export function useBridgeFees(
? getBridgeFeesWithExternalProjectId(externalProjectIdToQuery, feeArgs)
: getBridgeFees(feeArgs);
},
enabled: Boolean(amount.gt(0)),
enabled: enabled && Boolean(amount.gt(0)),
refetchInterval: 5000,
retry: (_, error) => {
if (
Expand Down
21 changes: 12 additions & 9 deletions src/hooks/useBridgeLimits.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,18 @@ export function useBridgeLimits(
fromChainId?: ChainId,
toChainId?: ChainId,
isUniversalSwap?: boolean,
universalSwapQuote?: UniversalSwapQuote
universalSwapQuote?: UniversalSwapQuote,
enabled: boolean = true
) {
const enabled = !!(
inputTokenSymbol &&
outputTokenSymbol &&
fromChainId &&
toChainId &&
(isUniversalSwap ? !!universalSwapQuote : true)
);
const queryEnabled =
enabled &&
!!(
inputTokenSymbol &&
outputTokenSymbol &&
fromChainId &&
toChainId &&
(isUniversalSwap ? !!universalSwapQuote : true)
);
const didUniversalSwapLoad = isUniversalSwap && !!universalSwapQuote;
const bridgeInputTokenSymbol = didUniversalSwapLoad
? universalSwapQuote.steps.bridge.tokenIn.symbol
Expand Down Expand Up @@ -78,7 +81,7 @@ export function useBridgeLimits(
toChainIdToQuery
);
},
enabled,
enabled: queryEnabled,
refetchInterval: 300_000, // 5 minutes
});
return {
Expand Down
68 changes: 0 additions & 68 deletions src/hooks/useEnrichedCrosschainBalances.ts

This file was deleted.

5 changes: 3 additions & 2 deletions src/hooks/useTokenInput.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import { useCallback, useEffect, useState } from "react";
import { BigNumber, utils } from "ethers";
import { convertTokenToUSD, convertUSDToToken } from "utils";
import { EnrichedToken } from "views/SwapAndBridge/components/ChainTokenSelector/ChainTokenSelectorModal";

import { formatUnitsWithMaxFractions } from "utils";
import { TokenWithBalance } from "views/SwapAndBridge/hooks/useSwapAndBridgeTokens";

export type UnitType = "usd" | "token";

type UseTokenInputProps = {
token: EnrichedToken | null;
token: TokenWithBalance | null;
setAmount: (amount: BigNumber | null) => void;
expectedAmount: string | undefined;
shouldUpdate: boolean;
Expand Down
2 changes: 1 addition & 1 deletion src/utils/token.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { BigNumber, ethers } from "ethers";
import { LifiToken } from "hooks/useAvailableCrosschainRoutes";
import { LifiToken } from "views/SwapAndBridge/hooks/useAvailableCrosschainRoutes";

import {
getProvider,
Expand All @@ -12,7 +12,7 @@
import { SwapToken } from "utils/serverless-api/types";
import { TokenInfo } from "constants/tokens";
import {
CHAIN_IDs,

Check warning on line 15 in src/utils/token.ts

View workflow job for this annotation

GitHub Actions / format-and-lint

'CHAIN_IDs' is defined but never used
chainsWithUsdt0Enabled,
getToken,
tokenTable,
Expand Down
23 changes: 16 additions & 7 deletions src/views/Bridge/hooks/useTransferQuote.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,14 @@ export function useTransferQuote(
amount: BigNumber,
swapSlippage: number,
fromAddress?: string,
toAddress?: string
toAddress?: string,
isEnabled: boolean = true
) {
const [initialQuoteTime, setInitialQuoteTime] = useState<
number | undefined
>();
const isSwapRoute = selectedRoute.type === "swap";
const isUniversalSwapRoute = selectedRoute.type === "universal-swap";
const isSwapRoute = selectedRoute?.type === "swap";
const isUniversalSwapRoute = selectedRoute?.type === "universal-swap";
const swapQuoteQuery = useSwapQuoteQuery({
// Setting `swapTokenSymbol` to undefined will disable the query
swapTokenSymbol: isSwapRoute ? selectedRoute.swapTokenSymbol : undefined,
Expand Down Expand Up @@ -72,17 +73,24 @@ export function useTransferQuote(
selectedRoute.externalProjectId,
toAddress,
isUniversalSwapRoute,
universalSwapQuoteQuery.data
universalSwapQuoteQuery.data,
isEnabled
);
const limitsQuery = useBridgeLimits(
selectedRoute.fromTokenSymbol,
selectedRoute.toTokenSymbol,
selectedRoute.fromChain,
selectedRoute.toChain,
isUniversalSwapRoute,
universalSwapQuoteQuery.data
universalSwapQuoteQuery.data,
isEnabled
);
const usdPriceQuery = useCoingeckoPrice(
selectedRoute.l1TokenAddress,
"usd",
undefined,
isEnabled
);
const usdPriceQuery = useCoingeckoPrice(selectedRoute.l1TokenAddress, "usd");

const transferQuoteQuery = useQuery({
queryKey: [
Expand All @@ -100,7 +108,8 @@ export function useTransferQuote(
selectedRoute.type,
],
enabled: Boolean(
feesQuery.fees &&
isEnabled &&
feesQuery.fees &&
limitsQuery.limits &&
usdPriceQuery.data?.price &&
// If it's a swap route, we also need to wait for the swap quote to be fetched
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import styled from "@emotion/styled";
import { Searchbar } from "./Searchbar";
import TokenMask from "assets/mask/token-mask-corner.svg";
import { LifiToken } from "hooks/useAvailableCrosschainRoutes";
import { LifiToken } from "views/SwapAndBridge/hooks/useAvailableCrosschainRoutes";
import {
CHAIN_IDs,
ChainInfo,
Expand All @@ -22,11 +22,13 @@
import { ReactComponent as WarningIcon } from "assets/icons/warning_triangle.svg";
import { ReactComponent as LinkExternalIcon } from "assets/icons/arrow-up-right-boxed.svg";
import AllChainsIcon from "assets/chain-logos/all-swap-chain.png";
import { useEnrichedCrosschainBalances } from "hooks/useEnrichedCrosschainBalances";
import useCurrentBreakpoint from "hooks/useCurrentBreakpoint";
import { BigNumber } from "ethers";
import { Text, TokenImage } from "components";
import { useHotkeys } from "react-hotkeys-hook";
import {
useSwapAndBridgeTokens,
TokenWithBalance,
} from "views/SwapAndBridge/hooks/useSwapAndBridgeTokens";

const popularChains = [
CHAIN_IDs.MAINNET,
Expand All @@ -53,27 +55,21 @@
all: ChainData[];
};

export type EnrichedToken = LifiToken & {
balance: BigNumber;
balanceUsd: number;
routeSource: "bridge" | "swap";
};

type EnrichedTokenWithReachability = EnrichedToken & {
type TokenWithBalanceWithReachability = TokenWithBalance & {
Copy link
Contributor

Choose a reason for hiding this comment

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

Perhaps TokenWithBalanceAndReachability?

isUnreachable: boolean;
};

type DisplayedTokens = {
popular: EnrichedTokenWithReachability[];
all: EnrichedTokenWithReachability[];
popular: TokenWithBalanceWithReachability[];
all: TokenWithBalanceWithReachability[];
};

type Props = {
onSelect: (token: EnrichedToken) => void;
onSelectOtherToken?: (token: EnrichedToken | null) => void; // Callback to reset the other selector
onSelect: (token: TokenWithBalance) => void;
onSelectOtherToken?: (token: TokenWithBalance | null) => void; // Callback to reset the other selector
isOriginToken: boolean;
currentToken?: EnrichedToken | null; // The currently selected token we're changing from
otherToken?: EnrichedToken | null; // The currently selected token on the other side
currentToken?: TokenWithBalance | null; // The currently selected token we're changing from
otherToken?: TokenWithBalance | null; // The currently selected token on the other side
displayModal: boolean;
setDisplayModal: (displayModal: boolean) => void;
};
Expand All @@ -87,7 +83,17 @@
currentToken,
otherToken,
}: Props) {
const crossChainRoutes = useEnrichedCrosschainBalances();
const { data: crossChainRoutes } = useSwapAndBridgeTokens(
isOriginToken
? {
outputToken: otherToken,
isInput: true,
}
: {
inputToken: otherToken,
isOutput: true,
}
);
const { isMobile } = useCurrentBreakpoint();

const [selectedChain, setSelectedChain] = useState<number | null>(
Expand All @@ -107,33 +113,32 @@
}, [displayModal, currentToken]);

const displayedTokens = useMemo(() => {
let tokens = selectedChain ? (crossChainRoutes[selectedChain] ?? []) : [];
let tokens = selectedChain ? (crossChainRoutes?.[selectedChain] ?? []) : [];

if (tokens.length === 0 && selectedChain === null) {
tokens = Object.values(crossChainRoutes).flatMap((t) => t);
tokens = Object.values(crossChainRoutes ?? {}).flatMap((t) => t);
}

// Enrich tokens with route source information and unreachable flag
const enrichedTokens = tokens.map((token) => {
// The hook (useSwapAndBridgeTokens) already handles all unreachability logic:
// - Same chain check (input tokens on same chain as output, or output tokens on same chain as input)
// - Bridge-only check (swap-only tokens when output is bridge-only)
const TokenWithBalances = tokens.map((token) => {
// Find the corresponding token in crossChainRoutes to get route source
const routeToken = crossChainRoutes?.[token.chainId]?.find(
(rt) => rt.address.toLowerCase() === token.address.toLowerCase()
);

// Token is unreachable if otherToken exists and is from the same chain
const isUnreachable = otherToken
? token.chainId === otherToken.chainId
: false;

return {
...token,
routeSource: routeToken?.routeSource || "bridge", // Default to bridge if not found
isUnreachable,
routeSource: routeToken?.routeSource || token.routeSource || ["bridge"], // Use token's routeSource if available, otherwise default to bridge
// Use the hook's isUnreachable value directly - it handles all cases
isUnreachable: token.isUnreachable ?? false,
};
});

// Filter by search first
const filteredTokens = enrichedTokens.filter((t) => {
const filteredTokens = TokenWithBalances.filter((t) => {
if (tokenSearch === "") {
return true;
}
Expand All @@ -152,7 +157,7 @@
});

// Sort function that prioritizes tokens with balance, then by balance amount, then alphabetically
const sortTokens = (tokens: EnrichedTokenWithReachability[]) => {
const sortTokens = (tokens: TokenWithBalanceWithReachability[]) => {
return tokens.sort((a, b) => {
// Sort by token balance - tokens with balance go to top
const aHasTokenBalance = a.balance.gt(0);
Expand Down Expand Up @@ -261,7 +266,7 @@
popular: popularChainsData,
all: allChainsData,
} as DisplayedChains;
}, [chainSearch, crossChainRoutes, otherToken]);

Check warning on line 269 in src/views/SwapAndBridge/components/ChainTokenSelector/ChainTokenSelectorModal.tsx

View workflow job for this annotation

GitHub Actions / format-and-lint

React Hook useMemo has an unnecessary dependency: 'otherToken'. Either exclude it or remove the dependency array

return isMobile ? (
<MobileModal
Expand Down Expand Up @@ -334,8 +339,8 @@
displayedChains: DisplayedChains;
displayedTokens: DisplayedTokens;
onChainSelect: (chainId: number | null) => void;
onTokenSelect: (token: EnrichedToken) => void;
onSelectOtherToken?: (token: EnrichedToken | null) => void;
onTokenSelect: (token: TokenWithBalance) => void;
onSelectOtherToken?: (token: TokenWithBalance | null) => void;
}) => {
return (
<Modal
Expand Down Expand Up @@ -414,8 +419,8 @@
displayedChains: DisplayedChains;
displayedTokens: DisplayedTokens;
onChainSelect: (chainId: number | null) => void;
onTokenSelect: (token: EnrichedToken) => void;
onSelectOtherToken?: (token: EnrichedToken | null) => void;
onTokenSelect: (token: TokenWithBalance) => void;
onSelectOtherToken?: (token: TokenWithBalance | null) => void;
}) => {
return (
<Modal
Expand Down Expand Up @@ -453,7 +458,7 @@

// Mobile Layout Component - 2-step process
const MobileLayout = ({
isOriginToken,

Check warning on line 461 in src/views/SwapAndBridge/components/ChainTokenSelector/ChainTokenSelectorModal.tsx

View workflow job for this annotation

GitHub Actions / format-and-lint

'isOriginToken' is defined but never used. Allowed unused args must match /^_/u
mobileStep,
selectedChain,
chainSearch,
Expand All @@ -477,8 +482,8 @@
displayedChains: DisplayedChains;
displayedTokens: DisplayedTokens;
onChainSelect: (chainId: number | null) => void;
onTokenSelect: (token: EnrichedToken) => void;
onSelectOtherToken?: (token: EnrichedToken | null) => void;
onTokenSelect: (token: TokenWithBalance) => void;
onSelectOtherToken?: (token: TokenWithBalance | null) => void;
onModalClose: () => void;
}) => {
const chainSearchInputRef = useRef<HTMLInputElement>(null);
Expand Down Expand Up @@ -638,8 +643,8 @@
displayedChains: DisplayedChains;
displayedTokens: DisplayedTokens;
onChainSelect: (chainId: number | null) => void;
onTokenSelect: (token: EnrichedToken) => void;
onSelectOtherToken?: (token: EnrichedToken | null) => void;
onTokenSelect: (token: TokenWithBalance) => void;
onSelectOtherToken?: (token: TokenWithBalance | null) => void;
onModalClose: () => void;
}) => {
const chainSearchInputRef = useRef<HTMLInputElement>(null);
Expand Down Expand Up @@ -851,7 +856,7 @@
tabIndex,
warningMessage,
}: {
token: EnrichedTokenWithReachability;
token: TokenWithBalanceWithReachability;
isSelected: boolean;
onClick: () => void;
warningMessage: string;
Expand Down
Loading
Loading