Skip to content

Commit 7ab8808

Browse files
[SDK] Handle very large numbers in BuyWidget component (#7503)
1 parent ec75d95 commit 7ab8808

File tree

5 files changed

+101
-6
lines changed

5 files changed

+101
-6
lines changed

.changeset/legal-fans-enjoy.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+
Handle very large numbers in BuyWidget

packages/thirdweb/src/react/web/ui/Bridge/FundWallet.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { useRef, useState } from "react";
33
import type { Token } from "../../../../bridge/types/Token.js";
44
import type { ThirdwebClient } from "../../../../client/client.js";
55
import { type Address, getAddress } from "../../../../utils/address.js";
6+
import { numberToPlainString } from "../../../../utils/formatNumber.js";
67
import { useCustomTheme } from "../../../core/design-system/CustomThemeProvider.js";
78
import {
89
fontSize,
@@ -112,9 +113,9 @@ export function FundWallet({
112113
// Convert USD amount to token amount using token price
113114
const tokenAmount = usdAmount / uiOptions.destinationToken.priceUsd;
114115
// Format to reasonable decimal places (up to 6 decimals, remove trailing zeros)
115-
const formattedAmount = Number.parseFloat(
116-
tokenAmount.toFixed(6),
117-
).toString();
116+
const formattedAmount = numberToPlainString(
117+
Number.parseFloat(tokenAmount.toFixed(6)),
118+
);
118119
setAmount(formattedAmount);
119120
};
120121

packages/thirdweb/src/react/web/ui/Bridge/common/TokenAndChain.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,10 +138,10 @@ function TokenIconWithFallback(props: {
138138
border: `1px solid ${theme.colors.borderColor}`,
139139
borderRadius: "50%",
140140
display: "flex",
141-
height: `${iconSize.md}px`,
141+
height: `${iconSize[props.size]}px`,
142142
justifyContent: "center",
143143
padding: spacing.xs,
144-
width: `${iconSize.md}px`,
144+
width: `${iconSize[props.size]}px`,
145145
}}
146146
>
147147
<Text

packages/thirdweb/src/utils/format-number.test.ts

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { expect, test } from "vitest";
2-
import { formatNumber } from "./formatNumber.js";
2+
import { formatNumber, numberToPlainString } from "./formatNumber.js";
33

44
test("formatNumber", () => {
55
// no decimals
@@ -27,3 +27,52 @@ test("formatNumber", () => {
2727
expect(formatNumber(0.00000000000009, 3)).toEqual(0.001);
2828
expect(formatNumber(0.00000000000001, 3)).toEqual(0.001);
2929
});
30+
31+
test("numberToPlainString", () => {
32+
// Numbers without exponential notation (should return as-is)
33+
expect(numberToPlainString(123)).toEqual("123");
34+
expect(numberToPlainString(0.123)).toEqual("0.123");
35+
expect(numberToPlainString(0)).toEqual("0");
36+
expect(numberToPlainString(-456)).toEqual("-456");
37+
expect(numberToPlainString(-0.789)).toEqual("-0.789");
38+
39+
// Small numbers with negative exponents
40+
expect(numberToPlainString(1e-1)).toEqual("0.1");
41+
expect(numberToPlainString(1e-2)).toEqual("0.01");
42+
expect(numberToPlainString(1e-3)).toEqual("0.001");
43+
expect(numberToPlainString(1.23e-4)).toEqual("0.000123");
44+
expect(numberToPlainString(1.2345e-6)).toEqual("0.0000012345");
45+
expect(numberToPlainString(5e-10)).toEqual("0.0000000005");
46+
expect(numberToPlainString(-5e-10)).toEqual("-0.0000000005");
47+
48+
// Large numbers with positive exponents - zerosNeeded >= 0
49+
expect(numberToPlainString(1e1)).toEqual("10");
50+
expect(numberToPlainString(1e2)).toEqual("100");
51+
expect(numberToPlainString(1.23e3)).toEqual("1230");
52+
expect(numberToPlainString(1.23e5)).toEqual("123000");
53+
expect(numberToPlainString(5.67e10)).toEqual("56700000000");
54+
55+
// Large numbers with positive exponents - zerosNeeded < 0 (decimal point insertion)
56+
expect(numberToPlainString(1.2345e2)).toEqual("123.45");
57+
expect(numberToPlainString(1.23e1)).toEqual("12.3");
58+
expect(numberToPlainString(9.876e2)).toEqual("987.6");
59+
expect(numberToPlainString(1.23456e3)).toEqual("1234.56");
60+
expect(numberToPlainString(5.4321e1)).toEqual("54.321");
61+
62+
// Edge cases where exponent equals decimal length
63+
expect(numberToPlainString(1.23e2)).toEqual("123");
64+
expect(numberToPlainString(1.234e3)).toEqual("1234");
65+
66+
// Negative numbers
67+
expect(numberToPlainString(-1.2345e2)).toEqual("-123.45");
68+
expect(numberToPlainString(-1.23e-4)).toEqual("-0.000123");
69+
70+
// Very large numbers (JavaScript precision limits apply)
71+
expect(numberToPlainString(1.0523871386385944e21)).toEqual(
72+
"1052387138638594400000",
73+
);
74+
75+
// Numbers that would normally show exponential notation
76+
expect(numberToPlainString(0.0000001)).toEqual("0.0000001");
77+
expect(numberToPlainString(10000000)).toEqual("10000000");
78+
});

packages/thirdweb/src/utils/formatNumber.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,43 @@ export function formatNumber(value: number, decimalPlaces: number) {
1414
const fn: "ceil" | "round" = value < threshold ? "ceil" : "round";
1515
return Math[fn]((value + Number.EPSILON) * precision) / precision;
1616
}
17+
18+
/**
19+
* Convert a number to a plain string, removing exponential notation
20+
* @internal
21+
*/
22+
export function numberToPlainString(num: number) {
23+
const str = num.toString();
24+
25+
// If no exponential notation, return as-is
26+
if (str.indexOf("e") === -1) {
27+
return str;
28+
}
29+
30+
// Parse exponential notation
31+
const [rawCoeff, rawExp = "0"] = str.split("e");
32+
const exponent = parseInt(rawExp, 10);
33+
// Separate sign and absolute coefficient
34+
const sign = rawCoeff?.startsWith("-") ? "-" : "";
35+
const coefficient = rawCoeff?.replace(/^[-+]/, "") || "";
36+
// Handle negative exponents (small numbers)
37+
if (exponent < 0) {
38+
const zeros = "0".repeat(Math.abs(exponent) - 1);
39+
const digits = coefficient.replace(".", "");
40+
return `${sign}0.${zeros}${digits}`;
41+
}
42+
43+
// Handle positive exponents (large numbers)
44+
const [integer, decimal = ""] = coefficient?.split(".") || [];
45+
const zerosNeeded = exponent - decimal.length;
46+
47+
if (zerosNeeded >= 0) {
48+
return `${integer}${decimal}${"0".repeat(zerosNeeded)}`;
49+
} else {
50+
// When exponent < decimal.length, we need to insert decimal point
51+
// at the correct position: integer.length + exponent
52+
const insertAt = (integer?.length ?? 0) + exponent;
53+
const result = integer + decimal;
54+
return `${result.slice(0, insertAt)}.${result.slice(insertAt)}`;
55+
}
56+
}

0 commit comments

Comments
 (0)