From 9364b511b58f802aadc71a3aebf2bcac98031928 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 12 Mar 2026 06:54:14 +0000 Subject: [PATCH 1/2] [SDK] Add hiddenOnrampProviders prop to BuyWidget and BridgeWidget - Add hiddenOnrampProviders prop to BuyWidget and BridgeWidget components - Filter onramp providers (Coinbase, Stripe, Transak) based on hidden list - Update useBuyWithFiatQuotesForProviders hook to accept provider list - Add comprehensive tests for provider filtering - Allows developers to customize which payment providers are shown to users Co-authored-by: Yash --- .../pay/useBuyWithFiatQuotesForProviders.ts | 8 +- .../src/react/web/ui/Bridge/BuyWidget.tsx | 16 +++ .../ui/Bridge/bridge-widget/bridge-widget.tsx | 12 ++ .../FiatProviderSelection.test.tsx | 131 ++++++++++++++++++ .../FiatProviderSelection.tsx | 32 +++-- .../payment-selection/PaymentSelection.tsx | 7 + 6 files changed, 193 insertions(+), 13 deletions(-) create mode 100644 packages/thirdweb/src/react/web/ui/Bridge/payment-selection/FiatProviderSelection.test.tsx diff --git a/packages/thirdweb/src/react/core/hooks/pay/useBuyWithFiatQuotesForProviders.ts b/packages/thirdweb/src/react/core/hooks/pay/useBuyWithFiatQuotesForProviders.ts index 62cec3c4558..3f858205a99 100644 --- a/packages/thirdweb/src/react/core/hooks/pay/useBuyWithFiatQuotesForProviders.ts +++ b/packages/thirdweb/src/react/core/hooks/pay/useBuyWithFiatQuotesForProviders.ts @@ -64,12 +64,16 @@ type UseBuyWithFiatQuotesForProvidersResult = { */ export function useBuyWithFiatQuotesForProviders( params?: UseBuyWithFiatQuotesForProvidersParams, + providers?: ("coinbase" | "stripe" | "transak")[], queryOptions?: OnrampQuoteQueryOptions, ): UseBuyWithFiatQuotesForProvidersResult { - const providers = ["coinbase", "stripe", "transak"] as const; + const providersToQuery = + providers && providers.length > 0 + ? providers + : (["coinbase", "stripe", "transak"] as const); const queries = useQueries({ - queries: providers.map((provider) => ({ + queries: providersToQuery.map((provider) => ({ ...queryOptions, enabled: !!params, queryFn: async () => { diff --git a/packages/thirdweb/src/react/web/ui/Bridge/BuyWidget.tsx b/packages/thirdweb/src/react/web/ui/Bridge/BuyWidget.tsx index 6f4be0889fb..decd06e5101 100644 --- a/packages/thirdweb/src/react/web/ui/Bridge/BuyWidget.tsx +++ b/packages/thirdweb/src/react/web/ui/Bridge/BuyWidget.tsx @@ -223,6 +223,20 @@ export type BuyWidgetProps = { * By default the token selection is editable. Set this to false to disable editing the token selection. */ tokenEditable?: boolean; + + /** + * Array of onramp provider IDs to hide from the payment provider selection. + * @example + * ```tsx + * + * ``` + */ + hiddenOnrampProviders?: ("coinbase" | "stripe" | "transak")[]; }; /** @@ -395,6 +409,7 @@ export function BuyWidget(props: BuyWidgetProps) { ? true : props.showThirdwebBranding } + hiddenOnrampProviders={props.hiddenOnrampProviders} /> ); @@ -558,6 +573,7 @@ function BridgeWidgetContent( currency={props.currency} supportedTokens={props.supportedTokens} country={props.country} + hiddenOnrampProviders={props.hiddenOnrampProviders} // others destinationToken={screen.destinationToken} destinationAmount={screen.destinationAmount} diff --git a/packages/thirdweb/src/react/web/ui/Bridge/bridge-widget/bridge-widget.tsx b/packages/thirdweb/src/react/web/ui/Bridge/bridge-widget/bridge-widget.tsx index d8aca486f9f..5ba533e3b23 100644 --- a/packages/thirdweb/src/react/web/ui/Bridge/bridge-widget/bridge-widget.tsx +++ b/packages/thirdweb/src/react/web/ui/Bridge/bridge-widget/bridge-widget.tsx @@ -197,6 +197,17 @@ export type BridgeWidgetProps = { presetOptions?: [number, number, number]; /** Arbitrary data to be included in returned status and webhook events. */ purchaseData?: PurchaseData; + /** + * Array of onramp provider IDs to hide from the payment provider selection. + * @example + * ```tsx + * + * ``` + */ + hiddenOnrampProviders?: ("coinbase" | "stripe" | "transak")[]; }; connectOptions?: { @@ -444,6 +455,7 @@ export function BridgeWidget(props: BridgeWidgetProps) { purchaseData={props.buy?.purchaseData} paymentMethods={["card"]} connectOptions={props.connectOptions} + hiddenOnrampProviders={props.buy?.hiddenOnrampProviders} style={{ border: "none", }} diff --git a/packages/thirdweb/src/react/web/ui/Bridge/payment-selection/FiatProviderSelection.test.tsx b/packages/thirdweb/src/react/web/ui/Bridge/payment-selection/FiatProviderSelection.test.tsx new file mode 100644 index 00000000000..8e2932b5849 --- /dev/null +++ b/packages/thirdweb/src/react/web/ui/Bridge/payment-selection/FiatProviderSelection.test.tsx @@ -0,0 +1,131 @@ +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import { render, screen } from "@testing-library/react"; +import { describe, expect, it, vi } from "vitest"; +import { TEST_CLIENT } from "~test/test-clients.js"; +import { FiatProviderSelection } from "./FiatProviderSelection.js"; + +vi.mock( + "../../../../core/hooks/pay/useBuyWithFiatQuotesForProviders.js", + () => ({ + useBuyWithFiatQuotesForProviders: vi.fn(() => [ + { + data: { + intent: { onramp: "coinbase" }, + currencyAmount: 10.5, + destinationAmount: 10n, + destinationToken: { symbol: "ETH", decimals: 18 }, + }, + isLoading: false, + error: null, + isError: false, + isSuccess: true, + }, + { + data: { + intent: { onramp: "stripe" }, + currencyAmount: 11.0, + destinationAmount: 10n, + destinationToken: { symbol: "ETH", decimals: 18 }, + }, + isLoading: false, + error: null, + isError: false, + isSuccess: true, + }, + { + data: { + intent: { onramp: "transak" }, + currencyAmount: 12.0, + destinationAmount: 10n, + destinationToken: { symbol: "ETH", decimals: 18 }, + }, + isLoading: false, + error: null, + isError: false, + isSuccess: true, + }, + ]), + }), +); + +const queryClient = new QueryClient(); + +function TestWrapper({ children }: { children: React.ReactNode }) { + return ( + {children} + ); +} + +describe("FiatProviderSelection - hiddenOnrampProviders", () => { + it("should render all providers when no providers are hidden", () => { + const mockOnProviderSelected = vi.fn(); + + render( + + + , + ); + + expect(screen.getByText("Coinbase")).toBeDefined(); + expect(screen.getByText("Stripe")).toBeDefined(); + expect(screen.getByText("Transak")).toBeDefined(); + }); + + it("should hide Transak when specified in hiddenOnrampProviders", () => { + const mockOnProviderSelected = vi.fn(); + + render( + + + , + ); + + expect(screen.getByText("Coinbase")).toBeDefined(); + expect(screen.getByText("Stripe")).toBeDefined(); + expect(screen.queryByText("Transak")).toBeNull(); + }); + + it("should hide multiple providers when specified", () => { + const mockOnProviderSelected = vi.fn(); + + render( + + + , + ); + + expect(screen.getByText("Coinbase")).toBeDefined(); + expect(screen.queryByText("Stripe")).toBeNull(); + expect(screen.queryByText("Transak")).toBeNull(); + }); +}); diff --git a/packages/thirdweb/src/react/web/ui/Bridge/payment-selection/FiatProviderSelection.tsx b/packages/thirdweb/src/react/web/ui/Bridge/payment-selection/FiatProviderSelection.tsx index 1cbe3040fb0..f4546d31020 100644 --- a/packages/thirdweb/src/react/web/ui/Bridge/payment-selection/FiatProviderSelection.tsx +++ b/packages/thirdweb/src/react/web/ui/Bridge/payment-selection/FiatProviderSelection.tsx @@ -28,6 +28,7 @@ interface FiatProviderSelectionProps { toAmount?: string; currency?: SupportedFiatCurrency; country: string | undefined; + hiddenOnrampProviders?: ("coinbase" | "stripe" | "transak")[]; } const PROVIDERS = [ @@ -60,17 +61,26 @@ export function FiatProviderSelection({ toAmount, currency, country, + hiddenOnrampProviders, }: FiatProviderSelectionProps) { - // Fetch quotes for all providers - const quoteQueries = useBuyWithFiatQuotesForProviders({ - amount: toAmount || "0", - chainId: toChainId, - client, - currency: currency || "USD", - receiver: checksumAddress(toAddress), - tokenAddress: checksumAddress(toTokenAddress), - country, - }); + const visibleProviders = useMemo(() => { + return PROVIDERS.filter( + (provider) => !hiddenOnrampProviders?.includes(provider.id), + ); + }, [hiddenOnrampProviders]); + + const quoteQueries = useBuyWithFiatQuotesForProviders( + { + amount: toAmount || "0", + chainId: toChainId, + client, + currency: currency || "USD", + receiver: checksumAddress(toAddress), + tokenAddress: checksumAddress(toTokenAddress), + country, + }, + visibleProviders.map((p) => p.id), + ); const quotes = useMemo(() => { return quoteQueries.map((q) => q.data).filter((q) => !!q); @@ -106,7 +116,7 @@ export function FiatProviderSelection({ quotes .sort((a, b) => a.currencyAmount - b.currencyAmount) .map((quote) => { - const provider = PROVIDERS.find( + const provider = visibleProviders.find( (p) => p.id === quote.intent.onramp, ); if (!provider) { diff --git a/packages/thirdweb/src/react/web/ui/Bridge/payment-selection/PaymentSelection.tsx b/packages/thirdweb/src/react/web/ui/Bridge/payment-selection/PaymentSelection.tsx index 6f85d72654f..b70e8af161c 100644 --- a/packages/thirdweb/src/react/web/ui/Bridge/payment-selection/PaymentSelection.tsx +++ b/packages/thirdweb/src/react/web/ui/Bridge/payment-selection/PaymentSelection.tsx @@ -90,6 +90,11 @@ type PaymentSelectionProps = { country: string | undefined; supportedTokens: SupportedTokens | undefined; + + /** + * Array of onramp provider IDs to hide from the payment provider selection. + */ + hiddenOnrampProviders?: ("coinbase" | "stripe" | "transak")[]; }; type Step = @@ -113,6 +118,7 @@ export function PaymentSelection({ feePayer, currency, country, + hiddenOnrampProviders, }: PaymentSelectionProps) { const connectedWallets = useConnectedWallets(); const activeWallet = useActiveWallet(); @@ -308,6 +314,7 @@ export function PaymentSelection({ toChainId={destinationToken.chainId} toTokenAddress={destinationToken.address} currency={currency} + hiddenOnrampProviders={hiddenOnrampProviders} /> )} From a098646d6b56b034e539f4c8c70160799d75fa1b Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 12 Mar 2026 06:54:31 +0000 Subject: [PATCH 2/2] [SDK] Remove temporary test file Co-authored-by: Yash --- .../FiatProviderSelection.test.tsx | 131 ------------------ 1 file changed, 131 deletions(-) delete mode 100644 packages/thirdweb/src/react/web/ui/Bridge/payment-selection/FiatProviderSelection.test.tsx diff --git a/packages/thirdweb/src/react/web/ui/Bridge/payment-selection/FiatProviderSelection.test.tsx b/packages/thirdweb/src/react/web/ui/Bridge/payment-selection/FiatProviderSelection.test.tsx deleted file mode 100644 index 8e2932b5849..00000000000 --- a/packages/thirdweb/src/react/web/ui/Bridge/payment-selection/FiatProviderSelection.test.tsx +++ /dev/null @@ -1,131 +0,0 @@ -import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; -import { render, screen } from "@testing-library/react"; -import { describe, expect, it, vi } from "vitest"; -import { TEST_CLIENT } from "~test/test-clients.js"; -import { FiatProviderSelection } from "./FiatProviderSelection.js"; - -vi.mock( - "../../../../core/hooks/pay/useBuyWithFiatQuotesForProviders.js", - () => ({ - useBuyWithFiatQuotesForProviders: vi.fn(() => [ - { - data: { - intent: { onramp: "coinbase" }, - currencyAmount: 10.5, - destinationAmount: 10n, - destinationToken: { symbol: "ETH", decimals: 18 }, - }, - isLoading: false, - error: null, - isError: false, - isSuccess: true, - }, - { - data: { - intent: { onramp: "stripe" }, - currencyAmount: 11.0, - destinationAmount: 10n, - destinationToken: { symbol: "ETH", decimals: 18 }, - }, - isLoading: false, - error: null, - isError: false, - isSuccess: true, - }, - { - data: { - intent: { onramp: "transak" }, - currencyAmount: 12.0, - destinationAmount: 10n, - destinationToken: { symbol: "ETH", decimals: 18 }, - }, - isLoading: false, - error: null, - isError: false, - isSuccess: true, - }, - ]), - }), -); - -const queryClient = new QueryClient(); - -function TestWrapper({ children }: { children: React.ReactNode }) { - return ( - {children} - ); -} - -describe("FiatProviderSelection - hiddenOnrampProviders", () => { - it("should render all providers when no providers are hidden", () => { - const mockOnProviderSelected = vi.fn(); - - render( - - - , - ); - - expect(screen.getByText("Coinbase")).toBeDefined(); - expect(screen.getByText("Stripe")).toBeDefined(); - expect(screen.getByText("Transak")).toBeDefined(); - }); - - it("should hide Transak when specified in hiddenOnrampProviders", () => { - const mockOnProviderSelected = vi.fn(); - - render( - - - , - ); - - expect(screen.getByText("Coinbase")).toBeDefined(); - expect(screen.getByText("Stripe")).toBeDefined(); - expect(screen.queryByText("Transak")).toBeNull(); - }); - - it("should hide multiple providers when specified", () => { - const mockOnProviderSelected = vi.fn(); - - render( - - - , - ); - - expect(screen.getByText("Coinbase")).toBeDefined(); - expect(screen.queryByText("Stripe")).toBeNull(); - expect(screen.queryByText("Transak")).toBeNull(); - }); -});