diff --git a/.changeset/ready-loops-search.md b/.changeset/ready-loops-search.md
new file mode 100644
index 00000000000..fc3230e9785
--- /dev/null
+++ b/.changeset/ready-loops-search.md
@@ -0,0 +1,5 @@
+---
+"thirdweb": patch
+---
+
+Handle different fiat currencies in payment widgets
diff --git a/apps/playground-web/src/app/connect/pay/components/CodeGen.tsx b/apps/playground-web/src/app/connect/pay/components/CodeGen.tsx
index d63b58c7f23..867ad8155d5 100644
--- a/apps/playground-web/src/app/connect/pay/components/CodeGen.tsx
+++ b/apps/playground-web/src/app/connect/pay/components/CodeGen.tsx
@@ -73,7 +73,7 @@ function Example() {
<${componentName}
client={client}
chain={defineChain(${options.payOptions.buyTokenChain.id})}
- amount="${options.payOptions.buyTokenAmount}"${options.payOptions.buyTokenAddress ? `\n\t token="${options.payOptions.buyTokenAddress}"` : ""}${options.payOptions.sellerAddress ? `\n\t seller="${options.payOptions.sellerAddress}"` : ""}${options.payOptions.title ? `\n\t ${options.payOptions.widget === "checkout" ? "name" : "title"}="${options.payOptions.title}"` : ""}${options.payOptions.image ? `\n\t image="${options.payOptions.image}"` : ""}${options.payOptions.description ? `\n\t description="${options.payOptions.description}"` : ""}${options.payOptions.paymentMethods && options.payOptions.paymentMethods.length > 0 ? `\n\t paymentMethods={${JSON.stringify(options.payOptions.paymentMethods)}}` : ""}${
+ amount="${options.payOptions.buyTokenAmount}"${options.payOptions.buyTokenAddress ? `\n\t token="${options.payOptions.buyTokenAddress}"` : ""}${options.payOptions.sellerAddress ? `\n\t seller="${options.payOptions.sellerAddress}"` : ""}${options.payOptions.title ? `\n\t ${options.payOptions.widget === "checkout" ? "name" : "title"}="${options.payOptions.title}"` : ""}${options.payOptions.image ? `\n\t image="${options.payOptions.image}"` : ""}${options.payOptions.description ? `\n\t description="${options.payOptions.description}"` : ""}${options.payOptions.paymentMethods && options.payOptions.paymentMethods.length > 0 ? `\n\t paymentMethods={${JSON.stringify(options.payOptions.paymentMethods)}}` : ""}${options.payOptions.currency ? `\n\t currency="${options.payOptions.currency}"` : ""}${
options.payOptions.widget === "transaction"
? `\n\t transaction={claimTo({
contract: nftContract,
diff --git a/apps/playground-web/src/app/connect/pay/components/types.ts b/apps/playground-web/src/app/connect/pay/components/types.ts
index fa4343e2194..f6713f34fed 100644
--- a/apps/playground-web/src/app/connect/pay/components/types.ts
+++ b/apps/playground-web/src/app/connect/pay/components/types.ts
@@ -2,6 +2,34 @@ import type { Chain } from "thirdweb/chains";
import type { ThemeOverrides } from "thirdweb/react";
import type { Address } from "thirdweb/utils";
+const CURRENCIES = [
+ "USD",
+ "EUR",
+ "GBP",
+ "JPY",
+ "KRW",
+ "CNY",
+ "INR",
+ "NOK",
+ "SEK",
+ "CHF",
+ "AUD",
+ "CAD",
+ "NZD",
+ "MXN",
+ "BRL",
+ "CLP",
+ "CZK",
+ "DKK",
+ "HKD",
+ "HUF",
+ "IDR",
+ "ILS",
+ "ISK",
+] as const;
+
+type SupportedFiatCurrency = (typeof CURRENCIES)[number] | (string & {});
+
export type BridgeComponentsPlaygroundOptions = {
theme: {
type: "dark" | "light";
@@ -26,6 +54,8 @@ export type BridgeComponentsPlaygroundOptions = {
paymentMethods: ("crypto" | "card")[];
+ currency?: SupportedFiatCurrency;
+
showThirdwebBranding: boolean;
};
};
diff --git a/apps/playground-web/src/app/connect/pay/embed/LeftSection.tsx b/apps/playground-web/src/app/connect/pay/embed/LeftSection.tsx
index ebeddba572e..920d2053809 100644
--- a/apps/playground-web/src/app/connect/pay/embed/LeftSection.tsx
+++ b/apps/playground-web/src/app/connect/pay/embed/LeftSection.tsx
@@ -17,6 +17,13 @@ import { CustomRadioGroup } from "@/components/ui/CustomRadioGroup";
import { Checkbox } from "@/components/ui/checkbox";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
+import {
+ Select,
+ SelectContent,
+ SelectItem,
+ SelectTrigger,
+ SelectValue,
+} from "@/components/ui/select";
import { TokenSelector } from "@/components/ui/TokenSelector";
import { THIRDWEB_CLIENT } from "@/lib/client";
import type { TokenMetadata } from "@/lib/types";
@@ -134,6 +141,51 @@ export function LeftSection(props: {
/>
+
+
+
+
+
{/* Shared Chain and Token Selection - Always visible for Buy and Checkout modes */}
{(!payOptions.widget ||
payOptions.widget === "buy" ||
diff --git a/apps/playground-web/src/app/connect/pay/embed/RightSection.tsx b/apps/playground-web/src/app/connect/pay/embed/RightSection.tsx
index d7726af45a5..d29b54806f6 100644
--- a/apps/playground-web/src/app/connect/pay/embed/RightSection.tsx
+++ b/apps/playground-web/src/app/connect/pay/embed/RightSection.tsx
@@ -64,6 +64,7 @@ export function RightSection(props: {
theme={themeObj}
title={props.options.payOptions.title}
tokenAddress={props.options.payOptions.buyTokenAddress}
+ currency={props.options.payOptions.currency}
showThirdwebBranding={props.options.payOptions.showThirdwebBranding}
/>
);
@@ -88,6 +89,7 @@ export function RightSection(props: {
seller={props.options.payOptions.sellerAddress}
theme={themeObj}
tokenAddress={props.options.payOptions.buyTokenAddress}
+ currency={props.options.payOptions.currency}
showThirdwebBranding={props.options.payOptions.showThirdwebBranding}
/>
);
@@ -108,6 +110,7 @@ export function RightSection(props: {
to: account?.address || "",
tokenId: 2n,
})}
+ currency={props.options.payOptions.currency}
showThirdwebBranding={props.options.payOptions.showThirdwebBranding}
/>
);
diff --git a/apps/playground-web/src/app/connect/pay/embed/page.tsx b/apps/playground-web/src/app/connect/pay/embed/page.tsx
index b555db63443..07e4e6ae3d7 100644
--- a/apps/playground-web/src/app/connect/pay/embed/page.tsx
+++ b/apps/playground-web/src/app/connect/pay/embed/page.tsx
@@ -18,6 +18,7 @@ const defaultConnectOptions: BridgeComponentsPlaygroundOptions = {
title: "",
transactionData: "",
widget: "buy",
+ currency: "USD",
showThirdwebBranding: true,
},
theme: {
diff --git a/packages/thirdweb/src/bridge/types/Token.ts b/packages/thirdweb/src/bridge/types/Token.ts
index bf32dea7680..a54f0e9aaac 100644
--- a/packages/thirdweb/src/bridge/types/Token.ts
+++ b/packages/thirdweb/src/bridge/types/Token.ts
@@ -7,5 +7,5 @@ export type Token = {
symbol: string;
name: string;
iconUri?: string;
- priceUsd: number;
+ prices: Record;
};
diff --git a/packages/thirdweb/src/pay/buyWithCrypto/getQuote.ts b/packages/thirdweb/src/pay/buyWithCrypto/getQuote.ts
index f8716d73747..f12041a6263 100644
--- a/packages/thirdweb/src/pay/buyWithCrypto/getQuote.ts
+++ b/packages/thirdweb/src/pay/buyWithCrypto/getQuote.ts
@@ -300,14 +300,14 @@ export async function getBuyWithCryptoQuote(
Number(
Value.format(quote.originAmount, firstStep.originToken.decimals),
) *
- firstStep.originToken.priceUsd *
+ (firstStep.originToken.prices["USD"] || 0) *
100,
amountWei: quote.originAmount.toString(),
token: {
chainId: firstStep.originToken.chainId,
decimals: firstStep.originToken.decimals,
name: firstStep.originToken.name,
- priceUSDCents: firstStep.originToken.priceUsd * 100,
+ priceUSDCents: (firstStep.originToken.prices["USD"] || 0) * 100,
symbol: firstStep.originToken.symbol,
tokenAddress: firstStep.originToken.address,
},
@@ -323,7 +323,7 @@ export async function getBuyWithCryptoQuote(
chainId: firstStep.originToken.chainId,
decimals: firstStep.originToken.decimals,
name: firstStep.originToken.name,
- priceUSDCents: firstStep.originToken.priceUsd * 100,
+ priceUSDCents: (firstStep.originToken.prices["USD"] || 0) * 100,
symbol: firstStep.originToken.symbol,
tokenAddress: firstStep.originToken.address,
},
@@ -337,7 +337,7 @@ export async function getBuyWithCryptoQuote(
Number(
Value.format(quote.originAmount, firstStep.originToken.decimals),
) *
- firstStep.originToken.priceUsd *
+ (firstStep.originToken.prices["USD"] || 0) *
100,
gasCostUSDCents: 0,
slippageBPS: 0,
@@ -348,7 +348,7 @@ export async function getBuyWithCryptoQuote(
firstStep.destinationToken.decimals,
),
) *
- firstStep.destinationToken.priceUsd *
+ (firstStep.destinationToken.prices["USD"] || 0) *
100,
toAmountUSDCents:
Number(
@@ -357,7 +357,7 @@ export async function getBuyWithCryptoQuote(
firstStep.destinationToken.decimals,
),
) *
- firstStep.destinationToken.priceUsd *
+ (firstStep.destinationToken.prices["USD"] || 0) *
100,
},
fromAddress: quote.intent.sender,
@@ -372,7 +372,7 @@ export async function getBuyWithCryptoQuote(
chainId: firstStep.originToken.chainId,
decimals: firstStep.originToken.decimals,
name: firstStep.originToken.name,
- priceUSDCents: firstStep.originToken.priceUsd * 100,
+ priceUSDCents: (firstStep.originToken.prices["USD"] || 0) * 100,
symbol: firstStep.originToken.symbol,
tokenAddress: firstStep.originToken.address,
},
@@ -395,7 +395,7 @@ export async function getBuyWithCryptoQuote(
chainId: firstStep.destinationToken.chainId,
decimals: firstStep.destinationToken.decimals,
name: firstStep.destinationToken.name,
- priceUSDCents: firstStep.destinationToken.priceUsd * 100,
+ priceUSDCents: (firstStep.destinationToken.prices["USD"] || 0) * 100,
symbol: firstStep.destinationToken.symbol,
tokenAddress: firstStep.destinationToken.address,
},
diff --git a/packages/thirdweb/src/pay/buyWithCrypto/getTransfer.ts b/packages/thirdweb/src/pay/buyWithCrypto/getTransfer.ts
index 37952813f6d..3728cb9f357 100644
--- a/packages/thirdweb/src/pay/buyWithCrypto/getTransfer.ts
+++ b/packages/thirdweb/src/pay/buyWithCrypto/getTransfer.ts
@@ -198,14 +198,14 @@ export async function getBuyWithCryptoTransfer(
Number(
Value.format(quote.originAmount, firstStep.originToken.decimals),
) *
- firstStep.originToken.priceUsd *
+ (firstStep.originToken.prices["USD"] || 0) *
100,
amountWei: quote.originAmount.toString(),
token: {
chainId: firstStep.originToken.chainId,
decimals: firstStep.originToken.decimals,
name: firstStep.originToken.name,
- priceUSDCents: firstStep.originToken.priceUsd * 100,
+ priceUSDCents: (firstStep.originToken.prices["USD"] || 0) * 100,
symbol: firstStep.originToken.symbol,
tokenAddress: firstStep.originToken.address,
},
@@ -226,7 +226,7 @@ export async function getBuyWithCryptoTransfer(
firstStep.originToken.decimals,
),
) *
- firstStep.originToken.priceUsd *
+ (firstStep.originToken.prices["USD"] || 0) *
100
: 0,
amountWei:
@@ -237,7 +237,7 @@ export async function getBuyWithCryptoTransfer(
chainId: firstStep.originToken.chainId,
decimals: firstStep.originToken.decimals,
name: firstStep.originToken.name,
- priceUSDCents: firstStep.originToken.priceUsd * 100,
+ priceUSDCents: (firstStep.originToken.prices["USD"] || 0) * 100,
symbol: firstStep.originToken.symbol,
tokenAddress: firstStep.originToken.address,
},
diff --git a/packages/thirdweb/src/pay/buyWithFiat/getQuote.ts b/packages/thirdweb/src/pay/buyWithFiat/getQuote.ts
index 578864edb4b..aa07953c97b 100644
--- a/packages/thirdweb/src/pay/buyWithFiat/getQuote.ts
+++ b/packages/thirdweb/src/pay/buyWithFiat/getQuote.ts
@@ -367,12 +367,12 @@ export async function getBuyWithFiatQuote(
decimals: number;
symbol: string;
name: string;
- priceUsd: number;
+ prices: Record;
}): PayTokenInfo => ({
chainId: token.chainId,
decimals: token.decimals,
name: token.name,
- priceUSDCents: Math.round(token.priceUsd * 100),
+ priceUSDCents: Math.round((token.prices["USD"] || 0) * 100),
symbol: token.symbol,
tokenAddress: token.address,
});
@@ -408,7 +408,7 @@ export async function getBuyWithFiatQuote(
const onRampTokenObject = {
amount: onRampTokenAmount,
amountUSDCents: Math.round(
- Number(onRampTokenAmount) * onRampTokenRaw.priceUsd * 100,
+ Number(onRampTokenAmount) * (onRampTokenRaw.prices["USD"] || 0) * 100,
),
amountWei: onRampTokenAmountWei.toString(),
token: tokenToPayTokenInfo(onRampTokenRaw),
@@ -434,7 +434,7 @@ export async function getBuyWithFiatQuote(
routingTokenObject = {
amount: routingAmount,
amountUSDCents: Math.round(
- Number(routingAmount) * routingTokenRaw.priceUsd * 100,
+ Number(routingAmount) * (routingTokenRaw.prices["USD"] || 0) * 100,
),
amountWei: routingAmountWei.toString(),
token: tokenToPayTokenInfo(routingTokenRaw),
diff --git a/packages/thirdweb/src/pay/convert/cryptoToFiat.ts b/packages/thirdweb/src/pay/convert/cryptoToFiat.ts
index 6b80fd3a353..1c008b29b5b 100644
--- a/packages/thirdweb/src/pay/convert/cryptoToFiat.ts
+++ b/packages/thirdweb/src/pay/convert/cryptoToFiat.ts
@@ -26,7 +26,6 @@ export type ConvertCryptoToFiatParams = {
chain: Chain;
/**
* The fiat symbol. e.g "USD"
- * Only USD is supported at the moment.
*/
to: SupportedFiatCurrency;
};
@@ -56,7 +55,7 @@ export type ConvertCryptoToFiatParams = {
export async function convertCryptoToFiat(
options: ConvertCryptoToFiatParams,
): Promise<{ result: number }> {
- const { client, fromTokenAddress, chain, fromAmount } = options;
+ const { client, fromTokenAddress, chain, fromAmount, to } = options;
if (Number(fromAmount) === 0) {
return { result: 0 };
}
@@ -74,10 +73,11 @@ export async function convertCryptoToFiat(
);
}
const token = await getToken(client, fromTokenAddress, chain.id);
- if (token.priceUsd === 0) {
+ const price = token?.prices[to] || 0;
+ if (!token || price === 0) {
throw new Error(
`Error: Failed to fetch price for token ${fromTokenAddress} on chainId: ${chain.id}`,
);
}
- return { result: token.priceUsd * fromAmount };
+ return { result: price * fromAmount };
}
diff --git a/packages/thirdweb/src/pay/convert/fiatToCrypto.ts b/packages/thirdweb/src/pay/convert/fiatToCrypto.ts
index f0843f0974d..32958bdb306 100644
--- a/packages/thirdweb/src/pay/convert/fiatToCrypto.ts
+++ b/packages/thirdweb/src/pay/convert/fiatToCrypto.ts
@@ -13,7 +13,6 @@ export type ConvertFiatToCryptoParams = {
client: ThirdwebClient;
/**
* The fiat symbol. e.g: "USD"
- * Currently only USD is supported.
*/
from: SupportedFiatCurrency;
/**
@@ -57,7 +56,7 @@ export type ConvertFiatToCryptoParams = {
export async function convertFiatToCrypto(
options: ConvertFiatToCryptoParams,
): Promise<{ result: number }> {
- const { client, to, chain, fromAmount } = options;
+ const { client, to, chain, fromAmount, from } = options;
if (Number(fromAmount) === 0) {
return { result: 0 };
}
@@ -73,10 +72,11 @@ export async function convertFiatToCrypto(
throw new Error("Invalid `to`. Expected a valid EVM contract address");
}
const token = await getToken(client, to, chain.id);
- if (!token || token.priceUsd === 0) {
+ const price = token?.prices[from] || 0;
+ if (!token || price === 0) {
throw new Error(
`Error: Failed to fetch price for token ${to} on chainId: ${chain.id}`,
);
}
- return { result: fromAmount / token.priceUsd };
+ return { result: fromAmount / price };
}
diff --git a/packages/thirdweb/src/pay/convert/type.ts b/packages/thirdweb/src/pay/convert/type.ts
index b3a233ea987..62dd76cc0e0 100644
--- a/packages/thirdweb/src/pay/convert/type.ts
+++ b/packages/thirdweb/src/pay/convert/type.ts
@@ -1,16 +1,30 @@
-const SUPPORTED_FIAT_CURRENCIES = [
+const CURRENCIES = [
"USD",
- "CAD",
- "GBP",
"EUR",
+ "GBP",
"JPY",
+ "KRW",
+ "CNY",
+ "INR",
+ "NOK",
+ "SEK",
+ "CHF",
"AUD",
+ "CAD",
"NZD",
+ "MXN",
+ "BRL",
+ "CLP",
+ "CZK",
+ "DKK",
+ "HKD",
+ "HUF",
+ "IDR",
+ "ILS",
+ "ISK",
] as const;
-/**
- * @internal
- */
-export type SupportedFiatCurrency = (typeof SUPPORTED_FIAT_CURRENCIES)[number];
+
+export type SupportedFiatCurrency = (typeof CURRENCIES)[number] | (string & {});
export function getFiatSymbol(showBalanceInFiat: SupportedFiatCurrency) {
switch (showBalanceInFiat) {
diff --git a/packages/thirdweb/src/react/core/hooks/usePaymentMethods.ts b/packages/thirdweb/src/react/core/hooks/usePaymentMethods.ts
index 2e9ae093c26..7898aead16f 100644
--- a/packages/thirdweb/src/react/core/hooks/usePaymentMethods.ts
+++ b/packages/thirdweb/src/react/core/hooks/usePaymentMethods.ts
@@ -106,7 +106,9 @@ export function usePaymentMethods(options: {
decimals: b.decimals,
iconUri: "",
name: b.name,
- priceUsd: 0,
+ prices: {
+ USD: 0,
+ },
symbol: b.symbol,
} as Token,
}));
@@ -183,10 +185,10 @@ export function usePaymentMethods(options: {
validOwnedTokens.sort((a, b) => {
const aDollarBalance =
Number.parseFloat(toTokens(a.balance, a.originToken.decimals)) *
- a.originToken.priceUsd;
+ (a.originToken.prices["USD"] || 0);
const bDollarBalance =
Number.parseFloat(toTokens(b.balance, b.originToken.decimals)) *
- b.originToken.priceUsd;
+ (b.originToken.prices["USD"] || 0);
return bDollarBalance - aDollarBalance;
});
diff --git a/packages/thirdweb/src/react/core/hooks/useTransactionDetails.ts b/packages/thirdweb/src/react/core/hooks/useTransactionDetails.ts
index c3039109aec..64ec52d18d3 100644
--- a/packages/thirdweb/src/react/core/hooks/useTransactionDetails.ts
+++ b/packages/thirdweb/src/react/core/hooks/useTransactionDetails.ts
@@ -9,6 +9,7 @@ import { getCompilerMetadata } from "../../../contract/actions/get-compiler-meta
import { getContract } from "../../../contract/contract.js";
import { decimals } from "../../../extensions/erc20/read/decimals.js";
import { getToken } from "../../../pay/convert/get-token.js";
+import type { SupportedFiatCurrency } from "../../../pay/convert/type.js";
import { encode } from "../../../transaction/actions/encode.js";
import type { PreparedTransaction } from "../../../transaction/prepare-transaction.js";
import { getTransactionGasCost } from "../../../transaction/utils.js";
@@ -43,6 +44,7 @@ interface UseTransactionDetailsOptions {
transaction: PreparedTransaction;
client: ThirdwebClient;
wallet: Wallet | undefined;
+ currency?: SupportedFiatCurrency;
}
/**
@@ -51,6 +53,7 @@ interface UseTransactionDetailsOptions {
*/
export function useTransactionDetails({
transaction,
+ currency,
client,
wallet,
}: UseTransactionDetailsOptions) {
@@ -153,9 +156,8 @@ export function useTransactionDetails({
: (value || 0n) + (gasCostWei || 0n);
const totalCost = toTokens(totalCostWei, decimal);
- const usdValue = tokenInfo?.priceUsd
- ? Number(totalCost) * tokenInfo.priceUsd
- : null;
+ const price = tokenInfo?.prices[currency || "USD"] || 0;
+ const usdValue = price ? Number(totalCost) * price : null;
return {
contractMetadata,
@@ -170,7 +172,7 @@ export function useTransactionDetails({
totalCostWei,
txCostDisplay: `${formatTokenAmount(costWei, decimal)} ${tokenSymbol}`,
usdValueDisplay: usdValue
- ? formatCurrencyAmount("USD", usdValue)
+ ? formatCurrencyAmount(currency || "USD", usdValue)
: null,
};
},
diff --git a/packages/thirdweb/src/react/web/ui/Bridge/BridgeOrchestrator.tsx b/packages/thirdweb/src/react/web/ui/Bridge/BridgeOrchestrator.tsx
index 5577beb3838..a01fe1d589e 100644
--- a/packages/thirdweb/src/react/web/ui/Bridge/BridgeOrchestrator.tsx
+++ b/packages/thirdweb/src/react/web/ui/Bridge/BridgeOrchestrator.tsx
@@ -2,6 +2,7 @@
import { useCallback, useMemo } from "react";
import type { Token } from "../../../../bridge/types/Token.js";
import type { ThirdwebClient } from "../../../../client/client.js";
+import type { SupportedFiatCurrency } from "../../../../pay/convert/type.js";
import type { PurchaseData } from "../../../../pay/types.js";
import type { PreparedTransaction } from "../../../../transaction/prepare-transaction.js";
import type { Address } from "../../../../utils/address.js";
@@ -39,6 +40,7 @@ export type UIOptions = Prettify<
description?: string;
image?: string;
};
+ currency?: SupportedFiatCurrency;
} & (
| {
mode: "fund_wallet";
@@ -310,6 +312,7 @@ export function BridgeOrchestrator({
onPaymentMethodSelected={handlePaymentMethodSelected}
paymentMethods={paymentMethods}
receiverAddress={state.context.receiverAddress}
+ currency={uiOptions.currency}
/>
)}
diff --git a/packages/thirdweb/src/react/web/ui/Bridge/BuyWidget.tsx b/packages/thirdweb/src/react/web/ui/Bridge/BuyWidget.tsx
index 2a48b03fba5..bbda75f9328 100644
--- a/packages/thirdweb/src/react/web/ui/Bridge/BuyWidget.tsx
+++ b/packages/thirdweb/src/react/web/ui/Bridge/BuyWidget.tsx
@@ -7,6 +7,7 @@ import type { Chain } from "../../../../chains/types.js";
import type { ThirdwebClient } from "../../../../client/client.js";
import { NATIVE_TOKEN_ADDRESS } from "../../../../constants/addresses.js";
import { getToken } from "../../../../pay/convert/get-token.js";
+import type { SupportedFiatCurrency } from "../../../../pay/convert/type.js";
import type { PurchaseData } from "../../../../pay/types.js";
import {
type Address,
@@ -173,6 +174,12 @@ export type BuyWidgetProps = {
* @default ["crypto", "card"]
*/
paymentMethods?: ("crypto" | "card")[];
+
+ /**
+ * The currency to use for the payment.
+ * @default "USD"
+ */
+ currency?: SupportedFiatCurrency;
};
// Enhanced UIOptions to handle unsupported token state
@@ -316,6 +323,7 @@ export function BuyWidget(props: BuyWidgetProps) {
title: props.title,
},
mode: "fund_wallet",
+ currency: props.currency || "USD",
},
type: "success",
};
diff --git a/packages/thirdweb/src/react/web/ui/Bridge/CheckoutWidget.tsx b/packages/thirdweb/src/react/web/ui/Bridge/CheckoutWidget.tsx
index 6fc43d1a950..f7372ae756d 100644
--- a/packages/thirdweb/src/react/web/ui/Bridge/CheckoutWidget.tsx
+++ b/packages/thirdweb/src/react/web/ui/Bridge/CheckoutWidget.tsx
@@ -7,6 +7,7 @@ import type { Chain } from "../../../../chains/types.js";
import type { ThirdwebClient } from "../../../../client/client.js";
import { NATIVE_TOKEN_ADDRESS } from "../../../../constants/addresses.js";
import { getToken } from "../../../../pay/convert/get-token.js";
+import type { SupportedFiatCurrency } from "../../../../pay/convert/type.js";
import type { PurchaseData } from "../../../../pay/types.js";
import { type Address, checksumAddress } from "../../../../utils/address.js";
import { stringify } from "../../../../utils/json.js";
@@ -179,6 +180,12 @@ export type CheckoutWidgetProps = {
* @default ["crypto", "card"]
*/
paymentMethods?: ("crypto" | "card")[];
+
+ /**
+ * The currency to use for the payment.
+ * @default "USD"
+ */
+ currency?: SupportedFiatCurrency;
};
// Enhanced UIOptions to handle unsupported token state
@@ -302,6 +309,7 @@ export function CheckoutWidget(props: CheckoutWidgetProps) {
title: props.name,
},
mode: "direct_payment",
+ currency: props.currency || "USD",
paymentInfo: {
amount: props.amount,
feePayer: props.feePayer === "seller" ? "receiver" : "sender",
diff --git a/packages/thirdweb/src/react/web/ui/Bridge/DirectPayment.tsx b/packages/thirdweb/src/react/web/ui/Bridge/DirectPayment.tsx
index c1405b7bfb1..a8be79d332a 100644
--- a/packages/thirdweb/src/react/web/ui/Bridge/DirectPayment.tsx
+++ b/packages/thirdweb/src/react/web/ui/Bridge/DirectPayment.tsx
@@ -2,10 +2,9 @@
import type { Token } from "../../../../bridge/types/Token.js";
import { defineChain } from "../../../../chains/utils.js";
import type { ThirdwebClient } from "../../../../client/client.js";
-import { type Address, shortenAddress } from "../../../../utils/address.js";
+import type { Address } from "../../../../utils/address.js";
import { useCustomTheme } from "../../../core/design-system/CustomThemeProvider.js";
import { useActiveAccount } from "../../../core/hooks/wallets/useActiveAccount.js";
-import { useEnsName } from "../../../core/utils/wallet.js";
import { ConnectButton } from "../ConnectWallet/ConnectButton.js";
import { PoweredByThirdweb } from "../ConnectWallet/PoweredByTW.js";
import { FiatValue } from "../ConnectWallet/screens/Buy/swap/FiatValue.js";
@@ -64,12 +63,6 @@ export function DirectPayment({
uiOptions.paymentInfo.sellerAddress,
);
};
- const ensName = useEnsName({
- address: uiOptions.paymentInfo.sellerAddress,
- client,
- });
- const sellerAddress =
- ensName.data || shortenAddress(uiOptions.paymentInfo.sellerAddress);
const buyNow = (
@@ -77,6 +70,7 @@ export function DirectPayment({
Buy Now ·
- {/* Seller section */}
-
-
- Sold by
-
-
- {sellerAddress}
-
-
-
-
-
{
- if (uiOptions.destinationToken.priceUsd === 0) {
+ const price =
+ uiOptions.destinationToken.prices[uiOptions.currency || "USD"] || 0;
+ if (price === 0) {
return;
}
// Convert USD amount to token amount using token price
- const tokenAmount = usdAmount / uiOptions.destinationToken.priceUsd;
+ const tokenAmount = usdAmount / price;
// Format to reasonable decimal places (up to 6 decimals, remove trailing zeros)
const formattedAmount = numberToPlainString(
Number.parseFloat(tokenAmount.toFixed(6)),
@@ -228,9 +231,13 @@ export function FundWallet({
size="md"
style={{ textWrap: "nowrap" }}
>
- ≈ $
- {(Number(amount) * uiOptions.destinationToken.priceUsd).toFixed(
- 2,
+ ≈{" "}
+ {formatCurrencyAmount(
+ uiOptions.currency || "USD",
+ Number(amount) *
+ (uiOptions.destinationToken.prices[
+ uiOptions.currency || "USD"
+ ] || 0),
)}
diff --git a/packages/thirdweb/src/react/web/ui/Bridge/TransactionWidget.tsx b/packages/thirdweb/src/react/web/ui/Bridge/TransactionWidget.tsx
index 29d2d28ad86..830ab55a969 100644
--- a/packages/thirdweb/src/react/web/ui/Bridge/TransactionWidget.tsx
+++ b/packages/thirdweb/src/react/web/ui/Bridge/TransactionWidget.tsx
@@ -7,6 +7,7 @@ import type { Chain } from "../../../../chains/types.js";
import type { ThirdwebClient } from "../../../../client/client.js";
import { NATIVE_TOKEN_ADDRESS } from "../../../../constants/addresses.js";
import { getToken } from "../../../../pay/convert/get-token.js";
+import type { SupportedFiatCurrency } from "../../../../pay/convert/type.js";
import type { PurchaseData } from "../../../../pay/types.js";
import {
type PreparedTransaction,
@@ -182,6 +183,12 @@ export type TransactionWidgetProps = {
* @default ["crypto", "card"]
*/
paymentMethods?: ("crypto" | "card")[];
+
+ /**
+ * The currency to use for the payment.
+ * @default "USD"
+ */
+ currency?: SupportedFiatCurrency;
};
// Enhanced UIOptions to handle unsupported token state
@@ -341,6 +348,7 @@ export function TransactionWidget(props: TransactionWidgetProps) {
return {
data: {
+ currency: props.currency || "USD",
metadata: {
description: props.description,
image: props.image,
diff --git a/packages/thirdweb/src/react/web/ui/Bridge/common/TokenBalanceRow.tsx b/packages/thirdweb/src/react/web/ui/Bridge/common/TokenBalanceRow.tsx
index c51001a8081..84a77e39a61 100644
--- a/packages/thirdweb/src/react/web/ui/Bridge/common/TokenBalanceRow.tsx
+++ b/packages/thirdweb/src/react/web/ui/Bridge/common/TokenBalanceRow.tsx
@@ -2,6 +2,7 @@ import styled from "@emotion/styled";
import type { Token } from "../../../../../bridge/index.js";
import { getCachedChain } from "../../../../../chains/utils.js";
import type { ThirdwebClient } from "../../../../../client/client.js";
+import type { SupportedFiatCurrency } from "../../../../../pay/convert/type.js";
import { useCustomTheme } from "../../../../core/design-system/CustomThemeProvider.js";
import { spacing } from "../../../../core/design-system/index.js";
import { FiatValue } from "../../ConnectWallet/screens/Buy/swap/FiatValue.js";
@@ -16,12 +17,14 @@ export function TokenBalanceRow({
amount,
onClick,
style,
+ currency,
}: {
client: ThirdwebClient;
token: Token;
amount: string;
onClick: (token: Token) => void;
style?: React.CSSProperties;
+ currency?: SupportedFiatCurrency;
}) {
const chain = getCachedChain(token.chainId);
return (
@@ -66,6 +69,7 @@ export function TokenBalanceRow({
}}
>
{}}
@@ -158,6 +159,7 @@ export function PaymentOverview(props: {
style={{ alignItems: "flex-end" }}
>
{}}
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 a41d8984052..48eb6f17cd4 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
@@ -1,6 +1,7 @@
"use client";
import { useMemo } from "react";
import type { ThirdwebClient } from "../../../../../client/client.js";
+import type { SupportedFiatCurrency } from "../../../../../pay/convert/type.js";
import { checksumAddress } from "../../../../../utils/address.js";
import { toTokens } from "../../../../../utils/units.js";
import { useCustomTheme } from "../../../../core/design-system/CustomThemeProvider.js";
@@ -24,6 +25,7 @@ interface FiatProviderSelectionProps {
toTokenAddress: string;
toAddress: string;
toAmount?: string;
+ currency?: SupportedFiatCurrency;
}
const PROVIDERS = [
@@ -54,6 +56,7 @@ export function FiatProviderSelection({
toTokenAddress,
toAddress,
toAmount,
+ currency,
}: FiatProviderSelectionProps) {
const theme = useCustomTheme();
@@ -62,7 +65,7 @@ export function FiatProviderSelection({
amount: toAmount || "0",
chainId: toChainId,
client,
- currency: "USD",
+ currency: currency || "USD",
receiver: checksumAddress(toAddress),
tokenAddress: checksumAddress(toTokenAddress),
});
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 59c872a4a67..1b87157ba5c 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
@@ -5,6 +5,7 @@ import { trackPayEvent } from "../../../../../analytics/track/pay.js";
import type { Token } from "../../../../../bridge/types/Token.js";
import { defineChain } from "../../../../../chains/utils.js";
import type { ThirdwebClient } from "../../../../../client/client.js";
+import type { SupportedFiatCurrency } from "../../../../../pay/convert/type.js";
import type { Address } from "../../../../../utils/address.js";
import { toUnits } from "../../../../../utils/units.js";
import type { Wallet } from "../../../../../wallets/interfaces/wallet.js";
@@ -82,6 +83,12 @@ export interface PaymentSelectionProps {
* Fee payer
*/
feePayer?: "sender" | "receiver";
+
+ /**
+ * The currency to use for the payment.
+ * @default "USD"
+ */
+ currency?: SupportedFiatCurrency;
}
type Step =
@@ -103,6 +110,7 @@ export function PaymentSelection({
includeDestinationToken,
paymentMethods = ["crypto", "card"],
feePayer,
+ currency,
}: PaymentSelectionProps) {
const connectedWallets = useConnectedWallets();
const activeWallet = useActiveWallet();
@@ -290,6 +298,7 @@ export function PaymentSelection({
toAmount={destinationAmount}
toChainId={destinationToken.chainId}
toTokenAddress={destinationToken.address}
+ currency={currency}
/>
)}
diff --git a/packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/swap/FiatValue.tsx b/packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/swap/FiatValue.tsx
index 5162bac27f9..721eb7999dc 100644
--- a/packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/swap/FiatValue.tsx
+++ b/packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/swap/FiatValue.tsx
@@ -2,12 +2,13 @@ import { useQuery } from "@tanstack/react-query";
import type { Chain } from "../../../../../../../chains/types.js";
import type { ThirdwebClient } from "../../../../../../../client/client.js";
import { convertCryptoToFiat } from "../../../../../../../pay/convert/cryptoToFiat.js";
-import { formatNumber } from "../../../../../../../utils/formatNumber.js";
+import type { SupportedFiatCurrency } from "../../../../../../../pay/convert/type.js";
import { fontSize } from "../../../../../../core/design-system/index.js";
import { Skeleton } from "../../../../components/Skeleton.js";
import type { TextProps } from "../../../../components/text.js";
import { Text } from "../../../../components/text.js";
import { useDebouncedValue } from "../../../../hooks/useDebouncedValue.js";
+import { formatCurrencyAmount } from "../../formatTokenBalance.js";
import type { ERC20OrNativeToken } from "../../nativeToken.js";
import { getTokenAddress } from "../../nativeToken.js";
@@ -17,6 +18,7 @@ export function FiatValue(
token: ERC20OrNativeToken;
chain: Chain;
client: ThirdwebClient;
+ currency?: SupportedFiatCurrency;
} & TextProps,
) {
const deferredTokenAmount = useDebouncedValue(props.tokenAmount, 500);
@@ -27,7 +29,7 @@ export function FiatValue(
client: props.client,
fromAmount: Number(deferredTokenAmount),
fromTokenAddress: getTokenAddress(props.token),
- to: "USD",
+ to: props.currency || "USD",
}),
queryKey: [
"cryptoToFiat",
@@ -43,13 +45,10 @@ export function FiatValue(
return cryptoToFiatQuery.data?.result ? (
- $
- {Number(
- formatNumber(cryptoToFiatQuery.data.result, 2).toFixed(2),
- ).toLocaleString(undefined, {
- maximumFractionDigits: 2,
- minimumFractionDigits: 2,
- })}
+ {formatCurrencyAmount(
+ props.currency || "USD",
+ cryptoToFiatQuery.data.result,
+ )}
) : null;
}
diff --git a/packages/thirdweb/src/react/web/ui/ConnectWallet/screens/formatTokenBalance.ts b/packages/thirdweb/src/react/web/ui/ConnectWallet/screens/formatTokenBalance.ts
index d5a9cca35d2..e8a107d7f74 100644
--- a/packages/thirdweb/src/react/web/ui/ConnectWallet/screens/formatTokenBalance.ts
+++ b/packages/thirdweb/src/react/web/ui/ConnectWallet/screens/formatTokenBalance.ts
@@ -1,6 +1,5 @@
import { formatNumber } from "../../../../../utils/formatNumber.js";
import { toTokens } from "../../../../../utils/units.js";
-import { getCurrencyMeta } from "./Buy/fiat/currencies.js";
/**
* @internal
@@ -34,11 +33,17 @@ export function formatTokenAmount(
).toString();
}
-export function formatCurrencyAmount(
- currency: string,
- amount: number,
- decimals = 2,
-) {
- const symbol = getCurrencyMeta(currency).symbol;
- return `${symbol}${formatNumber(amount, decimals).toFixed(decimals)}`;
+export function formatCurrencyAmount(currency: string, amount: number) {
+ return formatMoney(amount, "en-US", currency);
+}
+
+function formatMoney(
+ value: number,
+ locale: string,
+ currencyCode: string,
+): string {
+ return new Intl.NumberFormat(locale, {
+ style: "currency",
+ currency: currencyCode,
+ }).format(value);
}
diff --git a/packages/thirdweb/src/stories/Bridge/fixtures.ts b/packages/thirdweb/src/stories/Bridge/fixtures.ts
index 79d9e034ebf..628660198c2 100644
--- a/packages/thirdweb/src/stories/Bridge/fixtures.ts
+++ b/packages/thirdweb/src/stories/Bridge/fixtures.ts
@@ -26,7 +26,9 @@ export const ETH: Token = {
iconUri:
"https://coin-images.coingecko.com/coins/images/279/large/ethereum.png",
name: "Ethereum",
- priceUsd: 1000,
+ prices: {
+ USD: 1000,
+ },
symbol: "ETH",
};
@@ -37,7 +39,9 @@ export const USDC: Token = {
iconUri:
"https://coin-images.coingecko.com/coins/images/6319/large/USD_Coin_icon.png",
name: "USD Coin",
- priceUsd: 1,
+ prices: {
+ USD: 1,
+ },
symbol: "USDC",
};
@@ -48,7 +52,9 @@ export const UNI: Token = {
iconUri:
"https://coin-images.coingecko.com/coins/images/12504/large/uniswap-uni.png",
name: "Uniswap",
- priceUsd: 1000,
+ prices: {
+ USD: 1000,
+ },
symbol: "UNI",
};
@@ -93,7 +99,9 @@ export const simpleOnrampQuote: BridgePrepareResult = JSON.parse(
iconUri:
"https://assets.coingecko.com/coins/images/6319/large/USD_Coin_icon.png",
name: "USD Coin (PoS)",
- priceUsd: 1.0,
+ prices: {
+ USD: 1.0,
+ },
symbol: "USDC",
},
id: "onramp-simple-123",
@@ -122,7 +130,9 @@ export const onrampWithSwapsQuote: BridgePrepareResult = JSON.parse(
chainId: 1,
decimals: 18,
name: "Ethereum",
- priceUsd: 2500.0,
+ prices: {
+ USD: 2500.0,
+ },
symbol: "ETH",
},
id: "onramp-swaps-456",
@@ -143,7 +153,9 @@ export const onrampWithSwapsQuote: BridgePrepareResult = JSON.parse(
chainId: 137,
decimals: 18,
name: "Wrapped Ether",
- priceUsd: 2500.0,
+ prices: {
+ USD: 2500.0,
+ },
symbol: "WETH",
},
estimatedExecutionTimeMs: 30000, // 110 USDC
@@ -155,7 +167,9 @@ export const onrampWithSwapsQuote: BridgePrepareResult = JSON.parse(
iconUri:
"https://assets.coingecko.com/coins/images/6319/large/USD_Coin_icon.png",
name: "USD Coin (PoS)",
- priceUsd: 1.0,
+ prices: {
+ USD: 1.0,
+ },
symbol: "USDC",
},
transactions: [
@@ -186,7 +200,9 @@ export const onrampWithSwapsQuote: BridgePrepareResult = JSON.parse(
chainId: 1,
decimals: 18,
name: "Ethereum",
- priceUsd: 2500.0,
+ prices: {
+ USD: 2500.0,
+ },
symbol: "ETH",
},
estimatedExecutionTimeMs: 180000, // 0.044 WETH
@@ -196,7 +212,9 @@ export const onrampWithSwapsQuote: BridgePrepareResult = JSON.parse(
chainId: 137,
decimals: 18,
name: "Wrapped Ether",
- priceUsd: 2500.0,
+ prices: {
+ USD: 2500.0,
+ },
symbol: "WETH",
},
transactions: [
@@ -251,7 +269,9 @@ export const simpleBuyQuote: BridgePrepareResult = JSON.parse(
iconUri:
"https://assets.coingecko.com/coins/images/6319/large/USD_Coin_icon.png",
name: "USD Coin",
- priceUsd: 1.0,
+ prices: {
+ USD: 1.0,
+ },
symbol: "USDC",
},
estimatedExecutionTimeMs: 60000,
@@ -263,7 +283,9 @@ export const simpleBuyQuote: BridgePrepareResult = JSON.parse(
iconUri:
"https://assets.coingecko.com/coins/images/6319/large/USD_Coin_icon.png",
name: "Ethereum",
- priceUsd: 2500.0,
+ prices: {
+ USD: 2500.0,
+ },
symbol: "ETH",
},
transactions: [
@@ -309,7 +331,9 @@ export const longTextBuyQuote: BridgePrepareResult = JSON.parse(
iconUri:
"https://assets.coingecko.com/coins/images/6319/large/USD_Coin_icon.png",
name: "USD Coin (USDC.e on Etherlink)",
- priceUsd: 1.0,
+ prices: {
+ USD: 1.0,
+ },
symbol: "USDC.e",
},
estimatedExecutionTimeMs: 60000,