thirdweb 5.106.1 → 5.107.1-nightly-d53f8a25b4978a9f92f494f50955765817e31bad-20250920000329
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/bridge/Token.js +4 -1
- package/dist/cjs/bridge/Token.js.map +1 -1
- package/dist/cjs/exports/x402.js +8 -0
- package/dist/cjs/exports/x402.js.map +1 -0
- package/dist/cjs/react/core/design-system/index.js +1 -0
- package/dist/cjs/react/core/design-system/index.js.map +1 -1
- package/dist/cjs/react/web/ui/Bridge/BuyWidget.js +1 -1
- package/dist/cjs/react/web/ui/Bridge/CheckoutWidget.js +1 -1
- package/dist/cjs/react/web/ui/Bridge/TransactionWidget.js +1 -1
- package/dist/cjs/react/web/ui/Bridge/swap-widget/SearchInput.js +1 -1
- package/dist/cjs/react/web/ui/Bridge/swap-widget/SearchInput.js.map +1 -1
- package/dist/cjs/react/web/ui/Bridge/swap-widget/SwapWidget.js +44 -28
- package/dist/cjs/react/web/ui/Bridge/swap-widget/SwapWidget.js.map +1 -1
- package/dist/cjs/react/web/ui/Bridge/swap-widget/select-chain.js +9 -7
- package/dist/cjs/react/web/ui/Bridge/swap-widget/select-chain.js.map +1 -1
- package/dist/cjs/react/web/ui/Bridge/swap-widget/select-token-ui.js +82 -46
- package/dist/cjs/react/web/ui/Bridge/swap-widget/select-token-ui.js.map +1 -1
- package/dist/cjs/react/web/ui/Bridge/swap-widget/swap-ui.js +215 -80
- package/dist/cjs/react/web/ui/Bridge/swap-widget/swap-ui.js.map +1 -1
- package/dist/cjs/react/web/ui/Bridge/swap-widget/utils.js +6 -0
- package/dist/cjs/react/web/ui/Bridge/swap-widget/utils.js.map +1 -1
- package/dist/cjs/react/web/ui/ConnectWallet/icons/ArrowUpDownIcon.js +1 -1
- package/dist/cjs/react/web/ui/ConnectWallet/icons/ArrowUpDownIcon.js.map +1 -1
- package/dist/cjs/react/web/ui/components/buttons.js +5 -2
- package/dist/cjs/react/web/ui/components/buttons.js.map +1 -1
- package/dist/cjs/react/web/ui/components/formElements.js +4 -1
- package/dist/cjs/react/web/ui/components/formElements.js.map +1 -1
- package/dist/cjs/react/web/ui/hooks/useisMobile.js +19 -0
- package/dist/cjs/react/web/ui/hooks/useisMobile.js.map +1 -0
- package/dist/cjs/stories/Bridge/Swap/SelectChain.stories.js +16 -6
- package/dist/cjs/stories/Bridge/Swap/SelectChain.stories.js.map +1 -1
- package/dist/cjs/stories/Bridge/Swap/SwapWidget.stories.js +6 -11
- package/dist/cjs/stories/Bridge/Swap/SwapWidget.stories.js.map +1 -1
- package/dist/cjs/version.js +1 -1
- package/dist/cjs/version.js.map +1 -1
- package/dist/cjs/x402/facilitator.js +76 -0
- package/dist/cjs/x402/facilitator.js.map +1 -0
- package/dist/cjs/x402/fetchWithPayment.js +121 -0
- package/dist/cjs/x402/fetchWithPayment.js.map +1 -0
- package/dist/esm/bridge/Token.js +4 -1
- package/dist/esm/bridge/Token.js.map +1 -1
- package/dist/esm/exports/x402.js +3 -0
- package/dist/esm/exports/x402.js.map +1 -0
- package/dist/esm/react/core/design-system/index.js +1 -0
- package/dist/esm/react/core/design-system/index.js.map +1 -1
- package/dist/esm/react/web/ui/Bridge/BuyWidget.js +1 -1
- package/dist/esm/react/web/ui/Bridge/CheckoutWidget.js +1 -1
- package/dist/esm/react/web/ui/Bridge/TransactionWidget.js +1 -1
- package/dist/esm/react/web/ui/Bridge/swap-widget/SearchInput.js +1 -1
- package/dist/esm/react/web/ui/Bridge/swap-widget/SearchInput.js.map +1 -1
- package/dist/esm/react/web/ui/Bridge/swap-widget/SwapWidget.js +44 -28
- package/dist/esm/react/web/ui/Bridge/swap-widget/SwapWidget.js.map +1 -1
- package/dist/esm/react/web/ui/Bridge/swap-widget/select-chain.js +11 -9
- package/dist/esm/react/web/ui/Bridge/swap-widget/select-chain.js.map +1 -1
- package/dist/esm/react/web/ui/Bridge/swap-widget/select-token-ui.js +83 -47
- package/dist/esm/react/web/ui/Bridge/swap-widget/select-token-ui.js.map +1 -1
- package/dist/esm/react/web/ui/Bridge/swap-widget/swap-ui.js +218 -83
- package/dist/esm/react/web/ui/Bridge/swap-widget/swap-ui.js.map +1 -1
- package/dist/esm/react/web/ui/Bridge/swap-widget/utils.js +5 -0
- package/dist/esm/react/web/ui/Bridge/swap-widget/utils.js.map +1 -1
- package/dist/esm/react/web/ui/ConnectWallet/icons/ArrowUpDownIcon.js +2 -2
- package/dist/esm/react/web/ui/ConnectWallet/icons/ArrowUpDownIcon.js.map +1 -1
- package/dist/esm/react/web/ui/components/buttons.js +5 -2
- package/dist/esm/react/web/ui/components/buttons.js.map +1 -1
- package/dist/esm/react/web/ui/components/formElements.js +5 -2
- package/dist/esm/react/web/ui/components/formElements.js.map +1 -1
- package/dist/esm/react/web/ui/hooks/useisMobile.js +16 -0
- package/dist/esm/react/web/ui/hooks/useisMobile.js.map +1 -0
- package/dist/esm/stories/Bridge/Swap/SelectChain.stories.js +12 -4
- package/dist/esm/stories/Bridge/Swap/SelectChain.stories.js.map +1 -1
- package/dist/esm/stories/Bridge/Swap/SwapWidget.stories.js +7 -12
- package/dist/esm/stories/Bridge/Swap/SwapWidget.stories.js.map +1 -1
- package/dist/esm/version.js +1 -1
- package/dist/esm/version.js.map +1 -1
- package/dist/esm/x402/facilitator.js +73 -0
- package/dist/esm/x402/facilitator.js.map +1 -0
- package/dist/esm/x402/fetchWithPayment.js +118 -0
- package/dist/esm/x402/fetchWithPayment.js.map +1 -0
- package/dist/types/bridge/Token.d.ts +2 -0
- package/dist/types/bridge/Token.d.ts.map +1 -1
- package/dist/types/bridge/types/Token.d.ts +2 -0
- package/dist/types/bridge/types/Token.d.ts.map +1 -1
- package/dist/types/exports/x402.d.ts +3 -0
- package/dist/types/exports/x402.d.ts.map +1 -0
- package/dist/types/react/core/design-system/index.d.ts +1 -0
- package/dist/types/react/core/design-system/index.d.ts.map +1 -1
- package/dist/types/react/web/ui/Bridge/BuyWidget.d.ts +1 -1
- package/dist/types/react/web/ui/Bridge/CheckoutWidget.d.ts +1 -1
- package/dist/types/react/web/ui/Bridge/TransactionWidget.d.ts +1 -1
- package/dist/types/react/web/ui/Bridge/swap-widget/SearchInput.d.ts.map +1 -1
- package/dist/types/react/web/ui/Bridge/swap-widget/SwapWidget.d.ts +5 -0
- package/dist/types/react/web/ui/Bridge/swap-widget/SwapWidget.d.ts.map +1 -1
- package/dist/types/react/web/ui/Bridge/swap-widget/select-chain.d.ts +1 -0
- package/dist/types/react/web/ui/Bridge/swap-widget/select-chain.d.ts.map +1 -1
- package/dist/types/react/web/ui/Bridge/swap-widget/select-token-ui.d.ts.map +1 -1
- package/dist/types/react/web/ui/Bridge/swap-widget/swap-ui.d.ts +2 -1
- package/dist/types/react/web/ui/Bridge/swap-widget/swap-ui.d.ts.map +1 -1
- package/dist/types/react/web/ui/Bridge/swap-widget/utils.d.ts +1 -0
- package/dist/types/react/web/ui/Bridge/swap-widget/utils.d.ts.map +1 -1
- package/dist/types/react/web/ui/components/basic.d.ts +1 -1
- package/dist/types/react/web/ui/components/basic.d.ts.map +1 -1
- package/dist/types/react/web/ui/components/buttons.d.ts +1 -0
- package/dist/types/react/web/ui/components/buttons.d.ts.map +1 -1
- package/dist/types/react/web/ui/components/formElements.d.ts.map +1 -1
- package/dist/types/react/web/ui/hooks/useisMobile.d.ts +2 -0
- package/dist/types/react/web/ui/hooks/useisMobile.d.ts.map +1 -0
- package/dist/types/stories/Bridge/Swap/SelectChain.stories.d.ts +4 -2
- package/dist/types/stories/Bridge/Swap/SelectChain.stories.d.ts.map +1 -1
- package/dist/types/stories/Bridge/Swap/SwapWidget.stories.d.ts.map +1 -1
- package/dist/types/version.d.ts +1 -1
- package/dist/types/version.d.ts.map +1 -1
- package/dist/types/x402/facilitator.d.ts +48 -0
- package/dist/types/x402/facilitator.d.ts.map +1 -0
- package/dist/types/x402/fetchWithPayment.d.ts +43 -0
- package/dist/types/x402/fetchWithPayment.d.ts.map +1 -0
- package/package.json +10 -1
- package/src/bridge/Token.ts +6 -0
- package/src/bridge/types/Token.ts +2 -0
- package/src/exports/x402.ts +5 -0
- package/src/react/core/design-system/index.ts +1 -0
- package/src/react/web/ui/Bridge/BuyWidget.tsx +1 -1
- package/src/react/web/ui/Bridge/CheckoutWidget.tsx +1 -1
- package/src/react/web/ui/Bridge/TransactionWidget.tsx +1 -1
- package/src/react/web/ui/Bridge/swap-widget/SearchInput.tsx +1 -0
- package/src/react/web/ui/Bridge/swap-widget/SwapWidget.tsx +62 -45
- package/src/react/web/ui/Bridge/swap-widget/select-chain.tsx +25 -12
- package/src/react/web/ui/Bridge/swap-widget/select-token-ui.tsx +302 -197
- package/src/react/web/ui/Bridge/swap-widget/swap-ui.tsx +496 -233
- package/src/react/web/ui/Bridge/swap-widget/utils.ts +6 -0
- package/src/react/web/ui/ConnectWallet/icons/ArrowUpDownIcon.tsx +10 -10
- package/src/react/web/ui/components/basic.tsx +1 -1
- package/src/react/web/ui/components/buttons.tsx +6 -2
- package/src/react/web/ui/components/formElements.tsx +5 -1
- package/src/react/web/ui/hooks/useisMobile.ts +21 -0
- package/src/stories/Bridge/Swap/SelectChain.stories.tsx +40 -2
- package/src/stories/Bridge/Swap/SwapWidget.stories.tsx +18 -13
- package/src/version.ts +1 -1
- package/src/x402/facilitator.ts +87 -0
- package/src/x402/fetchWithPayment.ts +173 -0
- package/dist/cjs/react/web/ui/Bridge/swap-widget/common.js +0 -17
- package/dist/cjs/react/web/ui/Bridge/swap-widget/common.js.map +0 -1
- package/dist/esm/react/web/ui/Bridge/swap-widget/common.js +0 -14
- package/dist/esm/react/web/ui/Bridge/swap-widget/common.js.map +0 -1
- package/dist/types/react/web/ui/Bridge/swap-widget/common.d.ts +0 -9
- package/dist/types/react/web/ui/Bridge/swap-widget/common.d.ts.map +0 -1
- package/src/react/web/ui/Bridge/swap-widget/common.tsx +0 -35
|
@@ -3,21 +3,21 @@ import type { IconFC } from "./types.js";
|
|
|
3
3
|
export const ArrowUpDownIcon: IconFC = (props) => {
|
|
4
4
|
return (
|
|
5
5
|
<svg
|
|
6
|
-
|
|
7
|
-
viewBox="0 0 24 24"
|
|
6
|
+
viewBox="0 0 16 16"
|
|
8
7
|
fill="none"
|
|
9
|
-
|
|
10
|
-
strokeWidth="2"
|
|
11
|
-
strokeLinecap="round"
|
|
12
|
-
strokeLinejoin="round"
|
|
8
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
13
9
|
role="presentation"
|
|
14
10
|
width={props.size}
|
|
15
11
|
height={props.size}
|
|
16
12
|
>
|
|
17
|
-
<
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
13
|
+
<g
|
|
14
|
+
stroke="currentColor"
|
|
15
|
+
strokeWidth="1"
|
|
16
|
+
strokeLinecap="round"
|
|
17
|
+
strokeLinejoin="round"
|
|
18
|
+
>
|
|
19
|
+
<path d="M9.599 2.4v11.2l3.2-3.476M6.4 13.6V2.4L3.2 5.875"></path>
|
|
20
|
+
</g>
|
|
21
21
|
</svg>
|
|
22
22
|
);
|
|
23
23
|
};
|
|
@@ -84,7 +84,7 @@ export function Container(props: {
|
|
|
84
84
|
expand?: boolean;
|
|
85
85
|
center?: "x" | "y" | "both";
|
|
86
86
|
gap?: keyof typeof spacing;
|
|
87
|
-
children
|
|
87
|
+
children?: React.ReactNode;
|
|
88
88
|
style?: React.CSSProperties;
|
|
89
89
|
p?: keyof typeof spacing;
|
|
90
90
|
px?: keyof typeof spacing;
|
|
@@ -21,6 +21,7 @@ type ButtonProps = {
|
|
|
21
21
|
fullWidth?: boolean;
|
|
22
22
|
gap?: keyof typeof spacing;
|
|
23
23
|
bg?: keyof Theme["colors"];
|
|
24
|
+
hoverBg?: keyof Theme["colors"];
|
|
24
25
|
};
|
|
25
26
|
|
|
26
27
|
export const Button = /* @__PURE__ */ StyledButton((props: ButtonProps) => {
|
|
@@ -95,6 +96,9 @@ export const Button = /* @__PURE__ */ StyledButton((props: ButtonProps) => {
|
|
|
95
96
|
transition: "border 200ms ease",
|
|
96
97
|
WebkitTapHighlightColor: "transparent",
|
|
97
98
|
width: props.fullWidth ? "100%" : undefined,
|
|
99
|
+
"&:hover": {
|
|
100
|
+
background: props.hoverBg ? theme.colors[props.hoverBg] : undefined,
|
|
101
|
+
},
|
|
98
102
|
...(() => {
|
|
99
103
|
if (props.variant === "outline") {
|
|
100
104
|
return {
|
|
@@ -120,7 +124,7 @@ export const Button = /* @__PURE__ */ StyledButton((props: ButtonProps) => {
|
|
|
120
124
|
if (props.variant === "ghost-solid") {
|
|
121
125
|
return {
|
|
122
126
|
"&:hover": {
|
|
123
|
-
background: theme.colors.tertiaryBg,
|
|
127
|
+
background: theme.colors[props.hoverBg || "tertiaryBg"],
|
|
124
128
|
},
|
|
125
129
|
border: "1px solid transparent",
|
|
126
130
|
};
|
|
@@ -137,7 +141,7 @@ export const Button = /* @__PURE__ */ StyledButton((props: ButtonProps) => {
|
|
|
137
141
|
if (props.variant === "secondary") {
|
|
138
142
|
return {
|
|
139
143
|
"&:hover": {
|
|
140
|
-
background: theme.colors.secondaryButtonHoverBg,
|
|
144
|
+
background: theme.colors[props.hoverBg || "secondaryButtonHoverBg"],
|
|
141
145
|
},
|
|
142
146
|
};
|
|
143
147
|
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { useCustomTheme } from "../../../core/design-system/CustomThemeProvider.js";
|
|
3
3
|
import {
|
|
4
4
|
fontSize,
|
|
5
|
+
media,
|
|
5
6
|
radius,
|
|
6
7
|
spacing,
|
|
7
8
|
type Theme,
|
|
@@ -97,11 +98,14 @@ export const Input = /* @__PURE__ */ StyledInput<InputProps>((props) => {
|
|
|
97
98
|
color: theme.colors.primaryText,
|
|
98
99
|
display: "block",
|
|
99
100
|
fontFamily: "inherit",
|
|
100
|
-
fontSize: fontSize.
|
|
101
|
+
fontSize: fontSize.sm,
|
|
101
102
|
outline: "none",
|
|
102
103
|
padding: props.sm ? spacing.sm : fontSize.sm,
|
|
103
104
|
WebkitAppearance: "none",
|
|
104
105
|
width: "100%",
|
|
106
|
+
[media.mobile]: {
|
|
107
|
+
fontSize: fontSize.md,
|
|
108
|
+
},
|
|
105
109
|
};
|
|
106
110
|
});
|
|
107
111
|
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
|
|
3
|
+
const MOBILE_BREAKPOINT = 640;
|
|
4
|
+
|
|
5
|
+
export function useIsMobile() {
|
|
6
|
+
const [isMobile, setIsMobile] = React.useState<boolean | undefined>(
|
|
7
|
+
undefined,
|
|
8
|
+
);
|
|
9
|
+
|
|
10
|
+
React.useEffect(() => {
|
|
11
|
+
const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`);
|
|
12
|
+
const onChange = () => {
|
|
13
|
+
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
|
|
14
|
+
};
|
|
15
|
+
mql.addEventListener("change", onChange);
|
|
16
|
+
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
|
|
17
|
+
return () => mql.removeEventListener("change", onChange);
|
|
18
|
+
}, []);
|
|
19
|
+
|
|
20
|
+
return !!isMobile;
|
|
21
|
+
}
|
|
@@ -16,13 +16,14 @@ const meta = {
|
|
|
16
16
|
} satisfies Meta<typeof SelectBridgeChain>;
|
|
17
17
|
export default meta;
|
|
18
18
|
|
|
19
|
-
export function
|
|
19
|
+
export function WithDataDesktop() {
|
|
20
20
|
const [selectedChain, setSelectedChain] = useState<BridgeChain | undefined>(
|
|
21
21
|
undefined,
|
|
22
22
|
);
|
|
23
23
|
return (
|
|
24
24
|
<SwapWidgetContainer theme="dark" className="w-full">
|
|
25
25
|
<SelectBridgeChain
|
|
26
|
+
isMobile={false}
|
|
26
27
|
client={storyClient}
|
|
27
28
|
onSelectChain={setSelectedChain}
|
|
28
29
|
onBack={() => {}}
|
|
@@ -32,13 +33,50 @@ export function WithData() {
|
|
|
32
33
|
);
|
|
33
34
|
}
|
|
34
35
|
|
|
35
|
-
export function
|
|
36
|
+
export function LoadingDesktop() {
|
|
36
37
|
const [selectedChain, setSelectedChain] = useState<BridgeChain | undefined>(
|
|
37
38
|
undefined,
|
|
38
39
|
);
|
|
39
40
|
return (
|
|
40
41
|
<SwapWidgetContainer theme="dark" className="w-full">
|
|
41
42
|
<SelectBridgeChainUI
|
|
43
|
+
isMobile={false}
|
|
44
|
+
client={storyClient}
|
|
45
|
+
onSelectChain={setSelectedChain}
|
|
46
|
+
onBack={() => {}}
|
|
47
|
+
isPending={true}
|
|
48
|
+
chains={[]}
|
|
49
|
+
selectedChain={selectedChain}
|
|
50
|
+
/>
|
|
51
|
+
</SwapWidgetContainer>
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function WithDataMobile() {
|
|
56
|
+
const [selectedChain, setSelectedChain] = useState<BridgeChain | undefined>(
|
|
57
|
+
undefined,
|
|
58
|
+
);
|
|
59
|
+
return (
|
|
60
|
+
<SwapWidgetContainer theme="dark" className="w-full">
|
|
61
|
+
<SelectBridgeChain
|
|
62
|
+
isMobile={true}
|
|
63
|
+
client={storyClient}
|
|
64
|
+
onSelectChain={setSelectedChain}
|
|
65
|
+
onBack={() => {}}
|
|
66
|
+
selectedChain={selectedChain}
|
|
67
|
+
/>
|
|
68
|
+
</SwapWidgetContainer>
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function LoadingMobile() {
|
|
73
|
+
const [selectedChain, setSelectedChain] = useState<BridgeChain | undefined>(
|
|
74
|
+
undefined,
|
|
75
|
+
);
|
|
76
|
+
return (
|
|
77
|
+
<SwapWidgetContainer theme="dark" className="w-full">
|
|
78
|
+
<SelectBridgeChainUI
|
|
79
|
+
isMobile={true}
|
|
42
80
|
client={storyClient}
|
|
43
81
|
onSelectChain={setSelectedChain}
|
|
44
82
|
onBack={() => {}}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import type { Meta } from "@storybook/react";
|
|
2
2
|
import { lightTheme } from "../../../react/core/design-system/index.js";
|
|
3
3
|
import { SwapWidget } from "../../../react/web/ui/Bridge/swap-widget/SwapWidget.js";
|
|
4
|
-
import { ConnectButton } from "../../../react/web/ui/ConnectWallet/ConnectButton.js";
|
|
5
4
|
import { storyClient } from "../../utils.js";
|
|
6
5
|
|
|
7
6
|
const meta: Meta<typeof SwapWidget> = {
|
|
@@ -14,15 +13,6 @@ const meta: Meta<typeof SwapWidget> = {
|
|
|
14
13
|
return (
|
|
15
14
|
<div>
|
|
16
15
|
<Story />
|
|
17
|
-
<div
|
|
18
|
-
style={{
|
|
19
|
-
position: "absolute",
|
|
20
|
-
bottom: "20px",
|
|
21
|
-
right: "20px",
|
|
22
|
-
}}
|
|
23
|
-
>
|
|
24
|
-
<ConnectButton client={storyClient} />
|
|
25
|
-
</div>
|
|
26
16
|
</div>
|
|
27
17
|
);
|
|
28
18
|
},
|
|
@@ -31,15 +21,28 @@ const meta: Meta<typeof SwapWidget> = {
|
|
|
31
21
|
export default meta;
|
|
32
22
|
|
|
33
23
|
export function BasicUsage() {
|
|
34
|
-
return <SwapWidget client={storyClient} />;
|
|
24
|
+
return <SwapWidget client={storyClient} persistTokenSelections={false} />;
|
|
35
25
|
}
|
|
36
26
|
|
|
37
27
|
export function CurrencySet() {
|
|
38
|
-
return
|
|
28
|
+
return (
|
|
29
|
+
<SwapWidget
|
|
30
|
+
client={storyClient}
|
|
31
|
+
currency="JPY"
|
|
32
|
+
persistTokenSelections={false}
|
|
33
|
+
/>
|
|
34
|
+
);
|
|
39
35
|
}
|
|
40
36
|
|
|
41
37
|
export function LightMode() {
|
|
42
|
-
return
|
|
38
|
+
return (
|
|
39
|
+
<SwapWidget
|
|
40
|
+
client={storyClient}
|
|
41
|
+
currency="JPY"
|
|
42
|
+
theme="light"
|
|
43
|
+
persistTokenSelections={false}
|
|
44
|
+
/>
|
|
45
|
+
);
|
|
43
46
|
}
|
|
44
47
|
|
|
45
48
|
export function NoThirdwebBranding() {
|
|
@@ -48,6 +51,7 @@ export function NoThirdwebBranding() {
|
|
|
48
51
|
client={storyClient}
|
|
49
52
|
currency="JPY"
|
|
50
53
|
showThirdwebBranding={false}
|
|
54
|
+
persistTokenSelections={false}
|
|
51
55
|
/>
|
|
52
56
|
);
|
|
53
57
|
}
|
|
@@ -57,6 +61,7 @@ export function CustomTheme() {
|
|
|
57
61
|
<SwapWidget
|
|
58
62
|
client={storyClient}
|
|
59
63
|
currency="JPY"
|
|
64
|
+
persistTokenSelections={false}
|
|
60
65
|
theme={lightTheme({
|
|
61
66
|
colors: {
|
|
62
67
|
modalBg: "#FFFFF0",
|
package/src/version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const version = "5.
|
|
1
|
+
export const version = "5.107.1-nightly-d53f8a25b4978a9f92f494f50955765817e31bad-20250920000329";
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import type { FacilitatorConfig } from "x402/types";
|
|
2
|
+
import type { ThirdwebClient } from "../client/client.js";
|
|
3
|
+
|
|
4
|
+
export type ThirdwebX402FacilitatorConfig = {
|
|
5
|
+
client: ThirdwebClient;
|
|
6
|
+
serverWalletAddress: string;
|
|
7
|
+
vaultAccessToken?: string;
|
|
8
|
+
baseUrl?: string;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const DEFAULT_BASE_URL = "https://api.thirdweb.com/v1/payments/x402";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Creates a facilitator for the x402 payment protocol.
|
|
15
|
+
* Use this with any x402 middleware to enable settling transactions with your thirdweb server wallet.
|
|
16
|
+
*
|
|
17
|
+
* @param config - The configuration for the facilitator
|
|
18
|
+
* @returns a x402 compatible FacilitatorConfig
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```ts
|
|
22
|
+
* import { facilitator } from "thirdweb/x402";
|
|
23
|
+
* import { createThirdwebClient } from "thirdweb";
|
|
24
|
+
*
|
|
25
|
+
* const client = createThirdwebClient({
|
|
26
|
+
* secretKey: "your-secret-key",
|
|
27
|
+
* });
|
|
28
|
+
* const thirdwebX402Facilitator = facilitator({
|
|
29
|
+
* client: client,
|
|
30
|
+
* serverWalletAddress: "0x1234567890123456789012345678901234567890",
|
|
31
|
+
* });
|
|
32
|
+
*
|
|
33
|
+
* // add the facilitator to any x402 payment middleware
|
|
34
|
+
* const middleware = paymentMiddleware(
|
|
35
|
+
* "0x1234567890123456789012345678901234567890",
|
|
36
|
+
* {
|
|
37
|
+
* "/api/paywall": {
|
|
38
|
+
* price: "$0.01",
|
|
39
|
+
* network: "base-sepolia",
|
|
40
|
+
* config: {
|
|
41
|
+
* description: "Access to paid content",
|
|
42
|
+
* },
|
|
43
|
+
* },
|
|
44
|
+
* },
|
|
45
|
+
* thirdwebX402Facilitator,
|
|
46
|
+
* );
|
|
47
|
+
* ```
|
|
48
|
+
*
|
|
49
|
+
* @bridge x402
|
|
50
|
+
*/
|
|
51
|
+
export function facilitator(
|
|
52
|
+
config: ThirdwebX402FacilitatorConfig,
|
|
53
|
+
): FacilitatorConfig {
|
|
54
|
+
const secretKey = config.client.secretKey;
|
|
55
|
+
if (!secretKey) {
|
|
56
|
+
throw new Error("Client secret key is required for the x402 facilitator");
|
|
57
|
+
}
|
|
58
|
+
const serverWalletAddress = config.serverWalletAddress;
|
|
59
|
+
if (!serverWalletAddress) {
|
|
60
|
+
throw new Error(
|
|
61
|
+
"Server wallet address is required for the x402 facilitator",
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
return {
|
|
65
|
+
url: (config.baseUrl ?? DEFAULT_BASE_URL) as `${string}://${string}`,
|
|
66
|
+
createAuthHeaders: async () => {
|
|
67
|
+
return {
|
|
68
|
+
verify: {
|
|
69
|
+
"x-secret-key": secretKey,
|
|
70
|
+
},
|
|
71
|
+
settle: {
|
|
72
|
+
"x-secret-key": secretKey,
|
|
73
|
+
"x-settlement-wallet-address": serverWalletAddress,
|
|
74
|
+
...(config.vaultAccessToken
|
|
75
|
+
? { "x-vault-access-token": config.vaultAccessToken }
|
|
76
|
+
: {}),
|
|
77
|
+
},
|
|
78
|
+
supported: {
|
|
79
|
+
"x-secret-key": secretKey,
|
|
80
|
+
},
|
|
81
|
+
list: {
|
|
82
|
+
"x-secret-key": secretKey,
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { createPaymentHeader } from "x402/client";
|
|
2
|
+
import {
|
|
3
|
+
ChainIdToNetwork,
|
|
4
|
+
EvmNetworkToChainId,
|
|
5
|
+
type PaymentRequirements,
|
|
6
|
+
PaymentRequirementsSchema,
|
|
7
|
+
type Signer,
|
|
8
|
+
} from "x402/types";
|
|
9
|
+
import { viemAdapter } from "../adapters/viem.js";
|
|
10
|
+
import { getCachedChain } from "../chains/utils.js";
|
|
11
|
+
import type { ThirdwebClient } from "../client/client.js";
|
|
12
|
+
import type { Wallet } from "../wallets/interfaces/wallet.js";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Enables the payment of APIs using the x402 payment protocol.
|
|
16
|
+
*
|
|
17
|
+
* This function wraps the native fetch API to automatically handle 402 Payment Required responses
|
|
18
|
+
* by creating and sending a payment header. It will:
|
|
19
|
+
* 1. Make the initial request
|
|
20
|
+
* 2. If a 402 response is received, parse the payment requirements
|
|
21
|
+
* 3. Verify the payment amount is within the allowed maximum
|
|
22
|
+
* 4. Create a payment header using the provided wallet client
|
|
23
|
+
* 5. Retry the request with the payment header
|
|
24
|
+
*
|
|
25
|
+
* @param fetch - The fetch function to wrap (typically globalThis.fetch)
|
|
26
|
+
* @param client - The thirdweb client used to access RPC infrastructure
|
|
27
|
+
* @param wallet - The wallet used to sign payment messages
|
|
28
|
+
* @param maxValue - The maximum allowed payment amount in base units (defaults to 1 USDC)
|
|
29
|
+
* @returns A wrapped fetch function that handles 402 responses automatically
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```typescript
|
|
33
|
+
* import { wrapFetchWithPayment } from "thirdweb/x402";
|
|
34
|
+
* import { createThirdwebClient } from "thirdweb";
|
|
35
|
+
* import { createWallet } from "thirdweb/wallets";
|
|
36
|
+
*
|
|
37
|
+
* const client = createThirdwebClient({ clientId: "your-client-id" });
|
|
38
|
+
* const wallet = createWallet("io.metamask");
|
|
39
|
+
* await wallet.connect({ client })
|
|
40
|
+
*
|
|
41
|
+
* const fetchWithPay = wrapFetchWithPayment(fetch, client, wallet);
|
|
42
|
+
*
|
|
43
|
+
* // Make a request that may require payment
|
|
44
|
+
* const response = await fetchWithPay('https://api.example.com/paid-endpoint');
|
|
45
|
+
* ```
|
|
46
|
+
*
|
|
47
|
+
* @throws {Error} If the payment amount exceeds the maximum allowed value
|
|
48
|
+
* @throws {Error} If a payment has already been attempted for this request
|
|
49
|
+
* @throws {Error} If there's an error creating the payment header
|
|
50
|
+
*
|
|
51
|
+
* @bridge x402
|
|
52
|
+
*/
|
|
53
|
+
export function wrapFetchWithPayment(
|
|
54
|
+
fetch: typeof globalThis.fetch,
|
|
55
|
+
client: ThirdwebClient,
|
|
56
|
+
wallet: Wallet,
|
|
57
|
+
maxValue: bigint = BigInt(1 * 10 ** 6), // Default to 1 USDC
|
|
58
|
+
) {
|
|
59
|
+
return async (input: RequestInfo, init?: RequestInit) => {
|
|
60
|
+
const response = await fetch(input, init);
|
|
61
|
+
|
|
62
|
+
if (response.status !== 402) {
|
|
63
|
+
return response;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const { x402Version, accepts } = (await response.json()) as {
|
|
67
|
+
x402Version: number;
|
|
68
|
+
accepts: unknown[];
|
|
69
|
+
};
|
|
70
|
+
const parsedPaymentRequirements = accepts
|
|
71
|
+
.map((x) => PaymentRequirementsSchema.parse(x))
|
|
72
|
+
.filter((x) => x.scheme === "exact"); // TODO (402): accept other schemes
|
|
73
|
+
|
|
74
|
+
const account = wallet.getAccount();
|
|
75
|
+
let chain = wallet.getChain();
|
|
76
|
+
|
|
77
|
+
if (!account || !chain) {
|
|
78
|
+
throw new Error(
|
|
79
|
+
"Wallet not connected. Please connect your wallet to continue.",
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
const selectedPaymentRequirements = defaultPaymentRequirementsSelector(
|
|
83
|
+
parsedPaymentRequirements,
|
|
84
|
+
chain.id,
|
|
85
|
+
"exact",
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
if (BigInt(selectedPaymentRequirements.maxAmountRequired) > maxValue) {
|
|
89
|
+
throw new Error("Payment amount exceeds maximum allowed");
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const paymentChainId = EvmNetworkToChainId.get(
|
|
93
|
+
selectedPaymentRequirements.network,
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
if (!paymentChainId) {
|
|
97
|
+
throw new Error(
|
|
98
|
+
`No chain found for the selected payment requirement: ${selectedPaymentRequirements.network}`,
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// switch to the payment chain if it's not the current chain
|
|
103
|
+
if (paymentChainId !== chain.id) {
|
|
104
|
+
await wallet.switchChain(getCachedChain(paymentChainId));
|
|
105
|
+
chain = wallet.getChain();
|
|
106
|
+
if (!chain) {
|
|
107
|
+
throw new Error(`Failed to switch chain (${paymentChainId})`);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const walletClient = viemAdapter.wallet.toViem({
|
|
112
|
+
wallet: wallet,
|
|
113
|
+
chain,
|
|
114
|
+
client,
|
|
115
|
+
}) as Signer;
|
|
116
|
+
|
|
117
|
+
const paymentHeader = await createPaymentHeader(
|
|
118
|
+
walletClient,
|
|
119
|
+
x402Version,
|
|
120
|
+
selectedPaymentRequirements,
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
const initParams = init || {};
|
|
124
|
+
|
|
125
|
+
if ((initParams as { __is402Retry?: boolean }).__is402Retry) {
|
|
126
|
+
throw new Error("Payment already attempted");
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const newInit = {
|
|
130
|
+
...initParams,
|
|
131
|
+
headers: {
|
|
132
|
+
...(initParams.headers || {}),
|
|
133
|
+
"X-PAYMENT": paymentHeader,
|
|
134
|
+
"Access-Control-Expose-Headers": "X-PAYMENT-RESPONSE",
|
|
135
|
+
},
|
|
136
|
+
__is402Retry: true,
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
const secondResponse = await fetch(input, newInit);
|
|
140
|
+
return secondResponse;
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function defaultPaymentRequirementsSelector(
|
|
145
|
+
paymentRequirements: PaymentRequirements[],
|
|
146
|
+
chainId: number,
|
|
147
|
+
scheme: "exact",
|
|
148
|
+
) {
|
|
149
|
+
if (!paymentRequirements.length) {
|
|
150
|
+
throw new Error(
|
|
151
|
+
"No valid payment requirements found in server 402 response",
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
const currentWalletNetwork = ChainIdToNetwork[chainId];
|
|
155
|
+
// find the payment requirements matching the connected wallet chain
|
|
156
|
+
const matchingPaymentRequirements = paymentRequirements.find(
|
|
157
|
+
(x) => x.network === currentWalletNetwork && x.scheme === scheme,
|
|
158
|
+
);
|
|
159
|
+
|
|
160
|
+
if (matchingPaymentRequirements) {
|
|
161
|
+
return matchingPaymentRequirements;
|
|
162
|
+
} else {
|
|
163
|
+
// if no matching payment requirements, use the first payment requirement
|
|
164
|
+
// and switch the wallet to that chain
|
|
165
|
+
const firstPaymentRequirement = paymentRequirements.find(
|
|
166
|
+
(x) => x.scheme === scheme,
|
|
167
|
+
);
|
|
168
|
+
if (!firstPaymentRequirement) {
|
|
169
|
+
throw new Error("No suitable payment requirements found");
|
|
170
|
+
}
|
|
171
|
+
return firstPaymentRequirement;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.DecimalRenderer = DecimalRenderer;
|
|
4
|
-
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
-
const text_js_1 = require("../../components/text.js");
|
|
6
|
-
function DecimalRenderer(props) {
|
|
7
|
-
if (Number(props.value) > 1000) {
|
|
8
|
-
return ((0, jsx_runtime_1.jsx)(text_js_1.Text, { size: props.integerSize, color: props.color, weight: props.weight, children: compactFormatter.format(Number(props.value)) }));
|
|
9
|
-
}
|
|
10
|
-
const [integerPart, fractionPart] = props.value.split(".");
|
|
11
|
-
return ((0, jsx_runtime_1.jsxs)("div", { style: { display: "flex", alignItems: "baseline" }, children: [(0, jsx_runtime_1.jsx)(text_js_1.Text, { size: props.integerSize, color: props.color, weight: props.weight, children: integerPart }), (0, jsx_runtime_1.jsxs)(text_js_1.Text, { size: props.fractionSize, color: props.color, weight: props.weight, children: [".", fractionPart || "00"] })] }));
|
|
12
|
-
}
|
|
13
|
-
const compactFormatter = new Intl.NumberFormat("en-US", {
|
|
14
|
-
notation: "compact",
|
|
15
|
-
maximumFractionDigits: 2,
|
|
16
|
-
});
|
|
17
|
-
//# sourceMappingURL=common.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"common.js","sourceRoot":"","sources":["../../../../../../../src/react/web/ui/Bridge/swap-widget/common.tsx"],"names":[],"mappings":";;AAGA,0CA0BC;;AA5BD,sDAAgD;AAEhD,SAAgB,eAAe,CAAC,KAM/B;IACC,IAAI,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,IAAI,EAAE,CAAC;QAC/B,OAAO,CACL,uBAAC,cAAI,IAAC,IAAI,EAAE,KAAK,CAAC,WAAW,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,YACpE,gBAAgB,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,GACxC,CACR,CAAC;IACJ,CAAC;IACD,MAAM,CAAC,WAAW,EAAE,YAAY,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAE3D,OAAO,CACL,iCAAK,KAAK,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,EAAE,aACrD,uBAAC,cAAI,IAAC,IAAI,EAAE,KAAK,CAAC,WAAW,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,YACpE,WAAW,GACP,EACP,wBAAC,cAAI,IAAC,IAAI,EAAE,KAAK,CAAC,YAAY,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,kBACpE,YAAY,IAAI,IAAI,IACjB,IACH,CACP,CAAC;AACJ,CAAC;AAED,MAAM,gBAAgB,GAAG,IAAI,IAAI,CAAC,YAAY,CAAC,OAAO,EAAE;IACtD,QAAQ,EAAE,SAAS;IACnB,qBAAqB,EAAE,CAAC;CACzB,CAAC,CAAC"}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { Text } from "../../components/text.js";
|
|
3
|
-
export function DecimalRenderer(props) {
|
|
4
|
-
if (Number(props.value) > 1000) {
|
|
5
|
-
return (_jsx(Text, { size: props.integerSize, color: props.color, weight: props.weight, children: compactFormatter.format(Number(props.value)) }));
|
|
6
|
-
}
|
|
7
|
-
const [integerPart, fractionPart] = props.value.split(".");
|
|
8
|
-
return (_jsxs("div", { style: { display: "flex", alignItems: "baseline" }, children: [_jsx(Text, { size: props.integerSize, color: props.color, weight: props.weight, children: integerPart }), _jsxs(Text, { size: props.fractionSize, color: props.color, weight: props.weight, children: [".", fractionPart || "00"] })] }));
|
|
9
|
-
}
|
|
10
|
-
const compactFormatter = new Intl.NumberFormat("en-US", {
|
|
11
|
-
notation: "compact",
|
|
12
|
-
maximumFractionDigits: 2,
|
|
13
|
-
});
|
|
14
|
-
//# sourceMappingURL=common.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"common.js","sourceRoot":"","sources":["../../../../../../../src/react/web/ui/Bridge/swap-widget/common.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,IAAI,EAAE,MAAM,0BAA0B,CAAC;AAEhD,MAAM,UAAU,eAAe,CAAC,KAM/B;IACC,IAAI,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,IAAI,EAAE,CAAC;QAC/B,OAAO,CACL,KAAC,IAAI,IAAC,IAAI,EAAE,KAAK,CAAC,WAAW,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,YACpE,gBAAgB,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,GACxC,CACR,CAAC;IACJ,CAAC;IACD,MAAM,CAAC,WAAW,EAAE,YAAY,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAE3D,OAAO,CACL,eAAK,KAAK,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,EAAE,aACrD,KAAC,IAAI,IAAC,IAAI,EAAE,KAAK,CAAC,WAAW,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,YACpE,WAAW,GACP,EACP,MAAC,IAAI,IAAC,IAAI,EAAE,KAAK,CAAC,YAAY,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,kBACpE,YAAY,IAAI,IAAI,IACjB,IACH,CACP,CAAC;AACJ,CAAC;AAED,MAAM,gBAAgB,GAAG,IAAI,IAAI,CAAC,YAAY,CAAC,OAAO,EAAE;IACtD,QAAQ,EAAE,SAAS;IACnB,qBAAqB,EAAE,CAAC;CACzB,CAAC,CAAC"}
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import type { Theme } from "../../../../core/design-system/index.js";
|
|
2
|
-
export declare function DecimalRenderer(props: {
|
|
3
|
-
value: string;
|
|
4
|
-
color: keyof Theme["colors"];
|
|
5
|
-
weight: 400 | 500 | 600 | 700;
|
|
6
|
-
integerSize: "md" | "sm";
|
|
7
|
-
fractionSize: "sm" | "xs";
|
|
8
|
-
}): import("react/jsx-runtime").JSX.Element;
|
|
9
|
-
//# sourceMappingURL=common.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"common.d.ts","sourceRoot":"","sources":["../../../../../../../src/react/web/ui/Bridge/swap-widget/common.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,yCAAyC,CAAC;AAGrE,wBAAgB,eAAe,CAAC,KAAK,EAAE;IACrC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,KAAK,CAAC,QAAQ,CAAC,CAAC;IAC7B,MAAM,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC;IAC9B,WAAW,EAAE,IAAI,GAAG,IAAI,CAAC;IACzB,YAAY,EAAE,IAAI,GAAG,IAAI,CAAC;CAC3B,2CAoBA"}
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import type { Theme } from "../../../../core/design-system/index.js";
|
|
2
|
-
import { Text } from "../../components/text.js";
|
|
3
|
-
|
|
4
|
-
export function DecimalRenderer(props: {
|
|
5
|
-
value: string;
|
|
6
|
-
color: keyof Theme["colors"];
|
|
7
|
-
weight: 400 | 500 | 600 | 700;
|
|
8
|
-
integerSize: "md" | "sm";
|
|
9
|
-
fractionSize: "sm" | "xs";
|
|
10
|
-
}) {
|
|
11
|
-
if (Number(props.value) > 1000) {
|
|
12
|
-
return (
|
|
13
|
-
<Text size={props.integerSize} color={props.color} weight={props.weight}>
|
|
14
|
-
{compactFormatter.format(Number(props.value))}
|
|
15
|
-
</Text>
|
|
16
|
-
);
|
|
17
|
-
}
|
|
18
|
-
const [integerPart, fractionPart] = props.value.split(".");
|
|
19
|
-
|
|
20
|
-
return (
|
|
21
|
-
<div style={{ display: "flex", alignItems: "baseline" }}>
|
|
22
|
-
<Text size={props.integerSize} color={props.color} weight={props.weight}>
|
|
23
|
-
{integerPart}
|
|
24
|
-
</Text>
|
|
25
|
-
<Text size={props.fractionSize} color={props.color} weight={props.weight}>
|
|
26
|
-
.{fractionPart || "00"}
|
|
27
|
-
</Text>
|
|
28
|
-
</div>
|
|
29
|
-
);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const compactFormatter = new Intl.NumberFormat("en-US", {
|
|
33
|
-
notation: "compact",
|
|
34
|
-
maximumFractionDigits: 2,
|
|
35
|
-
});
|