diff --git a/.changeset/fallback-native-balance.md b/.changeset/fallback-native-balance.md new file mode 100644 index 00000000000..72b19d85941 --- /dev/null +++ b/.changeset/fallback-native-balance.md @@ -0,0 +1,13 @@ +--- +"thirdweb": patch +--- + +Add fallback mechanism to usePaymentMethods hook for getOwnedTokens failures + +When getOwnedTokens batches fail in the usePaymentMethods hook, the system now falls back to getting native token balances for each chain using getWalletBalance. This ensures users can still access their native tokens as payment methods even when the insight API is experiencing issues, providing a more resilient user experience. + +The fallback mechanism: +- Catches getOwnedTokens failures and logs warnings +- Falls back to native balance fetching using getWalletBalance for each chain +- Transforms results to match the expected format +- Continues normal processing flow seamlessly \ No newline at end of file diff --git a/packages/thirdweb/src/react/core/hooks/usePaymentMethods.ts b/packages/thirdweb/src/react/core/hooks/usePaymentMethods.ts index 7898aead16f..10b220ed27f 100644 --- a/packages/thirdweb/src/react/core/hooks/usePaymentMethods.ts +++ b/packages/thirdweb/src/react/core/hooks/usePaymentMethods.ts @@ -10,6 +10,10 @@ import type { ThirdwebClient } from "../../../client/client.js"; import { getOwnedTokens } from "../../../insight/get-tokens.js"; import { toTokens } from "../../../utils/units.js"; import type { Wallet } from "../../../wallets/interfaces/wallet.js"; +import { + type GetWalletBalanceResult, + getWalletBalance, +} from "../../../wallets/utils/getWalletBalance.js"; import type { PaymentMethod } from "../machines/paymentMachine.js"; import { useActiveWallet } from "./wallets/useActiveWallet.js"; @@ -80,16 +84,42 @@ export function usePaymentMethods(options: { const limit = 500; while (true) { - const batch = await getOwnedTokens({ - chains: insightEnabledChains.map((c) => getCachedChain(c.chainId)), - client, - ownerAddress: wallet.getAccount()?.address || "", - queryOptions: { - limit, - metadata: "false", - page, - }, - }); + let batch: GetWalletBalanceResult[]; + try { + batch = await getOwnedTokens({ + chains: insightEnabledChains.map((c) => getCachedChain(c.chainId)), + client, + ownerAddress: wallet.getAccount()?.address || "", + queryOptions: { + limit, + metadata: "false", + page, + }, + }); + } catch (error) { + // If the batch fails, fall back to getting native balance for each chain + console.warn(`Failed to get owned tokens for batch ${page}:`, error); + + const chainsInBatch = insightEnabledChains.map((c) => + getCachedChain(c.chainId), + ); + const nativeBalances = await Promise.allSettled( + chainsInBatch.map(async (chain) => { + const balance = await getWalletBalance({ + address: wallet.getAccount()?.address || "", + chain, + client, + }); + return balance; + }), + ); + + // Transform successful native balances into the same format as getOwnedTokens results + batch = nativeBalances + .filter((result) => result.status === "fulfilled") + .map((result) => result.value) + .filter((balance) => balance.value > 0n); + } if (batch.length === 0) { break; @@ -126,7 +156,9 @@ export function usePaymentMethods(options: { // Add destination token if included if (includeDestinationToken) { - const tokenKey = `${destinationToken.chainId}-${destinationToken.address.toLowerCase()}`; + const tokenKey = `${ + destinationToken.chainId + }-${destinationToken.address.toLowerCase()}`; allValidOriginTokens.set(tokenKey, destinationToken); } @@ -155,7 +187,9 @@ export function usePaymentMethods(options: { ) { continue; } - const tokenKey = `${route.originToken.chainId}-${route.originToken.address.toLowerCase()}`; + const tokenKey = `${ + route.originToken.chainId + }-${route.originToken.address.toLowerCase()}`; allValidOriginTokens.set(tokenKey, route.originToken); } } catch (error) { @@ -169,7 +203,9 @@ export function usePaymentMethods(options: { const validOwnedTokens: OwnedTokenWithQuote[] = []; for (const ownedToken of allOwnedTokens) { - const tokenKey = `${ownedToken.originToken.chainId}-${ownedToken.originToken.address.toLowerCase()}`; + const tokenKey = `${ + ownedToken.originToken.chainId + }-${ownedToken.originToken.address.toLowerCase()}`; const validOriginToken = allValidOriginTokens.get(tokenKey); if (validOriginToken) {