diff --git a/apps/playground-web/src/components/ui/TokenSelector.tsx b/apps/playground-web/src/components/ui/TokenSelector.tsx index 2462f0a4c11..8d9266c4826 100644 --- a/apps/playground-web/src/components/ui/TokenSelector.tsx +++ b/apps/playground-web/src/components/ui/TokenSelector.tsx @@ -10,13 +10,7 @@ import { import { shortenAddress } from "thirdweb/utils"; import { Badge } from "@/components/ui/badge"; import { Img } from "@/components/ui/Img"; -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from "@/components/ui/select"; +import { SelectWithSearch } from "@/components/ui/select-with-search"; import { useAllChainsData } from "@/hooks/chains"; import { useTokensData } from "@/hooks/useTokensData"; import type { TokenMetadata } from "@/lib/types"; @@ -85,12 +79,41 @@ export function TokenSelector(props: { return value; }, [tokens]); + const options = useMemo(() => { + return tokens.map((token) => ({ + label: token.symbol, + value: `${token.chainId}:${token.address}`, + })); + }, [tokens]); + const selectedValue = props.selectedToken ? `${props.selectedToken.chainId}:${props.selectedToken.address}` : undefined; + const searchFn = useCallback( + (option: { label: string; value: string }, searchValue: string) => { + const token = addressChainToToken.get(option.value); + if (!token) { + return false; + } + + const searchLower = searchValue.toLowerCase(); + return ( + token.symbol.toLowerCase().includes(searchLower) || + token.name.toLowerCase().includes(searchLower) || + token.address.toLowerCase().includes(searchLower) + ); + }, + [addressChainToToken], + ); + const renderTokenOption = useCallback( - (token: TokenMetadata) => { + (option: { label: string; value: string }) => { + const token = addressChainToToken.get(option.value); + if (!token) { + return option.label; + } + const resolvedSrc = token.iconUri ? replaceIpfsUrl(token.iconUri, props.client) : fallbackChainIcon; @@ -121,11 +144,13 @@ export function TokenSelector(props: { ); }, - [props.disableAddress, props.client], + [props.disableAddress, props.client, addressChainToToken], ); return ( - + /> ); } diff --git a/packages/thirdweb/src/react/core/hooks/usePaymentMethods.ts b/packages/thirdweb/src/react/core/hooks/usePaymentMethods.ts index d633d0ab336..897f0ff9ad2 100644 --- a/packages/thirdweb/src/react/core/hooks/usePaymentMethods.ts +++ b/packages/thirdweb/src/react/core/hooks/usePaymentMethods.ts @@ -8,6 +8,7 @@ import { getClientFetch } from "../../../utils/fetch.js"; import { toTokens, toUnits } from "../../../utils/units.js"; import type { Wallet } from "../../../wallets/interfaces/wallet.js"; import type { PaymentMethod } from "../machines/paymentMachine.js"; +import type { SupportedTokens } from "../utils/defaultTokens.js"; import { useActiveWallet } from "./wallets/useActiveWallet.js"; /** @@ -33,6 +34,7 @@ export function usePaymentMethods(options: { client: ThirdwebClient; payerWallet?: Wallet; includeDestinationToken?: boolean; + supportedTokens?: SupportedTokens; }) { const { destinationToken, @@ -40,6 +42,7 @@ export function usePaymentMethods(options: { client, payerWallet, includeDestinationToken, + supportedTokens, } = options; const localWallet = useActiveWallet(); // TODO (bridge): get all connected wallets const wallet = payerWallet || localWallet; @@ -118,8 +121,28 @@ export function usePaymentMethods(options: { (a.originToken.prices.USD || 1) ); }); - // Move all sufficient balance quotes to the top - return [...sufficientBalanceQuotes, ...insufficientBalanceQuotes]; + + // Filter out quotes that are not included in the supportedTokens (if provided) + const tokensToInclude = supportedTokens + ? Object.keys(supportedTokens).flatMap( + (c: string) => + supportedTokens[Number(c)]?.map((t) => ({ + chainId: Number(c), + address: t.address, + })) ?? [], + ) + : []; + const finalQuotes = supportedTokens + ? [...sufficientBalanceQuotes, ...insufficientBalanceQuotes].filter( + (q) => + tokensToInclude.find( + (t) => + t.chainId === q.originToken.chainId && + t.address === q.originToken.address, + ), + ) + : [...sufficientBalanceQuotes, ...insufficientBalanceQuotes]; + return finalQuotes; }, queryKey: [ "payment-methods", @@ -128,6 +151,7 @@ export function usePaymentMethods(options: { destinationAmount, payerWallet?.getAccount()?.address, includeDestinationToken, + supportedTokens, ], // 5 minutes refetchOnWindowFocus: false, staleTime: 5 * 60 * 1000, diff --git a/packages/thirdweb/src/react/web/ui/Bridge/BridgeOrchestrator.tsx b/packages/thirdweb/src/react/web/ui/Bridge/BridgeOrchestrator.tsx index a01fe1d589e..bc1540d9b88 100644 --- a/packages/thirdweb/src/react/web/ui/Bridge/BridgeOrchestrator.tsx +++ b/packages/thirdweb/src/react/web/ui/Bridge/BridgeOrchestrator.tsx @@ -17,6 +17,7 @@ import { type PaymentMethod, usePaymentMachine, } from "../../../core/machines/paymentMachine.js"; +import type { SupportedTokens } from "../../../core/utils/defaultTokens.js"; import { webWindowAdapter } from "../../adapters/WindowAdapter.js"; import en from "../ConnectWallet/locale/en.js"; import type { ConnectLocale } from "../ConnectWallet/locale/types.js"; @@ -128,6 +129,7 @@ export interface BridgeOrchestratorProps { * @default true */ showThirdwebBranding?: boolean; + supportedTokens?: SupportedTokens; } export function BridgeOrchestrator({ @@ -144,6 +146,7 @@ export function BridgeOrchestrator({ presetOptions, paymentMethods = ["crypto", "card"], showThirdwebBranding = true, + supportedTokens, }: BridgeOrchestratorProps) { // Initialize adapters const adapters = useMemo( @@ -313,6 +316,7 @@ export function BridgeOrchestrator({ paymentMethods={paymentMethods} receiverAddress={state.context.receiverAddress} currency={uiOptions.currency} + supportedTokens={supportedTokens} /> )} diff --git a/packages/thirdweb/src/react/web/ui/Bridge/BuyWidget.tsx b/packages/thirdweb/src/react/web/ui/Bridge/BuyWidget.tsx index 5a3c052cf57..c4048a7cac7 100644 --- a/packages/thirdweb/src/react/web/ui/Bridge/BuyWidget.tsx +++ b/packages/thirdweb/src/react/web/ui/Bridge/BuyWidget.tsx @@ -398,6 +398,7 @@ export function BuyWidget(props: BuyWidgetProps) { // Show normal bridge orchestrator content = ( ); } diff --git a/packages/thirdweb/src/react/web/ui/Bridge/TransactionWidget.tsx b/packages/thirdweb/src/react/web/ui/Bridge/TransactionWidget.tsx index 830ab55a969..2b70601b4a9 100644 --- a/packages/thirdweb/src/react/web/ui/Bridge/TransactionWidget.tsx +++ b/packages/thirdweb/src/react/web/ui/Bridge/TransactionWidget.tsx @@ -409,6 +409,7 @@ export function TransactionWidget(props: TransactionWidgetProps) { // Show normal bridge orchestrator content = (