Skip to content

Commit 2dfc245

Browse files
[SDK] feat: Add provider selection to fiat onramp (#6190)
1 parent 64d7bf3 commit 2dfc245

File tree

7 files changed

+149
-5
lines changed

7 files changed

+149
-5
lines changed

.changeset/silent-comics-carry.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"thirdweb": patch
3+
---
4+
5+
Add fiat provider selection in PayEmbed

apps/playground-web/src/components/pay/embed.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export function StyledPayEmbedPreview() {
1010
const { theme } = useTheme();
1111

1212
return (
13-
<>
13+
<div className="flex flex-col items-center justify-center">
1414
<StyledConnectButton />
1515
<div className="h-10" />
1616
<PayEmbed
@@ -27,6 +27,6 @@ export function StyledPayEmbedPreview() {
2727
},
2828
}}
2929
/>
30-
</>
30+
</div>
3131
);
3232
}

packages/thirdweb/src/pay/buyWithFiat/getQuote.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,11 @@ export type BuyWithFiatQuote = {
238238
*
239239
*/
240240
onRampLink: string;
241+
242+
/**
243+
* The provider that was used to get the quote.
244+
*/
245+
provider: FiatProvider;
241246
};
242247

243248
/**

packages/thirdweb/src/pay/utils/commonTypes.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,6 @@ export type PayOnChainTransactionDetails = {
1717
explorerLink?: string;
1818
};
1919

20-
export type FiatProvider = "STRIPE" | "TRANSAK" | "KADO" | "COINBASE";
20+
export type FiatProvider = (typeof FiatProviders)[number];
21+
22+
export const FiatProviders = ["COINBASE", "STRIPE", "TRANSAK", "KADO"] as const;

packages/thirdweb/src/react/core/utils/storage.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type { AsyncStorage } from "../../../utils/storage/AsyncStorage.js";
22
import type { AuthArgsType } from "../../../wallets/in-app/core/authentication/types.js";
33

44
export const LAST_AUTH_PROVIDER_STORAGE_KEY = "lastAuthProvider";
5+
export const PREFERRED_FIAT_PROVIDER_STORAGE_KEY = "preferredFiatProvider";
56

67
export async function setLastAuthProvider(
78
authProvider: AuthArgsType["strategy"],

packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/BuyScreen.tsx

Lines changed: 76 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { ChevronDownIcon } from "@radix-ui/react-icons";
12
import { useQuery, useQueryClient } from "@tanstack/react-query";
23
import { useCallback, useMemo, useState } from "react";
34
import type { Chain } from "../../../../../../chains/types.js";
@@ -10,11 +11,13 @@ import type { GetBuyWithCryptoQuoteParams } from "../../../../../../pay/buyWithC
1011
import type { BuyWithCryptoStatus } from "../../../../../../pay/buyWithCrypto/getStatus.js";
1112
import type { BuyWithFiatStatus } from "../../../../../../pay/buyWithFiat/getStatus.js";
1213
import { isSwapRequiredPostOnramp } from "../../../../../../pay/buyWithFiat/isSwapRequiredPostOnramp.js";
14+
import type { FiatProvider } from "../../../../../../pay/utils/commonTypes.js";
1315
import { formatNumber } from "../../../../../../utils/formatNumber.js";
1416
import type { Account } from "../../../../../../wallets/interfaces/wallet.js";
1517
import type { WalletId } from "../../../../../../wallets/wallet-types.js";
1618
import {
1719
type Theme,
20+
iconSize,
1821
spacing,
1922
} from "../../../../../core/design-system/index.js";
2023
import type {
@@ -27,6 +30,7 @@ import { useBuyWithFiatQuote } from "../../../../../core/hooks/pay/useBuyWithFia
2730
import { useActiveAccount } from "../../../../../core/hooks/wallets/useActiveAccount.js";
2831
import { invalidateWalletBalance } from "../../../../../core/providers/invalidateWalletBalance.js";
2932
import type { SupportedTokens } from "../../../../../core/utils/defaultTokens.js";
33+
import { PREFERRED_FIAT_PROVIDER_STORAGE_KEY } from "../../../../../core/utils/storage.js";
3034
import { ErrorState } from "../../../../wallets/shared/ErrorState.js";
3135
import { LoadingScreen } from "../../../../wallets/shared/LoadingScreen.js";
3236
import type { PayEmbedConnectOptions } from "../../../PayEmbed.js";
@@ -56,6 +60,7 @@ import { PayWithCreditCard } from "./PayWIthCreditCard.js";
5660
import { TransactionModeScreen } from "./TransactionModeScreen.js";
5761
import { CurrencySelection } from "./fiat/CurrencySelection.js";
5862
import { FiatFlow } from "./fiat/FiatFlow.js";
63+
import { Providers } from "./fiat/Providers.js";
5964
import type { CurrencyMeta } from "./fiat/currencies.js";
6065
import type { SelectedScreen } from "./main/types.js";
6166
import {
@@ -1255,9 +1260,22 @@ function FiatScreenContent(props: {
12551260
const receiverAddress =
12561261
defaultRecipientAddress || props.payer.account.address;
12571262
const { drawerRef, drawerOverlayRef, isOpen, setIsOpen } = useDrawer();
1258-
const [drawerScreen, setDrawerScreen] = useState<"fees">("fees");
1263+
const [drawerScreen, setDrawerScreen] = useState<"fees" | "providers">(
1264+
"fees",
1265+
);
12591266

12601267
const buyWithFiatOptions = props.payOptions.buyWithFiat;
1268+
const [preferredProvider, setPreferredProvider] = useState<
1269+
FiatProvider | undefined
1270+
>(
1271+
buyWithFiatOptions !== false
1272+
? buyWithFiatOptions?.preferredProvider ||
1273+
((localStorage.getItem(
1274+
PREFERRED_FIAT_PROVIDER_STORAGE_KEY,
1275+
) as FiatProvider | null) ??
1276+
undefined)
1277+
: undefined,
1278+
);
12611279

12621280
const fiatQuoteQuery = useBuyWithFiatQuote(
12631281
buyWithFiatOptions !== false && tokenAmount
@@ -1273,7 +1291,7 @@ function FiatScreenContent(props: {
12731291
isTestMode: buyWithFiatOptions?.testMode,
12741292
purchaseData: props.payOptions.purchaseData,
12751293
fromAddress: payer.account.address,
1276-
preferredProvider: buyWithFiatOptions?.preferredProvider,
1294+
preferredProvider: preferredProvider,
12771295
}
12781296
: undefined,
12791297
);
@@ -1314,6 +1332,11 @@ function FiatScreenContent(props: {
13141332
setIsOpen(true);
13151333
}
13161334

1335+
function showProviders() {
1336+
setDrawerScreen("providers");
1337+
setIsOpen(true);
1338+
}
1339+
13171340
const disableSubmit = !fiatQuoteQuery.data;
13181341

13191342
const errorMsg =
@@ -1337,6 +1360,28 @@ function FiatScreenContent(props: {
13371360
<FiatFees quote={fiatQuoteQuery.data} />
13381361
</div>
13391362
)}
1363+
{drawerScreen === "providers" && (
1364+
<div>
1365+
<Text size="lg" color="primaryText">
1366+
Providers
1367+
</Text>
1368+
<Spacer y="lg" />
1369+
<Providers
1370+
preferredProvider={
1371+
preferredProvider || fiatQuoteQuery.data?.provider
1372+
}
1373+
onSelect={(provider) => {
1374+
setPreferredProvider(provider);
1375+
// save the pref in local storage
1376+
localStorage.setItem(
1377+
PREFERRED_FIAT_PROVIDER_STORAGE_KEY,
1378+
provider,
1379+
);
1380+
setIsOpen(false);
1381+
}}
1382+
/>
1383+
</div>
1384+
)}
13401385
</Drawer>
13411386
</>
13421387
)}
@@ -1349,6 +1394,35 @@ function FiatScreenContent(props: {
13491394
currency={selectedCurrency}
13501395
onSelectCurrency={showCurrencySelector}
13511396
/>
1397+
<Container
1398+
bg="tertiaryBg"
1399+
flex="row"
1400+
borderColor="borderColor"
1401+
style={{
1402+
paddingLeft: spacing.md,
1403+
justifyContent: "space-between",
1404+
alignItems: "center",
1405+
borderWidth: "1px",
1406+
borderStyle: "solid",
1407+
borderBottom: "none",
1408+
}}
1409+
>
1410+
<Text size="xs" color="secondaryText">
1411+
Provider
1412+
</Text>
1413+
<Button variant="ghost" onClick={showProviders}>
1414+
<Container flex="row" center="y" gap="xxs" color="secondaryText">
1415+
<Text size="xs">
1416+
{preferredProvider
1417+
? `${preferredProvider.charAt(0).toUpperCase() + preferredProvider.slice(1).toLowerCase()}`
1418+
: fiatQuoteQuery.data?.provider
1419+
? `${fiatQuoteQuery.data?.provider.charAt(0).toUpperCase() + fiatQuoteQuery.data?.provider.slice(1).toLowerCase()}`
1420+
: ""}
1421+
</Text>
1422+
<ChevronDownIcon width={iconSize.sm} height={iconSize.sm} />
1423+
</Container>
1424+
</Button>
1425+
</Container>
13521426
{/* Estimated time + View fees button */}
13531427
<EstimatedTimeAndFees
13541428
quoteIsLoading={fiatQuoteQuery.isLoading}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import {
2+
type FiatProvider,
3+
FiatProviders,
4+
} from "../../../../../../../pay/utils/commonTypes.js";
5+
import { Container } from "../../../../components/basic.js";
6+
import { Button } from "../../../../components/buttons.js";
7+
import { Link } from "../../../../components/text.js";
8+
/**
9+
* @internal
10+
*/
11+
export function Providers(props: {
12+
preferredProvider?: FiatProvider;
13+
onSelect: (provider: FiatProvider) => void;
14+
}) {
15+
return (
16+
<Container
17+
expand
18+
flex="column"
19+
gap="sm"
20+
style={{
21+
alignItems: "flex-start",
22+
}}
23+
>
24+
{FiatProviders.map((provider) => {
25+
return (
26+
<Container
27+
key={provider}
28+
flex="row"
29+
expand
30+
style={{
31+
justifyContent: "space-between",
32+
}}
33+
>
34+
<Button
35+
fullWidth
36+
onClick={() => props.onSelect(provider)}
37+
variant={"link"}
38+
>
39+
<Link
40+
color={
41+
props.preferredProvider === provider
42+
? "primaryText"
43+
: "secondaryText"
44+
}
45+
size="sm"
46+
hoverColor="primaryText"
47+
>
48+
{provider.charAt(0).toUpperCase() +
49+
provider.slice(1).toLowerCase()}
50+
</Link>
51+
</Button>
52+
</Container>
53+
);
54+
})}
55+
</Container>
56+
);
57+
}

0 commit comments

Comments
 (0)