Skip to content

Commit 7617cc8

Browse files
authored
[SDK] Fix: Respect supported tokens in payment widgets (#7780)
1 parent e1ad7da commit 7617cc8

File tree

7 files changed

+84
-33
lines changed

7 files changed

+84
-33
lines changed

apps/playground-web/src/components/ui/TokenSelector.tsx

Lines changed: 46 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,7 @@ import {
1010
import { shortenAddress } from "thirdweb/utils";
1111
import { Badge } from "@/components/ui/badge";
1212
import { Img } from "@/components/ui/Img";
13-
import {
14-
Select,
15-
SelectContent,
16-
SelectItem,
17-
SelectTrigger,
18-
SelectValue,
19-
} from "@/components/ui/select";
13+
import { SelectWithSearch } from "@/components/ui/select-with-search";
2014
import { useAllChainsData } from "@/hooks/chains";
2115
import { useTokensData } from "@/hooks/useTokensData";
2216
import type { TokenMetadata } from "@/lib/types";
@@ -85,12 +79,41 @@ export function TokenSelector(props: {
8579
return value;
8680
}, [tokens]);
8781

82+
const options = useMemo(() => {
83+
return tokens.map((token) => ({
84+
label: token.symbol,
85+
value: `${token.chainId}:${token.address}`,
86+
}));
87+
}, [tokens]);
88+
8889
const selectedValue = props.selectedToken
8990
? `${props.selectedToken.chainId}:${props.selectedToken.address}`
9091
: undefined;
9192

93+
const searchFn = useCallback(
94+
(option: { label: string; value: string }, searchValue: string) => {
95+
const token = addressChainToToken.get(option.value);
96+
if (!token) {
97+
return false;
98+
}
99+
100+
const searchLower = searchValue.toLowerCase();
101+
return (
102+
token.symbol.toLowerCase().includes(searchLower) ||
103+
token.name.toLowerCase().includes(searchLower) ||
104+
token.address.toLowerCase().includes(searchLower)
105+
);
106+
},
107+
[addressChainToToken],
108+
);
109+
92110
const renderTokenOption = useCallback(
93-
(token: TokenMetadata) => {
111+
(option: { label: string; value: string }) => {
112+
const token = addressChainToToken.get(option.value);
113+
if (!token) {
114+
return option.label;
115+
}
116+
94117
const resolvedSrc = token.iconUri
95118
? replaceIpfsUrl(token.iconUri, props.client)
96119
: fallbackChainIcon;
@@ -121,11 +144,13 @@ export function TokenSelector(props: {
121144
</div>
122145
);
123146
},
124-
[props.disableAddress, props.client],
147+
[props.disableAddress, props.client, addressChainToToken],
125148
);
126149

127150
return (
128-
<Select
151+
<SelectWithSearch
152+
className={cn("w-full", props.className)}
153+
closeOnSelect={true}
129154
disabled={tokensQuery.isLoading || props.disabled}
130155
onValueChange={(tokenAddress) => {
131156
const token = addressChainToToken.get(tokenAddress);
@@ -134,27 +159,17 @@ export function TokenSelector(props: {
134159
}
135160
props.onChange(token);
136161
}}
162+
options={options}
163+
overrideSearchFn={searchFn}
164+
placeholder={
165+
tokensQuery.isLoading
166+
? "Loading Tokens..."
167+
: props.placeholder || "Select Token"
168+
}
169+
renderOption={renderTokenOption}
170+
searchPlaceholder="Search by Symbol, Name or Address"
171+
showCheck={false}
137172
value={selectedValue}
138-
>
139-
<SelectTrigger className={cn("w-full", props.className)}>
140-
<SelectValue
141-
placeholder={
142-
tokensQuery.isLoading
143-
? "Loading Tokens..."
144-
: props.placeholder || "Select Token"
145-
}
146-
/>
147-
</SelectTrigger>
148-
<SelectContent>
149-
{tokens.map((token) => {
150-
const value = `${token.chainId}:${token.address}`;
151-
return (
152-
<SelectItem key={value} value={value}>
153-
{renderTokenOption(token)}
154-
</SelectItem>
155-
);
156-
})}
157-
</SelectContent>
158-
</Select>
173+
/>
159174
);
160175
}

packages/thirdweb/src/react/core/hooks/usePaymentMethods.ts

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { getClientFetch } from "../../../utils/fetch.js";
88
import { toTokens, toUnits } from "../../../utils/units.js";
99
import type { Wallet } from "../../../wallets/interfaces/wallet.js";
1010
import type { PaymentMethod } from "../machines/paymentMachine.js";
11+
import type { SupportedTokens } from "../utils/defaultTokens.js";
1112
import { useActiveWallet } from "./wallets/useActiveWallet.js";
1213

1314
/**
@@ -33,13 +34,15 @@ export function usePaymentMethods(options: {
3334
client: ThirdwebClient;
3435
payerWallet?: Wallet;
3536
includeDestinationToken?: boolean;
37+
supportedTokens?: SupportedTokens;
3638
}) {
3739
const {
3840
destinationToken,
3941
destinationAmount,
4042
client,
4143
payerWallet,
4244
includeDestinationToken,
45+
supportedTokens,
4346
} = options;
4447
const localWallet = useActiveWallet(); // TODO (bridge): get all connected wallets
4548
const wallet = payerWallet || localWallet;
@@ -118,8 +121,28 @@ export function usePaymentMethods(options: {
118121
(a.originToken.prices.USD || 1)
119122
);
120123
});
121-
// Move all sufficient balance quotes to the top
122-
return [...sufficientBalanceQuotes, ...insufficientBalanceQuotes];
124+
125+
// Filter out quotes that are not included in the supportedTokens (if provided)
126+
const tokensToInclude = supportedTokens
127+
? Object.keys(supportedTokens).flatMap(
128+
(c: string) =>
129+
supportedTokens[Number(c)]?.map((t) => ({
130+
chainId: Number(c),
131+
address: t.address,
132+
})) ?? [],
133+
)
134+
: [];
135+
const finalQuotes = supportedTokens
136+
? [...sufficientBalanceQuotes, ...insufficientBalanceQuotes].filter(
137+
(q) =>
138+
tokensToInclude.find(
139+
(t) =>
140+
t.chainId === q.originToken.chainId &&
141+
t.address === q.originToken.address,
142+
),
143+
)
144+
: [...sufficientBalanceQuotes, ...insufficientBalanceQuotes];
145+
return finalQuotes;
123146
},
124147
queryKey: [
125148
"payment-methods",
@@ -128,6 +151,7 @@ export function usePaymentMethods(options: {
128151
destinationAmount,
129152
payerWallet?.getAccount()?.address,
130153
includeDestinationToken,
154+
supportedTokens,
131155
], // 5 minutes
132156
refetchOnWindowFocus: false,
133157
staleTime: 5 * 60 * 1000,

packages/thirdweb/src/react/web/ui/Bridge/BridgeOrchestrator.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
type PaymentMethod,
1818
usePaymentMachine,
1919
} from "../../../core/machines/paymentMachine.js";
20+
import type { SupportedTokens } from "../../../core/utils/defaultTokens.js";
2021
import { webWindowAdapter } from "../../adapters/WindowAdapter.js";
2122
import en from "../ConnectWallet/locale/en.js";
2223
import type { ConnectLocale } from "../ConnectWallet/locale/types.js";
@@ -128,6 +129,7 @@ export interface BridgeOrchestratorProps {
128129
* @default true
129130
*/
130131
showThirdwebBranding?: boolean;
132+
supportedTokens?: SupportedTokens;
131133
}
132134

133135
export function BridgeOrchestrator({
@@ -144,6 +146,7 @@ export function BridgeOrchestrator({
144146
presetOptions,
145147
paymentMethods = ["crypto", "card"],
146148
showThirdwebBranding = true,
149+
supportedTokens,
147150
}: BridgeOrchestratorProps) {
148151
// Initialize adapters
149152
const adapters = useMemo(
@@ -313,6 +316,7 @@ export function BridgeOrchestrator({
313316
paymentMethods={paymentMethods}
314317
receiverAddress={state.context.receiverAddress}
315318
currency={uiOptions.currency}
319+
supportedTokens={supportedTokens}
316320
/>
317321
)}
318322

packages/thirdweb/src/react/web/ui/Bridge/BuyWidget.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,7 @@ export function BuyWidget(props: BuyWidgetProps) {
398398
// Show normal bridge orchestrator
399399
content = (
400400
<BridgeOrchestrator
401+
supportedTokens={props.supportedTokens}
401402
client={props.client}
402403
connectLocale={localeQuery.data}
403404
connectOptions={props.connectOptions}

packages/thirdweb/src/react/web/ui/Bridge/CheckoutWidget.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,7 @@ export function CheckoutWidget(props: CheckoutWidgetProps) {
369369
receiverAddress={props.seller}
370370
showThirdwebBranding={props.showThirdwebBranding}
371371
uiOptions={bridgeDataQuery.data.data}
372+
supportedTokens={props.supportedTokens}
372373
/>
373374
);
374375
}

packages/thirdweb/src/react/web/ui/Bridge/TransactionWidget.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -409,6 +409,7 @@ export function TransactionWidget(props: TransactionWidgetProps) {
409409
// Show normal bridge orchestrator
410410
content = (
411411
<BridgeOrchestrator
412+
supportedTokens={props.supportedTokens}
412413
client={props.client}
413414
connectLocale={localeQuery.data}
414415
connectOptions={props.connectOptions}

packages/thirdweb/src/react/web/ui/Bridge/payment-selection/PaymentSelection.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { usePaymentMethods } from "../../../../core/hooks/usePaymentMethods.js";
1313
import { useActiveWallet } from "../../../../core/hooks/wallets/useActiveWallet.js";
1414
import { useConnectedWallets } from "../../../../core/hooks/wallets/useConnectedWallets.js";
1515
import type { PaymentMethod } from "../../../../core/machines/paymentMachine.js";
16+
import type { SupportedTokens } from "../../../../core/utils/defaultTokens.js";
1617
import type { ConnectLocale } from "../../ConnectWallet/locale/types.js";
1718
import { WalletSwitcherConnectionScreen } from "../../ConnectWallet/screens/WalletSwitcherConnectionScreen.js";
1819
import { Container, ModalHeader } from "../../components/basic.js";
@@ -89,6 +90,8 @@ export interface PaymentSelectionProps {
8990
* @default "USD"
9091
*/
9192
currency?: SupportedFiatCurrency;
93+
94+
supportedTokens?: SupportedTokens;
9295
}
9396

9497
type Step =
@@ -109,6 +112,7 @@ export function PaymentSelection({
109112
connectLocale,
110113
includeDestinationToken,
111114
paymentMethods = ["crypto", "card"],
115+
supportedTokens,
112116
feePayer,
113117
currency,
114118
}: PaymentSelectionProps) {
@@ -149,6 +153,7 @@ export function PaymentSelection({
149153
receiverAddress?.toLowerCase() !==
150154
payerWallet?.getAccount()?.address?.toLowerCase(),
151155
payerWallet,
156+
supportedTokens,
152157
});
153158

154159
// Handle error from usePaymentMethods

0 commit comments

Comments
 (0)