Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/twelve-hornets-cough.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"thirdweb": patch
---

Allow selecting fiat currency in Pay UI
4 changes: 1 addition & 3 deletions packages/thirdweb/src/pay/buyWithFiat/getQuote.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,8 @@ export type GetBuyWithFiatQuoteParams = {

/**
* Symbol of the fiat currency to buy the token with.
*
* Currently, only `USD` is supported.
*/
fromCurrencySymbol: "USD";
fromCurrencySymbol: "USD" | "CAD" | "GBP" | "EUR";

/**
* The maximum slippage in basis points (bps) allowed for the transaction.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ import {
import { EstimatedTimeAndFees } from "./EstimatedTimeAndFees.js";
import { PayWithCreditCard } from "./PayWIthCreditCard.js";
import { PaymentSelection } from "./PaymentSelection.js";
import { CurrencySelection } from "./fiat/CurrencySelection.js";
import { FiatFlow } from "./fiat/FiatFlow.js";
import type { CurrencyMeta } from "./fiat/currencies.js";
import type { BuyForTx, SelectedScreen } from "./main/types.js";
Expand Down Expand Up @@ -120,9 +121,7 @@ type BuyScreenContentProps = {
isEmbed: boolean;
};

function useBuyScreenStates(options: {
payOptions: PayUIOptions;
}) {
function useBuyScreenStates(options: { payOptions: PayUIOptions }) {
const { payOptions } = options;

const [method, setMethod] = useState<"crypto" | "creditCard">(
Expand Down Expand Up @@ -209,6 +208,7 @@ function BuyScreenContent(props: BuyScreenContentProps) {
fromToken,
setFromToken,
selectedCurrency,
setSelectedCurrency,
} = useUISelectionStates({
payOptions,
buyForTx,
Expand Down Expand Up @@ -271,6 +271,18 @@ function BuyScreenContent(props: BuyScreenContentProps) {
return screen.node;
}

if (screen.type === "select-currency") {
return (
<CurrencySelection
onSelect={(currency) => {
showMainScreen();
setSelectedCurrency(currency);
}}
onBack={showMainScreen}
/>
);
}

if (screen.type === "screen-id" && screen.name === "select-to-token") {
const chains = supportedDestinations.map((x) => x.chain);
// if token selection is disabled - only show network selector screen
Expand Down Expand Up @@ -491,7 +503,9 @@ function BuyScreenContent(props: BuyScreenContentProps) {
closeDrawer={closeDrawer}
selectedCurrency={selectedCurrency}
showCurrencySelector={() => {
// currently disabled because we are only using Stripe
setScreen({
type: "select-currency",
});
}}
account={account}
/>
Expand Down Expand Up @@ -778,7 +792,7 @@ function FiatScreenContent(
const fiatQuoteQuery = useBuyWithFiatQuote(
buyWithFiatOptions !== false && tokenAmount
? {
fromCurrencySymbol: "USD", // STRIPE only supports USD
fromCurrencySymbol: selectedCurrency.shorthand,
toChainId: toChain.id,
toAddress: account.address,
toTokenAddress: isNativeToken(toToken)
Expand Down Expand Up @@ -893,7 +907,6 @@ function FiatScreenContent(
client={client}
currency={selectedCurrency}
onSelectCurrency={showCurrencySelector}
disableCurrencySelection={true}
/>
{/* Estimated time + View fees button */}
<EstimatedTimeAndFees
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ export function PayWithCreditCard(props: {
client: ThirdwebClient;
currency: CurrencyMeta;
onSelectCurrency: () => void;
disableCurrencySelection?: boolean;
}) {
return (
<Container
Expand All @@ -47,7 +46,6 @@ export function PayWithCreditCard(props: {
>
{/* Left */}
<CurrencyButton
disabled={props.disableCurrencySelection}
variant="ghost"
onClick={props.onSelectCurrency}
style={{
Expand All @@ -58,11 +56,9 @@ export function PayWithCreditCard(props: {
gap="sm"
>
<props.currency.icon size={iconSize.md} />
<Container flex="row" center="y" gap="xxs">
<Container flex="row" center="y" gap="xxs" color="secondaryText">
<Text color="primaryText">{props.currency.shorthand}</Text>
{!props.disableCurrencySelection && (
<ChevronDownIcon width={iconSize.sm} height={iconSize.sm} />
)}
<ChevronDownIcon width={iconSize.sm} height={iconSize.sm} />
</Container>
</CurrencyButton>

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { iconSize } from "../../../../../../core/design-system/index.js";
import styled from "@emotion/styled";
import { useCustomTheme } from "../../../../../../core/design-system/CustomThemeProvider.js";
import {
iconSize,
spacing,
} from "../../../../../../core/design-system/index.js";
import { Spacer } from "../../../../components/Spacer.js";
import { Container, Line, ModalHeader } from "../../../../components/basic.js";
import { Button } from "../../../../components/buttons.js";
Expand All @@ -18,22 +23,22 @@ export function CurrencySelection(props: {
<Line />
<Spacer y="lg" />

<Container flex="column" gap="xs" px="md">
<Container flex="column" gap="xs" px="lg">
{currencies.map((c) => {
return (
<Button
<SelectCurrencyButton
fullWidth
variant="ghost"
variant="secondary"
key={c.shorthand}
onClick={() => props.onSelect(c)}
gap="sm"
>
<c.icon size={iconSize.lg} />
<Container flex="column" gap="xxs">
<Text color="primaryText">{c.shorthand}</Text>
<Text>{c.name}</Text>
<Text size="sm">{c.name}</Text>
</Container>
</Button>
</SelectCurrencyButton>
);
})}
</Container>
Expand All @@ -42,3 +47,18 @@ export function CurrencySelection(props: {
</Container>
);
}

const SelectCurrencyButton = /* @__PURE__ */ styled(Button)(() => {
const theme = useCustomTheme();
return {
background: theme.colors.tertiaryBg,
justifyContent: "flex-start",
gap: spacing.sm,
padding: spacing.sm,
"&:hover": {
background: theme.colors.secondaryButtonBg,
transform: "scale(1.01)",
},
transition: "background 200ms ease, transform 150ms ease",
};
});
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ import { Spacer } from "../../../../components/Spacer.js";
import { Container, Line } from "../../../../components/basic.js";
import { ButtonLink } from "../../../../components/buttons.js";
import { Text } from "../../../../components/text.js";
import { USDIcon } from "../../../icons/currencies/USDIcon.js";
import { TokenInfoRow } from "../tx-history/TokenInfoRow.js";
import type { FiatStatusMeta } from "../tx-history/statusMeta.js";
import { getCurrencyMeta } from "./currencies.js";

/**
* Show a table with the details of a "OnRamp" transaction step in the "Buy with Fiat" flow.
Expand All @@ -40,6 +40,7 @@ export function OnRampTxDetailsTable(props: {
}) {
const onRampChainQuery = useChainQuery(getCachedChain(props.token.chainId));
const onrampTxHash = props.statusMeta?.txHash;
const currencyMeta = getCurrencyMeta(props.fiat.currencySymbol);

const lineSpacer = (
<>
Expand Down Expand Up @@ -79,9 +80,7 @@ export function OnRampTxDetailsTable(props: {
}}
>
<Container flex="row" gap="xs" center="y">
{props.fiat.currencySymbol === "USD" && (
<USDIcon size={iconSize.sm} />
)}
<currencyMeta.icon size={iconSize.sm} />
<Text color="primaryText">
{formatNumber(Number(props.fiat.amount), 4)}{" "}
{props.fiat.currencySymbol}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { USDIcon } from "../../../icons/currencies/USDIcon.js";
import type { IconFC } from "../../../icons/types.js";

export type CurrencyMeta = {
shorthand: string;
shorthand: "USD" | "CAD" | "GBP" | "EUR";
name: string;
icon: IconFC;
};
Expand All @@ -17,7 +17,7 @@ export const defaultSelectedCurrency: CurrencyMeta = {
icon: USDIcon,
};

export const currencies = [
export const currencies: CurrencyMeta[] = [
defaultSelectedCurrency,
{
shorthand: "CAD",
Expand Down Expand Up @@ -45,7 +45,7 @@ export function getCurrencyMeta(shorthand: string): CurrencyMeta {
// This should never happen
icon: UnknownCurrencyIcon,
name: shorthand,
shorthand,
shorthand: shorthand as CurrencyMeta["shorthand"],
}
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,7 @@ export type SelectedScreen =
}
| {
type: "main";
}
| {
type: "select-currency";
};
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,9 @@ export function useUISelectionStates(options: {
// --------------------------------------------------------------------------

// stripe only supports USD, so not using a state right now
const selectedCurrency = defaultSelectedCurrency;
const [selectedCurrency, setSelectedCurrency] = useState(
defaultSelectedCurrency,
);

return {
tokenAmount,
Expand All @@ -91,5 +93,6 @@ export function useUISelectionStates(options: {
setFromToken,
selectedCurrency,
setHasEditedAmount,
setSelectedCurrency,
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import { useMemo } from "react";
import type { Chain } from "../../../../chains/types.js";
import type { ThirdwebClient } from "../../../../client/client.js";
import { NATIVE_TOKEN_ADDRESS } from "../../../../constants/addresses.js";
import { iconSize } from "../../../core/design-system/index.js";
import { useChainQuery } from "../../../core/hooks/others/useChainQuery.js";
import { genericTokenIcon } from "../ConnectWallet/icons/dataUris.js";
Expand Down Expand Up @@ -30,7 +31,10 @@ export function TokenIcon(props: {
const chainQuery = useChainQuery(props.chain);

const tokenImage = useMemo(() => {
if (isNativeToken(props.token)) {
if (
isNativeToken(props.token) ||
props.token.address === NATIVE_TOKEN_ADDRESS
) {
return chainQuery.data?.icon?.url;
}
return props.token.icon;
Expand Down