thirdweb 5.116.0 → 5.116.2-nightly-151127d66825365cb0ed949ae28b9906ee4dfc8d-20251213000343

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.
Files changed (135) hide show
  1. package/dist/cjs/react/core/hooks/x402/useFetchWithPaymentCore.js +5 -1
  2. package/dist/cjs/react/core/hooks/x402/useFetchWithPaymentCore.js.map +1 -1
  3. package/dist/cjs/react/native/hooks/x402/useFetchWithPayment.js +6 -1
  4. package/dist/cjs/react/native/hooks/x402/useFetchWithPayment.js.map +1 -1
  5. package/dist/cjs/react/web/hooks/x402/useFetchWithPayment.js +7 -1
  6. package/dist/cjs/react/web/hooks/x402/useFetchWithPayment.js.map +1 -1
  7. package/dist/cjs/react/web/ui/Bridge/swap-widget/swap-ui.js +20 -0
  8. package/dist/cjs/react/web/ui/Bridge/swap-widget/swap-ui.js.map +1 -1
  9. package/dist/cjs/react/web/ui/Bridge/swap-widget/use-bridge-chains.js +15 -2
  10. package/dist/cjs/react/web/ui/Bridge/swap-widget/use-bridge-chains.js.map +1 -1
  11. package/dist/cjs/react/web/ui/ConnectWallet/WalletEntryButton.js +16 -2
  12. package/dist/cjs/react/web/ui/ConnectWallet/WalletEntryButton.js.map +1 -1
  13. package/dist/cjs/react/web/ui/ConnectWallet/icons/EmailIcon.js +1 -4
  14. package/dist/cjs/react/web/ui/ConnectWallet/icons/EmailIcon.js.map +1 -1
  15. package/dist/cjs/react/web/ui/ConnectWallet/icons/WalletDotIcon.js +1 -1
  16. package/dist/cjs/react/web/ui/ConnectWallet/icons/WalletDotIcon.js.map +1 -1
  17. package/dist/cjs/react/web/ui/ConnectWallet/in-app-wallet-icon.js +101 -0
  18. package/dist/cjs/react/web/ui/ConnectWallet/in-app-wallet-icon.js.map +1 -0
  19. package/dist/cjs/react/web/ui/components/Spinner.js +7 -7
  20. package/dist/cjs/react/web/ui/components/Spinner.js.map +1 -1
  21. package/dist/cjs/react/web/ui/design-system/elements.js +1 -2
  22. package/dist/cjs/react/web/ui/design-system/elements.js.map +1 -1
  23. package/dist/cjs/react/web/wallets/shared/ConnectWalletSocialOptions.js +4 -4
  24. package/dist/cjs/react/web/wallets/shared/ConnectWalletSocialOptions.js.map +1 -1
  25. package/dist/cjs/react/web/wallets/shared/OTPLoginUI.js +13 -7
  26. package/dist/cjs/react/web/wallets/shared/OTPLoginUI.js.map +1 -1
  27. package/dist/cjs/stories/ConnectEmbed.stories.js +34 -0
  28. package/dist/cjs/stories/ConnectEmbed.stories.js.map +1 -1
  29. package/dist/cjs/stories/in-app-wallet-icon.stories.js +59 -0
  30. package/dist/cjs/stories/in-app-wallet-icon.stories.js.map +1 -0
  31. package/dist/cjs/version.js +1 -1
  32. package/dist/cjs/version.js.map +1 -1
  33. package/dist/cjs/wallets/in-app/web/lib/auth/otp.js +12 -1
  34. package/dist/cjs/wallets/in-app/web/lib/auth/otp.js.map +1 -1
  35. package/dist/cjs/x402/fetchWithPayment.js +13 -1
  36. package/dist/cjs/x402/fetchWithPayment.js.map +1 -1
  37. package/dist/cjs/x402/permitSignatureStorage.js +70 -0
  38. package/dist/cjs/x402/permitSignatureStorage.js.map +1 -0
  39. package/dist/cjs/x402/sign.js +51 -4
  40. package/dist/cjs/x402/sign.js.map +1 -1
  41. package/dist/esm/react/core/hooks/x402/useFetchWithPaymentCore.js +5 -1
  42. package/dist/esm/react/core/hooks/x402/useFetchWithPaymentCore.js.map +1 -1
  43. package/dist/esm/react/native/hooks/x402/useFetchWithPayment.js +6 -1
  44. package/dist/esm/react/native/hooks/x402/useFetchWithPayment.js.map +1 -1
  45. package/dist/esm/react/web/hooks/x402/useFetchWithPayment.js +8 -2
  46. package/dist/esm/react/web/hooks/x402/useFetchWithPayment.js.map +1 -1
  47. package/dist/esm/react/web/ui/Bridge/swap-widget/swap-ui.js +20 -0
  48. package/dist/esm/react/web/ui/Bridge/swap-widget/swap-ui.js.map +1 -1
  49. package/dist/esm/react/web/ui/Bridge/swap-widget/use-bridge-chains.js +15 -2
  50. package/dist/esm/react/web/ui/Bridge/swap-widget/use-bridge-chains.js.map +1 -1
  51. package/dist/esm/react/web/ui/ConnectWallet/WalletEntryButton.js +16 -2
  52. package/dist/esm/react/web/ui/ConnectWallet/WalletEntryButton.js.map +1 -1
  53. package/dist/esm/react/web/ui/ConnectWallet/icons/EmailIcon.js +1 -4
  54. package/dist/esm/react/web/ui/ConnectWallet/icons/EmailIcon.js.map +1 -1
  55. package/dist/esm/react/web/ui/ConnectWallet/icons/WalletDotIcon.js +1 -1
  56. package/dist/esm/react/web/ui/ConnectWallet/icons/WalletDotIcon.js.map +1 -1
  57. package/dist/esm/react/web/ui/ConnectWallet/in-app-wallet-icon.js +98 -0
  58. package/dist/esm/react/web/ui/ConnectWallet/in-app-wallet-icon.js.map +1 -0
  59. package/dist/esm/react/web/ui/components/Spinner.js +9 -9
  60. package/dist/esm/react/web/ui/components/Spinner.js.map +1 -1
  61. package/dist/esm/react/web/ui/design-system/elements.js +0 -1
  62. package/dist/esm/react/web/ui/design-system/elements.js.map +1 -1
  63. package/dist/esm/react/web/wallets/shared/ConnectWalletSocialOptions.js +1 -1
  64. package/dist/esm/react/web/wallets/shared/ConnectWalletSocialOptions.js.map +1 -1
  65. package/dist/esm/react/web/wallets/shared/OTPLoginUI.js +13 -7
  66. package/dist/esm/react/web/wallets/shared/OTPLoginUI.js.map +1 -1
  67. package/dist/esm/stories/ConnectEmbed.stories.js +31 -0
  68. package/dist/esm/stories/ConnectEmbed.stories.js.map +1 -1
  69. package/dist/esm/stories/in-app-wallet-icon.stories.js +55 -0
  70. package/dist/esm/stories/in-app-wallet-icon.stories.js.map +1 -0
  71. package/dist/esm/version.js +1 -1
  72. package/dist/esm/version.js.map +1 -1
  73. package/dist/esm/wallets/in-app/web/lib/auth/otp.js +12 -1
  74. package/dist/esm/wallets/in-app/web/lib/auth/otp.js.map +1 -1
  75. package/dist/esm/x402/fetchWithPayment.js +13 -1
  76. package/dist/esm/x402/fetchWithPayment.js.map +1 -1
  77. package/dist/esm/x402/permitSignatureStorage.js +65 -0
  78. package/dist/esm/x402/permitSignatureStorage.js.map +1 -0
  79. package/dist/esm/x402/sign.js +51 -4
  80. package/dist/esm/x402/sign.js.map +1 -1
  81. package/dist/scripts/bridge-widget.js +113 -112
  82. package/dist/types/react/core/hooks/x402/useFetchWithPaymentCore.d.ts +6 -0
  83. package/dist/types/react/core/hooks/x402/useFetchWithPaymentCore.d.ts.map +1 -1
  84. package/dist/types/react/native/hooks/x402/useFetchWithPayment.d.ts +1 -0
  85. package/dist/types/react/native/hooks/x402/useFetchWithPayment.d.ts.map +1 -1
  86. package/dist/types/react/web/hooks/x402/useFetchWithPayment.d.ts.map +1 -1
  87. package/dist/types/react/web/ui/Bridge/swap-widget/swap-ui.d.ts.map +1 -1
  88. package/dist/types/react/web/ui/Bridge/swap-widget/use-bridge-chains.d.ts +1 -2
  89. package/dist/types/react/web/ui/Bridge/swap-widget/use-bridge-chains.d.ts.map +1 -1
  90. package/dist/types/react/web/ui/ConnectWallet/WalletEntryButton.d.ts.map +1 -1
  91. package/dist/types/react/web/ui/ConnectWallet/icons/EmailIcon.d.ts +0 -3
  92. package/dist/types/react/web/ui/ConnectWallet/icons/EmailIcon.d.ts.map +1 -1
  93. package/dist/types/react/web/ui/ConnectWallet/icons/WalletDotIcon.d.ts.map +1 -1
  94. package/dist/types/react/web/ui/ConnectWallet/in-app-wallet-icon.d.ts +7 -0
  95. package/dist/types/react/web/ui/ConnectWallet/in-app-wallet-icon.d.ts.map +1 -0
  96. package/dist/types/react/web/ui/components/Spinner.d.ts.map +1 -1
  97. package/dist/types/react/web/ui/design-system/elements.d.ts +0 -4
  98. package/dist/types/react/web/ui/design-system/elements.d.ts.map +1 -1
  99. package/dist/types/react/web/wallets/shared/ConnectWalletSocialOptions.d.ts +2 -1
  100. package/dist/types/react/web/wallets/shared/ConnectWalletSocialOptions.d.ts.map +1 -1
  101. package/dist/types/react/web/wallets/shared/OTPLoginUI.d.ts.map +1 -1
  102. package/dist/types/stories/ConnectEmbed.stories.d.ts +3 -0
  103. package/dist/types/stories/ConnectEmbed.stories.d.ts.map +1 -1
  104. package/dist/types/stories/in-app-wallet-icon.stories.d.ts +10 -0
  105. package/dist/types/stories/in-app-wallet-icon.stories.d.ts.map +1 -0
  106. package/dist/types/version.d.ts +1 -1
  107. package/dist/types/version.d.ts.map +1 -1
  108. package/dist/types/wallets/in-app/web/lib/auth/otp.d.ts.map +1 -1
  109. package/dist/types/x402/fetchWithPayment.d.ts +6 -0
  110. package/dist/types/x402/fetchWithPayment.d.ts.map +1 -1
  111. package/dist/types/x402/permitSignatureStorage.d.ts +43 -0
  112. package/dist/types/x402/permitSignatureStorage.d.ts.map +1 -0
  113. package/dist/types/x402/sign.d.ts +3 -1
  114. package/dist/types/x402/sign.d.ts.map +1 -1
  115. package/package.json +1 -1
  116. package/src/react/core/hooks/x402/useFetchWithPaymentCore.ts +11 -1
  117. package/src/react/native/hooks/x402/useFetchWithPayment.ts +6 -1
  118. package/src/react/web/hooks/x402/useFetchWithPayment.tsx +12 -2
  119. package/src/react/web/ui/Bridge/swap-widget/swap-ui.tsx +26 -0
  120. package/src/react/web/ui/Bridge/swap-widget/use-bridge-chains.ts +19 -2
  121. package/src/react/web/ui/ConnectWallet/WalletEntryButton.tsx +23 -2
  122. package/src/react/web/ui/ConnectWallet/icons/EmailIcon.tsx +12 -19
  123. package/src/react/web/ui/ConnectWallet/icons/WalletDotIcon.tsx +1 -0
  124. package/src/react/web/ui/ConnectWallet/in-app-wallet-icon.tsx +195 -0
  125. package/src/react/web/ui/components/Spinner.tsx +9 -9
  126. package/src/react/web/ui/design-system/elements.ts +0 -1
  127. package/src/react/web/wallets/shared/ConnectWalletSocialOptions.tsx +1 -1
  128. package/src/react/web/wallets/shared/OTPLoginUI.tsx +24 -11
  129. package/src/stories/ConnectEmbed.stories.tsx +55 -0
  130. package/src/stories/in-app-wallet-icon.stories.tsx +163 -0
  131. package/src/version.ts +1 -1
  132. package/src/wallets/in-app/web/lib/auth/otp.ts +11 -1
  133. package/src/x402/fetchWithPayment.ts +21 -0
  134. package/src/x402/permitSignatureStorage.ts +99 -0
  135. package/src/x402/sign.ts +76 -1
@@ -0,0 +1,195 @@
1
+ "use client";
2
+ import type { ThirdwebClient } from "../../../../client/client.js";
3
+ import type { Wallet } from "../../../../wallets/interfaces/wallet.js";
4
+ import type { AuthOption } from "../../../../wallets/types.js";
5
+ import { useCustomTheme } from "../../../core/design-system/CustomThemeProvider.js";
6
+ import {
7
+ iconSize,
8
+ radius,
9
+ spacing,
10
+ } from "../../../core/design-system/index.js";
11
+ import { socialIcons } from "../../../core/utils/walletIcon.js";
12
+ import { defaultAuthOptions } from "../../wallets/shared/ConnectWalletSocialOptions.js";
13
+ import { Img } from "../components/Img.js";
14
+ import { EmailIcon } from "./icons/EmailIcon.js";
15
+ import { FingerPrintIcon } from "./icons/FingerPrintIcon.js";
16
+ import { GuestIcon } from "./icons/GuestIcon.js";
17
+ import { PhoneIcon } from "./icons/PhoneIcon.js";
18
+
19
+ export function InAppWalletIcon(props: {
20
+ client: ThirdwebClient;
21
+ wallet: Wallet<"inApp">;
22
+ }) {
23
+ const enabledAuthMethods = (
24
+ props.wallet.getConfig()?.auth?.options || defaultAuthOptions
25
+ )
26
+ .slice() // clone
27
+ .sort((a, b) => {
28
+ if (a in socialIcons && !(b in socialIcons)) {
29
+ return -1;
30
+ }
31
+ if (!(a in socialIcons) && b in socialIcons) {
32
+ return 1;
33
+ }
34
+ return 0;
35
+ });
36
+
37
+ const theme = useCustomTheme();
38
+
39
+ const firstMethod = enabledAuthMethods[0];
40
+ const secondMethod = enabledAuthMethods[1];
41
+ const thirdMethod = enabledAuthMethods[2];
42
+ const fourthMethod = enabledAuthMethods[3];
43
+
44
+ const offset = "4px";
45
+ const offset2 = "6px";
46
+ const smallIconSize = "20";
47
+ const extraIconSize = "12";
48
+
49
+ if (firstMethod && secondMethod) {
50
+ return (
51
+ <div
52
+ style={{
53
+ width: `${iconSize.xl}px`,
54
+ height: `${iconSize.xl}px`,
55
+ position: "relative",
56
+ gap: spacing["3xs"],
57
+ border: `1px solid ${theme.colors.borderColor}`,
58
+ borderRadius: radius.md,
59
+ backgroundColor: theme.colors.tertiaryBg,
60
+ }}
61
+ >
62
+ <div
63
+ style={{
64
+ position: "absolute",
65
+ top: offset,
66
+ left: offset,
67
+ display: "flex",
68
+ }}
69
+ >
70
+ <AuthOptionIcon
71
+ authOption={firstMethod}
72
+ client={props.client}
73
+ size={smallIconSize}
74
+ />
75
+ </div>
76
+
77
+ <div
78
+ style={{
79
+ position: "absolute",
80
+ bottom: offset,
81
+ right: offset,
82
+ display: "flex",
83
+ }}
84
+ >
85
+ <AuthOptionIcon
86
+ authOption={secondMethod}
87
+ client={props.client}
88
+ size={smallIconSize}
89
+ />
90
+ </div>
91
+
92
+ <div>
93
+ {thirdMethod && (
94
+ <div
95
+ style={{
96
+ position: "absolute",
97
+ top: offset2,
98
+ right: offset2,
99
+ display: "flex",
100
+ }}
101
+ >
102
+ <AuthOptionIcon
103
+ authOption={thirdMethod}
104
+ client={props.client}
105
+ size={extraIconSize}
106
+ />
107
+ </div>
108
+ )}
109
+
110
+ {fourthMethod && (
111
+ <div
112
+ style={{
113
+ position: "absolute",
114
+ bottom: offset2,
115
+ left: offset2,
116
+ display: "flex",
117
+ }}
118
+ >
119
+ <AuthOptionIcon
120
+ authOption={fourthMethod}
121
+ client={props.client}
122
+ size={extraIconSize}
123
+ />
124
+ </div>
125
+ )}
126
+ </div>
127
+ </div>
128
+ );
129
+ }
130
+
131
+ if (firstMethod) {
132
+ return (
133
+ <div
134
+ style={{
135
+ width: `${iconSize.xl}px`,
136
+ height: `${iconSize.xl}px`,
137
+ display: "flex",
138
+ justifyContent: "center",
139
+ alignItems: "center",
140
+ border: `1px solid ${theme.colors.borderColor}`,
141
+ borderRadius: radius.md,
142
+ backgroundColor: theme.colors.tertiaryBg,
143
+ }}
144
+ >
145
+ <AuthOptionIcon
146
+ authOption={firstMethod}
147
+ client={props.client}
148
+ key={firstMethod}
149
+ size={iconSize.lg}
150
+ />
151
+ </div>
152
+ );
153
+ }
154
+
155
+ return null;
156
+ }
157
+
158
+ function AuthOptionIcon(props: {
159
+ authOption: AuthOption;
160
+ client: ThirdwebClient;
161
+ size: string;
162
+ }) {
163
+ const theme = useCustomTheme();
164
+ if (props.authOption in socialIcons) {
165
+ const icon = socialIcons[props.authOption as keyof typeof socialIcons];
166
+ return (
167
+ <Img
168
+ src={icon}
169
+ width={props.size}
170
+ height={props.size}
171
+ client={props.client}
172
+ />
173
+ );
174
+ }
175
+
176
+ if (props.authOption === "phone") {
177
+ return <PhoneIcon size={props.size} color={theme.colors.secondaryText} />;
178
+ }
179
+
180
+ if (props.authOption === "email") {
181
+ return <EmailIcon size={props.size} color={theme.colors.secondaryText} />;
182
+ }
183
+
184
+ if (props.authOption === "passkey") {
185
+ return (
186
+ <FingerPrintIcon size={props.size} color={theme.colors.secondaryText} />
187
+ );
188
+ }
189
+
190
+ if (props.authOption === "guest") {
191
+ return <GuestIcon size={props.size} color={theme.colors.secondaryText} />;
192
+ }
193
+
194
+ return null;
195
+ }
@@ -3,7 +3,7 @@ import { keyframes } from "@emotion/react";
3
3
  import { useCustomTheme } from "../../../core/design-system/CustomThemeProvider.js";
4
4
  import type { Theme } from "../../../core/design-system/index.js";
5
5
  import { iconSize } from "../../../core/design-system/index.js";
6
- import { StyledCircle, StyledSvg } from "../design-system/elements.js";
6
+ import { StyledSvg } from "../design-system/elements.js";
7
7
 
8
8
  /**
9
9
  * @internal
@@ -24,20 +24,25 @@ export const Spinner: React.FC<{
24
24
  viewBox="0 0 50 50"
25
25
  className="tw-spinner"
26
26
  >
27
- <Circle
27
+ <circle
28
28
  cx="25"
29
29
  cy="25"
30
30
  fill="none"
31
31
  r="20"
32
+ style={{
33
+ strokeLinecap: "round",
34
+ animation: `tw-spinner-circle-dash 1.5s ease-in-out infinite`,
35
+ }}
32
36
  stroke={props.color ? theme.colors[props.color] : "currentColor"}
33
37
  strokeWidth={Number(iconSize[props.size]) > 64 ? "2" : "4"}
34
38
  />
39
+ <style>{dashAnimation}</style>
35
40
  </Svg>
36
41
  );
37
42
  };
38
-
39
43
  // animations
40
- const dashAnimation = keyframes`
44
+ const dashAnimation = `
45
+ @keyframes tw-spinner-circle-dash {
41
46
  0% {
42
47
  stroke-dasharray: 1, 150;
43
48
  stroke-dashoffset: 0;
@@ -63,8 +68,3 @@ const Svg = /* @__PURE__ */ StyledSvg({
63
68
  height: "1em",
64
69
  width: "1em",
65
70
  });
66
-
67
- const Circle = /* @__PURE__ */ StyledCircle({
68
- animation: `${dashAnimation} 1.5s ease-in-out infinite`,
69
- strokeLinecap: "round",
70
- });
@@ -2,7 +2,6 @@ import styled from "@emotion/styled";
2
2
 
3
3
  export const StyledDiv = /* @__PURE__ */ styled.div;
4
4
  export const StyledSvg = /* @__PURE__ */ styled.svg;
5
- export const StyledCircle = /* @__PURE__ */ styled.circle;
6
5
  export const StyledSpan = /* @__PURE__ */ styled.span;
7
6
  export const StyledAnchor = /* @__PURE__ */ styled.a;
8
7
  export const StyledButton = /* @__PURE__ */ styled.button;
@@ -61,7 +61,7 @@ export type ConnectWalletSelectUIState =
61
61
  };
62
62
  };
63
63
 
64
- const defaultAuthOptions: AuthOption[] = [
64
+ export const defaultAuthOptions: AuthOption[] = [
65
65
  "email",
66
66
  "phone",
67
67
  "google",
@@ -29,7 +29,10 @@ type VerificationStatus =
29
29
  | "valid"
30
30
  | "idle"
31
31
  | "payment_required";
32
- type AccountStatus = "sending" | "sent" | "error";
32
+ type AccountStatus =
33
+ | { type: "sending" }
34
+ | { type: "sent" }
35
+ | { type: "error"; message: string | undefined };
33
36
  type ScreenToShow = "base" | "enter-password-or-recovery-code";
34
37
 
35
38
  /**
@@ -52,7 +55,9 @@ export function OTPLoginUI(props: {
52
55
  const [otpInput, setOtpInput] = useState("");
53
56
  const [verifyStatus, setVerifyStatus] = useState<VerificationStatus>("idle");
54
57
  const [error, setError] = useState<string | undefined>();
55
- const [accountStatus, setAccountStatus] = useState<AccountStatus>("sending");
58
+ const [accountStatus, setAccountStatus] = useState<AccountStatus>({
59
+ type: "sending",
60
+ });
56
61
  const [countdown, setCountdown] = useState(0);
57
62
  const ecosystem = isEcosystemWallet(wallet)
58
63
  ? {
@@ -66,7 +71,7 @@ export function OTPLoginUI(props: {
66
71
  const sendEmailOrSms = useCallback(async () => {
67
72
  setOtpInput("");
68
73
  setVerifyStatus("idle");
69
- setAccountStatus("sending");
74
+ setAccountStatus({ type: "sending" });
70
75
 
71
76
  try {
72
77
  if ("email" in userInfo) {
@@ -76,7 +81,7 @@ export function OTPLoginUI(props: {
76
81
  email: userInfo.email,
77
82
  strategy: "email",
78
83
  });
79
- setAccountStatus("sent");
84
+ setAccountStatus({ type: "sent" });
80
85
  setCountdown(60); // Start 60-second countdown
81
86
  } else if ("phone" in userInfo) {
82
87
  await preAuthenticate({
@@ -85,7 +90,7 @@ export function OTPLoginUI(props: {
85
90
  phoneNumber: userInfo.phone,
86
91
  strategy: "phone",
87
92
  });
88
- setAccountStatus("sent");
93
+ setAccountStatus({ type: "sent" });
89
94
  setCountdown(60); // Start 60-second countdown
90
95
  } else {
91
96
  throw new Error("Invalid userInfo");
@@ -93,7 +98,10 @@ export function OTPLoginUI(props: {
93
98
  } catch (e) {
94
99
  console.error(e);
95
100
  setVerifyStatus("idle");
96
- setAccountStatus("error");
101
+ setAccountStatus({
102
+ type: "error",
103
+ message: e instanceof Error ? e.message : undefined,
104
+ });
97
105
  }
98
106
  }, [props.client, userInfo, ecosystem]);
99
107
 
@@ -317,19 +325,24 @@ export function OTPLoginUI(props: {
317
325
 
318
326
  {!isWideModal && <Line />}
319
327
 
320
- <Container gap="xs" p={isWideModal ? undefined : "lg"}>
321
- {accountStatus === "error" && (
328
+ <Container
329
+ gap="sm"
330
+ p={isWideModal ? undefined : "lg"}
331
+ flex="column"
332
+ >
333
+ {accountStatus.type === "error" && (
322
334
  <Text
323
335
  center
324
336
  color="danger"
325
337
  size="sm"
326
338
  className="tw-screen-error"
327
339
  >
328
- {locale.emailLoginScreen.failedToSendCode}
340
+ {accountStatus.message ||
341
+ locale.emailLoginScreen.failedToSendCode}
329
342
  </Text>
330
343
  )}
331
344
 
332
- {accountStatus === "sending" && (
345
+ {accountStatus.type === "sending" && (
333
346
  <Container
334
347
  center="both"
335
348
  flex="row"
@@ -343,7 +356,7 @@ export function OTPLoginUI(props: {
343
356
  </Container>
344
357
  )}
345
358
 
346
- {accountStatus !== "sending" && (
359
+ {accountStatus.type !== "sending" && (
347
360
  <LinkButton
348
361
  onClick={countdown === 0 ? sendEmailOrSms : undefined}
349
362
  style={{
@@ -1,6 +1,7 @@
1
1
  import type { Meta } from "@storybook/react";
2
2
  import { ConnectButton } from "../react/web/ui/ConnectWallet/ConnectButton.js";
3
3
  import { ConnectEmbed } from "../react/web/ui/ConnectWallet/Modal/ConnectEmbed.js";
4
+ import { createWallet } from "../wallets/create-wallet.js";
4
5
  import { ecosystemWallet } from "../wallets/in-app/web/ecosystem.js";
5
6
  import { inAppWallet } from "../wallets/in-app/web/in-app.js";
6
7
  import { storyClient } from "./utils.js";
@@ -110,6 +111,60 @@ export function AllInAppWalletAuthMethods() {
110
111
  );
111
112
  }
112
113
 
114
+ export function ConfiguredInAppWalletWideModal() {
115
+ return (
116
+ <ConnectEmbed
117
+ client={storyClient}
118
+ className="foo-bar"
119
+ modalSize="wide"
120
+ wallets={[
121
+ createWallet("io.metamask"),
122
+ inAppWallet({
123
+ auth: {
124
+ options: ["google", "github", "email"],
125
+ },
126
+ }),
127
+ ]}
128
+ />
129
+ );
130
+ }
131
+
132
+ export function GoogleLoginWideModal() {
133
+ return (
134
+ <ConnectEmbed
135
+ client={storyClient}
136
+ className="foo-bar"
137
+ modalSize="wide"
138
+ wallets={[
139
+ createWallet("io.metamask"),
140
+ inAppWallet({
141
+ auth: {
142
+ options: ["google"],
143
+ },
144
+ }),
145
+ ]}
146
+ />
147
+ );
148
+ }
149
+
150
+ export function GithubLoginWideModal() {
151
+ return (
152
+ <ConnectEmbed
153
+ client={storyClient}
154
+ className="foo-bar"
155
+ modalSize="wide"
156
+ wallets={[
157
+ createWallet("io.metamask"),
158
+ inAppWallet({
159
+ auth: {
160
+ options: ["github"],
161
+ },
162
+ }),
163
+ ]}
164
+ />
165
+ );
166
+ }
167
+
113
168
  export function EcosystemWallet() {
114
169
  return (
115
170
  <ConnectEmbed
@@ -0,0 +1,163 @@
1
+ import type { Meta } from "@storybook/react";
2
+ import {
3
+ CustomThemeProvider,
4
+ useCustomTheme,
5
+ } from "../react/core/design-system/CustomThemeProvider.js";
6
+ import { InAppWalletIcon } from "../react/web/ui/ConnectWallet/in-app-wallet-icon.js";
7
+ import { defaultAuthOptions } from "../react/web/wallets/shared/ConnectWalletSocialOptions.js";
8
+ import { inAppWallet } from "../wallets/in-app/web/in-app.js";
9
+ import type { AuthOption } from "../wallets/types.js";
10
+ import { storyClient } from "./utils.js";
11
+
12
+ const meta: Meta<typeof Variant> = {
13
+ title: "Components/in-app-wallet-icon",
14
+ decorators: [
15
+ (Story) => {
16
+ return (
17
+ <CustomThemeProvider theme="dark">
18
+ <Story />
19
+ </CustomThemeProvider>
20
+ );
21
+ },
22
+ ],
23
+ };
24
+ export default meta;
25
+
26
+ function Variants() {
27
+ const theme = useCustomTheme();
28
+ return (
29
+ <div
30
+ style={{
31
+ backgroundColor: theme.colors.modalBg,
32
+ padding: "14px",
33
+ borderRadius: "10px",
34
+ display: "flex",
35
+ flexDirection: "column",
36
+ gap: "12px",
37
+ }}
38
+ >
39
+ <div>
40
+ <SectionTitle title="Default" />
41
+ <Variant authOptions={defaultAuthOptions} />
42
+ </div>
43
+
44
+ <div>
45
+ <SectionTitle title="Single method enabled" />
46
+ <div style={{ display: "flex", gap: "10px", flexWrap: "wrap" }}>
47
+ <Variant authOptions={["email"]} />
48
+ <Variant authOptions={["phone"]} />
49
+ <Variant authOptions={["passkey"]} />
50
+ <Variant authOptions={["guest"]} />
51
+ <Variant authOptions={["google"]} />
52
+ <Variant authOptions={["apple"]} />
53
+ <Variant authOptions={["facebook"]} />
54
+ <Variant authOptions={["discord"]} />
55
+ <Variant authOptions={["github"]} />
56
+ <Variant authOptions={["twitch"]} />
57
+ <Variant authOptions={["x"]} />
58
+ <Variant authOptions={["telegram"]} />
59
+ <Variant authOptions={["line"]} />
60
+ <Variant authOptions={["coinbase"]} />
61
+ <Variant authOptions={["epic"]} />
62
+ <Variant authOptions={["farcaster"]} />
63
+ <Variant authOptions={["tiktok"]} />
64
+ <Variant authOptions={["steam"]} />
65
+ </div>
66
+ </div>
67
+
68
+ <div>
69
+ <SectionTitle title="Two methods enabled" />
70
+ <div style={{ display: "flex", gap: "10px", flexWrap: "wrap" }}>
71
+ <Variant authOptions={["email", "phone"]} />
72
+ <Variant authOptions={["email", "passkey"]} />
73
+ <Variant authOptions={["email", "guest"]} />
74
+ <Variant authOptions={["email", "google"]} />
75
+ <Variant authOptions={["email", "apple"]} />
76
+ <Variant authOptions={["email", "facebook"]} />
77
+ <Variant authOptions={["google", "discord"]} />
78
+ <Variant authOptions={["google", "github"]} />
79
+ <Variant authOptions={["google", "twitch"]} />
80
+ <Variant authOptions={["google", "x"]} />
81
+ <Variant authOptions={["google", "telegram"]} />
82
+ <Variant authOptions={["google", "line"]} />
83
+ <Variant authOptions={["google", "coinbase"]} />
84
+ <Variant authOptions={["google", "epic"]} />
85
+ </div>
86
+ </div>
87
+
88
+ <div>
89
+ <SectionTitle title="Three methods enabled" />
90
+ <div style={{ display: "flex", gap: "10px", flexWrap: "wrap" }}>
91
+ <Variant authOptions={["google", "apple", "github"]} />
92
+ <Variant authOptions={["email", "phone", "guest"]} />
93
+ <Variant authOptions={["email", "phone", "google"]} />
94
+ <Variant authOptions={["email", "phone", "apple"]} />
95
+ <Variant authOptions={["email", "phone", "facebook"]} />
96
+ </div>
97
+ </div>
98
+
99
+ <div>
100
+ <SectionTitle title="Four or more methods enabled" />
101
+ <div style={{ display: "flex", gap: "10px", flexWrap: "wrap" }}>
102
+ <Variant
103
+ authOptions={["email", "phone", "google", "apple", "facebook"]}
104
+ />
105
+ <Variant authOptions={["epic", "tiktok", "github", "email"]} />
106
+ <Variant
107
+ authOptions={["twitch", "tiktok", "epic", "email", "phone"]}
108
+ />
109
+ <Variant authOptions={["email", "phone", "passkey", "guest"]} />
110
+ <Variant
111
+ authOptions={[
112
+ "google",
113
+ "apple",
114
+ "facebook",
115
+ "github",
116
+ "discord",
117
+ "twitch",
118
+ "x",
119
+ ]}
120
+ />
121
+ </div>
122
+ </div>
123
+ </div>
124
+ );
125
+ }
126
+
127
+ export function LightTheme() {
128
+ return <ThemeSetup theme="light" />;
129
+ }
130
+
131
+ export function DarkTheme() {
132
+ return <ThemeSetup theme="dark" />;
133
+ }
134
+
135
+ function ThemeSetup(props: { theme: "light" | "dark" }) {
136
+ return (
137
+ <CustomThemeProvider theme={props.theme}>
138
+ <Variants />
139
+ </CustomThemeProvider>
140
+ );
141
+ }
142
+
143
+ function Variant(props: { authOptions: AuthOption[] }) {
144
+ return (
145
+ <InAppWalletIcon
146
+ client={storyClient}
147
+ wallet={inAppWallet({
148
+ auth: {
149
+ options: props.authOptions,
150
+ },
151
+ })}
152
+ />
153
+ );
154
+ }
155
+
156
+ function SectionTitle(props: { title: string }) {
157
+ const theme = useCustomTheme();
158
+ return (
159
+ <p style={{ color: theme.colors.secondaryText, fontSize: "14px" }}>
160
+ {props.title}
161
+ </p>
162
+ );
163
+ }
package/src/version.ts CHANGED
@@ -1 +1 @@
1
- export const version = "5.116.0";
1
+ export const version = "5.116.2-nightly-151127d66825365cb0ed949ae28b9906ee4dfc8d-20251213000343";
@@ -51,7 +51,17 @@ export const sendOtp = async (args: PreAuthArgsType): Promise<void> => {
51
51
  });
52
52
 
53
53
  if (!response.ok) {
54
- throw new Error("Failed to send verification code");
54
+ const raw = await response.text();
55
+ let message: string | undefined;
56
+ try {
57
+ const parsed = JSON.parse(raw);
58
+ if (parsed && typeof parsed.message === "string") {
59
+ message = parsed.message;
60
+ }
61
+ } catch {
62
+ // ignore parse errors
63
+ }
64
+ throw new Error(message || "Failed to send verification code");
55
65
  }
56
66
 
57
67
  return await response.json();
@@ -1,6 +1,10 @@
1
1
  import { getCachedChain } from "../chains/utils.js";
2
2
  import type { ThirdwebClient } from "../client/client.js";
3
+ import { getAddress } from "../utils/address.js";
4
+ import type { AsyncStorage } from "../utils/storage/AsyncStorage.js";
5
+ import { webLocalStorage } from "../utils/storage/webStorage.js";
3
6
  import type { Wallet } from "../wallets/interfaces/wallet.js";
7
+ import { clearPermitSignatureFromCache } from "./permitSignatureStorage.js";
4
8
  import {
5
9
  extractEvmChainId,
6
10
  networkToCaip2ChainId,
@@ -57,6 +61,11 @@ export function wrapFetchWithPayment(
57
61
  paymentRequirementsSelector?: (
58
62
  paymentRequirements: RequestedPaymentRequirements[],
59
63
  ) => RequestedPaymentRequirements | undefined;
64
+ /**
65
+ * Storage for caching permit signatures (for "upto" scheme).
66
+ * When provided, permit signatures will be cached and reused if the on-chain allowance is sufficient.
67
+ */
68
+ storage?: AsyncStorage;
60
69
  },
61
70
  ) {
62
71
  return async (input: RequestInfo, init?: RequestInit) => {
@@ -131,6 +140,7 @@ export function wrapFetchWithPayment(
131
140
  account,
132
141
  selectedPaymentRequirements,
133
142
  x402Version,
143
+ options?.storage ?? webLocalStorage,
134
144
  );
135
145
 
136
146
  const initParams = init || {};
@@ -150,6 +160,17 @@ export function wrapFetchWithPayment(
150
160
  };
151
161
 
152
162
  const secondResponse = await fetch(input, newInit);
163
+
164
+ // If payment was rejected (still 402), clear cached signature
165
+ if (secondResponse.status === 402 && options?.storage) {
166
+ await clearPermitSignatureFromCache(options.storage, {
167
+ chainId: paymentChainId,
168
+ asset: selectedPaymentRequirements.asset,
169
+ owner: getAddress(account.address),
170
+ spender: getAddress(selectedPaymentRequirements.payTo),
171
+ });
172
+ }
173
+
153
174
  return secondResponse;
154
175
  };
155
176
  }