Skip to content

Commit f6a30c0

Browse files
ElasticBottlejnsdlsMananTank
authored
feat: add phone login for embedded wallet (#2366)
Co-authored-by: Jonas Daniels <jonas.daniels@outlook.com> Co-authored-by: Manan Tank <manantankm@gmail.com>
1 parent b1013fc commit f6a30c0

File tree

29 files changed

+1979
-217
lines changed

29 files changed

+1979
-217
lines changed

.changeset/serious-crews-shake.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
---
2+
"@thirdweb-dev/wallets": minor
3+
---
4+
5+
Adds Login with SMS in EmbeddedWallet.
6+
7+
Note that the `phoneNumber` requires the `+` symbol along with the IsoCountry code before the actual phone number.
8+
9+
For a list of supported country code, you can use the `supportedSmsCountries` object which contains a list of all support country information.
10+
11+
Usage:
12+
13+
```typescript
14+
const embeddedWalletSdk = new EmbeddedWalletConnector({
15+
clientId: "YOUR_THIRDWEB_CLIENT_ID",
16+
// ...
17+
});
18+
19+
// phone number needs to have the country code as prefix
20+
await embeddedWalletSdk.sendVerificationSms({ phoneNumber: "+11234567890" });
21+
22+
const authResult = await embeddedWalletSdk.authenticate({
23+
strategy: "phone_number_verification",
24+
verificationCode: "123456",
25+
});
26+
27+
const walletAddress = await embeddedWalletSdk.connect({
28+
authResult,
29+
});
30+
const signer = await embeddedWalletSdk.getSigner();
31+
```

.changeset/tidy-pumpkins-agree.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
---
2+
"@thirdweb-dev/react": minor
3+
---
4+
5+
Add Login with SMS in EmbeddedWallet
6+
7+
Note that by having `phone` before `email` in the options array, the phone login option will be presented first.
8+
9+
Use `['email', 'phone']` to have the email option presented first.
10+
11+
```ts
12+
<ThirdwebProvider
13+
clientId={import.meta.env.VITE_TEMPLATE_CLIENT_ID}
14+
activeChain={activeChain}
15+
supportedWallets={[
16+
embeddedWallet({
17+
auth: {
18+
options: ["phone", "email", "apple", "google"],
19+
},
20+
}),
21+
...defaultWallets,
22+
]}
23+
>
24+
<App />
25+
</ThirdwebProvider>
26+
```

legacy_packages/react/src/evm/hooks/wallets/useEmbeddedWallet.ts

Lines changed: 51 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
import { UseQueryResult, useQuery } from "@tanstack/react-query";
12
import {
3+
useAddress,
24
useCreateWalletInstance,
35
useSetConnectedWallet,
46
useSetConnectionStatus,
@@ -7,17 +9,12 @@ import {
79
useWallets,
810
} from "@thirdweb-dev/react-core";
911
import {
12+
EmbeddedWallet,
1013
walletIds,
1114
type AuthParams,
12-
EmbeddedWallet,
1315
} from "@thirdweb-dev/wallets";
14-
import { useCallback, useEffect } from "react";
16+
import { useCallback } from "react";
1517
import { embeddedWallet } from "../../../wallet/wallets/embeddedWallet/embeddedWallet";
16-
import {
17-
UseQueryResult,
18-
useQuery,
19-
useQueryClient,
20-
} from "@tanstack/react-query";
2118

2219
/**
2320
* Hook to connect `EmbeddedWallet` which allows users to login via Email or social logins
@@ -194,25 +191,65 @@ export function useEmbeddedWalletUserEmail(): UseQueryResult<
194191
string | undefined
195192
> {
196193
const wallet = useWallet();
197-
const queryClient = useQueryClient();
194+
const address = useAddress();
198195

199196
const emailQuery = useQuery<string | undefined, string>(
200-
[wallet?.walletId, "embeddedWallet-email"],
197+
[wallet?.walletId, address, "embeddedWallet-email"],
201198
() => {
202199
if (wallet && wallet.walletId === walletIds.embeddedWallet) {
203-
return (wallet as EmbeddedWallet).getEmail();
200+
return (wallet as EmbeddedWallet).getEmail() ?? "";
204201
}
202+
return "";
205203
},
206204
{
207205
retry: false,
208206
enabled: wallet?.walletId === walletIds.embeddedWallet,
209207
},
210208
);
211209

212-
// Invalidate the query when the wallet changes
213-
useEffect(() => {
214-
queryClient.invalidateQueries([wallet?.walletId, "embeddedWallet-email"]);
215-
}, [wallet, queryClient]);
210+
return emailQuery;
211+
}
212+
213+
/**
214+
* Hook to get the user's phone number from connected `EmbeddedWallet`
215+
*
216+
* @example
217+
* ```ts
218+
* const phoneNumberQuery = useEmbeddedWalletUserPhoneNumber();
219+
*
220+
* if (phoneNumberQuery.isFetching) {
221+
* return <div> Loading... </div>;
222+
* }
223+
*
224+
* if (phoneNumberQuery.data) {
225+
* return <div> Connected with {phoneNumberQuery.data} </div>;
226+
* }
227+
*
228+
* return <div> Not connected </div>;
229+
* ```
230+
*
231+
* @walletConnection
232+
* @returns Hook's `data` property contains the `string` email if `EmbeddedWallet` is connected, otherwise `undefined`
233+
*/
234+
export function useEmbeddedWalletUserPhoneNumber(): UseQueryResult<
235+
string | undefined
236+
> {
237+
const wallet = useWallet();
238+
const address = useAddress();
239+
240+
const emailQuery = useQuery<string | undefined, string>(
241+
[wallet?.walletId, address, "embeddedWallet-phone-number"],
242+
() => {
243+
if (wallet && wallet.walletId === walletIds.embeddedWallet) {
244+
return (wallet as EmbeddedWallet).getPhoneNumber() ?? "";
245+
}
246+
return "";
247+
},
248+
{
249+
retry: false,
250+
enabled: wallet?.walletId === walletIds.embeddedWallet,
251+
},
252+
);
216253

217254
return emailQuery;
218255
}

legacy_packages/react/src/evm/locales/en.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { ThirdwebLocale } from "./types";
21
import { DeepPartial, immutableOverride } from "../utils/applyOverrides";
2+
import { ThirdwebLocale } from "./types";
33

44
// wallets that connect via extension and QR scan
55
function extensionAndQRScanScreens(walletName: string) {
@@ -210,20 +210,27 @@ export function enDefault(): ThirdwebLocale {
210210
signInWithGoogle: "Sign in with Google",
211211
signInWithFacebook: "Sign in with Facebook",
212212
signInWithApple: "Sign in with Apple",
213+
signInWithEmail: "Sign in with Email",
213214
emailPlaceholder: "Enter your email address",
214215
submitEmail: "Continue",
215216
signIn: "Sign in",
216217
emailRequired: "Email address is required",
217218
invalidEmail: "Invalid email address",
218219
maxAccountsExceeded:
219220
"Maximum number of accounts exceeded. Please notify the app developer.",
221+
invalidPhone: "Invalid phone number",
222+
invalidEmailOrPhone: "Invalid email address or phone number",
223+
countryCodeMissing: "Phone number must start with a country code",
224+
phonePlaceholder: "Enter phone number",
225+
signInWithPhone: "Sign in with phone number",
226+
phoneRequired: "phone number is required",
220227
socialLoginScreen: {
221228
title: "Sign in",
222229
instruction: "Sign into your account in the pop-up",
223230
failed: "Failed to sign in",
224231
retry: "Retry",
225232
},
226-
emailLoginScreen: {
233+
otpLoginScreen: {
227234
title: "Sign in",
228235
enterCodeSendTo: "Enter the verification code sent to",
229236
newDeviceDetected: "New device detected",

legacy_packages/react/src/evm/locales/es.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,19 +213,27 @@ export function esDefault(): ThirdwebLocale {
213213
signInWithGoogle: "Iniciar sesión con Google",
214214
signInWithFacebook: "Iniciar sesión con Facebook",
215215
signInWithApple: "Iniciar sesión con Apple",
216+
signInWithEmail: "Iniciar sesión con correo electrónico",
216217
emailPlaceholder: "Ingresa tu dirección de correo electrónico",
217218
submitEmail: "Continuar",
218219
signIn: "Iniciar sesión",
219220
emailRequired: "Se requiere dirección de correo electrónico",
220221
invalidEmail: "Dirección de correo electrónico inválida",
221222
maxAccountsExceeded: "Número máximo de cuentas alcanzado",
223+
invalidPhone: "Número de teléfono inválido",
224+
invalidEmailOrPhone:
225+
"Dirección de correo electrónico o número de teléfono inválido",
226+
countryCodeMissing: "Phone number must start with a country code",
227+
phonePlaceholder: "Ingresa tu número de teléfono",
228+
signInWithPhone: "Iniciar sesión con número de teléfono",
229+
phoneRequired: "Se requiere número de teléfono",
222230
socialLoginScreen: {
223231
title: "Iniciar sesión",
224232
instruction: "Inicie sesión en su cuenta en la ventana abierta",
225233
failed: "Error al iniciar sesión",
226234
retry: "Reintentar",
227235
},
228-
emailLoginScreen: {
236+
otpLoginScreen: {
229237
title: "Iniciar sesión",
230238
enterCodeSendTo: "Ingresa el código de verificación enviado a",
231239
newDeviceDetected: "Nuevo dispositivo detectado",

legacy_packages/react/src/evm/locales/ja.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -212,19 +212,26 @@ export function jaDefault(): ThirdwebLocale {
212212
signInWithGoogle: "Googleでサインイン",
213213
signInWithFacebook: "Facebookでサインイン",
214214
signInWithApple: "Appleでサインイン",
215+
signInWithEmail: "電子メールでサインインする",
215216
emailPlaceholder: "メールアドレスを入力してください",
216217
submitEmail: "続ける",
217218
emailRequired: "メールアドレスが必要です",
218219
invalidEmail: "無効なメールアドレス",
219220
signIn: "サインイン",
220221
maxAccountsExceeded: "アカウントの最大数を超えました",
222+
invalidPhone: "無効な電話番号",
223+
invalidEmailOrPhone: "無効なメールアドレスまたは電話番号",
224+
countryCodeMissing: "Phone number must start with a country code",
225+
phonePlaceholder: "電話番号を入力してください",
226+
signInWithPhone: "電話番号でログイン",
227+
phoneRequired: "電話番号が必要です",
221228
socialLoginScreen: {
222229
title: "サインイン",
223230
instruction: "ポップアップウィンドウでアカウントにサインインします", // TODO: check if this is correct
224231
failed: "サインインに失敗しました",
225232
retry: "再試行",
226233
},
227-
emailLoginScreen: {
234+
otpLoginScreen: {
228235
title: "サインイン",
229236
enterCodeSendTo: "送信された確認コードを入力してください",
230237
newDeviceDetected: "新しいデバイスが検出されました",

legacy_packages/react/src/evm/locales/tl.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,19 +215,26 @@ export function tlDefault(): ThirdwebLocale {
215215
signInWithGoogle: "Mag-sign in gamit ang Google",
216216
signInWithFacebook: "Mag-sign in gamit ang Facebook",
217217
signInWithApple: "Mag-sign in gamit ang Apple",
218+
signInWithEmail: "Mag-sign in gamit ang email",
218219
emailPlaceholder: "Ilagay ang iyong email address",
219220
submitEmail: "Magpatuloy",
220221
signIn: "Mag-sign in",
221222
emailRequired: "Kinakailangan ang email address",
222223
invalidEmail: "Hindi wastong email address",
223224
maxAccountsExceeded: "Naabot mo na ang maximum na bilang ng accounts",
225+
invalidPhone: "Hindi wastong numero ng telepono",
226+
invalidEmailOrPhone: "Hindi wastong email address o numero ng telepono",
227+
countryCodeMissing: "Phone number must start with a country code",
228+
phonePlaceholder: "Ilagay ang iyong numero ng telepono",
229+
signInWithPhone: "Mag-login gamit ang numero ng telepono",
230+
phoneRequired: "Kinakailangan ang numero ng telepono",
224231
socialLoginScreen: {
225232
title: "Mag-sign in",
226233
instruction: "Mag-sign in sa iyong account sa pop-up",
227234
failed: "Hindi nagawa ang pag-sign in",
228235
retry: "Subukan muli",
229236
},
230-
emailLoginScreen: {
237+
otpLoginScreen: {
231238
title: "Mag-sign in",
232239
enterCodeSendTo: "Ilagay ang verification code na ipinadala sa",
233240
newDeviceDetected: "Natuklasan ang bagong device",

legacy_packages/react/src/evm/locales/types.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ export type ThirdwebLocale = {
131131
submitButton: string;
132132
title: string;
133133
};
134-
emailLoginScreen: {
134+
otpLoginScreen: {
135135
enterCodeSendTo: string;
136136
enterRecoveryCode: string;
137137
failedToSendCode: string;
@@ -143,6 +143,7 @@ export type ThirdwebLocale = {
143143
title: string;
144144
verify: string;
145145
};
146+
signInWithEmail: string;
146147
emailPlaceholder: string;
147148
emailRequired: string;
148149
enterPassword: {
@@ -153,6 +154,12 @@ export type ThirdwebLocale = {
153154
wrongPassword: string;
154155
};
155156
invalidEmail: string;
157+
invalidEmailOrPhone: string;
158+
invalidPhone: string;
159+
countryCodeMissing: string;
160+
phonePlaceholder: string;
161+
signInWithPhone: string;
162+
phoneRequired: string;
156163
signIn: string;
157164
signInWithApple: string;
158165
signInWithFacebook: string;

0 commit comments

Comments
 (0)