Skip to content

Commit 207f78e

Browse files
authored
Embedded Wallet Phone Number SIgn In (#157)
Native only, WebGL in ~1/2M
1 parent 3458d5b commit 207f78e

File tree

12 files changed

+12557
-10928
lines changed

12 files changed

+12557
-10928
lines changed

Assets/Thirdweb/Core/Plugin/thirdweb.jslib

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ var plugin = {
5757
chainId,
5858
password,
5959
email,
60+
phoneNumber,
6061
personalWallet,
6162
authOptions,
6263
smartWalletAccountOverride,
@@ -74,6 +75,7 @@ var plugin = {
7475
UTF8ToString(chainId),
7576
UTF8ToString(password),
7677
UTF8ToString(email),
78+
UTF8ToString(phoneNumber),
7779
UTF8ToString(personalWallet),
7880
UTF8ToString(authOptions),
7981
UTF8ToString(smartWalletAccountOverride)
Binary file not shown.

Assets/Thirdweb/Core/Plugins/EmbeddedWallet/embedded-wallet.dll.meta

Lines changed: 2 additions & 56 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Assets/Thirdweb/Core/Prefabs/WalletProvider_EmbeddedWallet.prefab

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1377,7 +1377,7 @@ GameObject:
13771377
m_Icon: {fileID: 0}
13781378
m_NavMeshLayer: 0
13791379
m_StaticEditorFlags: 0
1380-
m_IsActive: 1
1380+
m_IsActive: 0
13811381
--- !u!224 &1834539732908528857
13821382
RectTransform:
13831383
m_ObjectHideFlags: 0
@@ -1647,6 +1647,9 @@ MonoBehaviour:
16471647
RecoveryCodesCanvas: {fileID: 3065337689055971296}
16481648
RecoveryCodesText: {fileID: 4840930599477263590}
16491649
RecoveryCodesCopy: {fileID: 7908066674043299061}
1650+
OnEmailOTPVerificationFailed:
1651+
m_PersistentCalls:
1652+
m_Calls: []
16501653
--- !u!1 &3462065987877024667
16511654
GameObject:
16521655
m_ObjectHideFlags: 0
@@ -2440,7 +2443,7 @@ MonoBehaviour:
24402443
m_OnCullStateChanged:
24412444
m_PersistentCalls:
24422445
m_Calls: []
2443-
m_text: Enter the code sent to your email
2446+
m_text: Enter the code sent to you
24442447
m_isRightToLeft: 0
24452448
m_fontAsset: {fileID: 11400000, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}
24462449
m_sharedMaterial: {fileID: 2180264, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}
@@ -2467,7 +2470,7 @@ MonoBehaviour:
24672470
m_faceColor:
24682471
serializedVersion: 2
24692472
rgba: 4294967295
2470-
m_fontSize: 40.75
2473+
m_fontSize: 44.75
24712474
m_fontSizeBase: 36
24722475
m_fontWeight: 400
24732476
m_enableAutoSizing: 1

Assets/Thirdweb/Core/Scripts/Bridge.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ public static async Task<string> Connect(WalletConnection walletConnection)
9999
walletConnection.chainId.ToString(),
100100
string.IsNullOrEmpty(walletConnection.password) ? Utils.GetDeviceIdentifier() : walletConnection.password,
101101
walletConnection.email,
102+
walletConnection.phoneNumber,
102103
walletConnection.personalWallet.ToString()[..1].ToLower() + walletConnection.personalWallet.ToString()[1..],
103104
JsonConvert.SerializeObject(walletConnection.authOptions),
104105
walletConnection.smartWalletAccountOverride,
@@ -457,7 +458,7 @@ public static async Task<string> ResolveAddressFromENS(string ens)
457458
[DllImport("__Internal")]
458459
private static extern string ThirdwebInitialize(string chainOrRPC, string options);
459460
[DllImport("__Internal")]
460-
private static extern string ThirdwebConnect(string taskId, string wallet, string chainId, string password, string email, string personalWallet, string authOptions, string smartWalletAccountOverride, Action<string, string, string> cb);
461+
private static extern string ThirdwebConnect(string taskId, string wallet, string chainId, string password, string email, string phoneNumber, string personalWallet, string authOptions, string smartWalletAccountOverride, Action<string, string, string> cb);
461462
[DllImport("__Internal")]
462463
private static extern string ThirdwebDisconnect(string taskId, Action<string, string, string> cb);
463464
[DllImport("__Internal")]

Assets/Thirdweb/Core/Scripts/Utils.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -719,6 +719,12 @@ public static string HashMessage(this string message)
719719
return sha3.CalculateHash(message);
720720
}
721721

722+
public static bool IsValidEmail(string email)
723+
{
724+
var emailRegex = new System.Text.RegularExpressions.Regex(@"^\S+@\S+\.\S+$");
725+
return emailRegex.IsMatch(email.Replace("+", ""));
726+
}
727+
722728
public static string GenerateRandomString(int v)
723729
{
724730
var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";

Assets/Thirdweb/Core/Scripts/Wallet.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -965,6 +965,7 @@ public class WalletConnection
965965
public BigInteger chainId;
966966
public string password;
967967
public string email;
968+
public string phoneNumber;
968969
public WalletProvider personalWallet;
969970
public AuthOptions authOptions;
970971
public string smartWalletAccountOverride;
@@ -985,6 +986,7 @@ public WalletConnection(
985986
BigInteger chainId,
986987
string password = null,
987988
string email = null,
989+
string phoneNumber = null,
988990
WalletProvider personalWallet = WalletProvider.LocalWallet,
989991
AuthOptions authOptions = null,
990992
string smartWalletAccountOverride = null
@@ -994,6 +996,7 @@ public WalletConnection(
994996
this.chainId = chainId;
995997
this.password = password;
996998
this.email = email;
999+
this.phoneNumber = phoneNumber;
9971000
this.personalWallet = personalWallet;
9981001
this.authOptions = authOptions ?? new AuthOptions(authProvider: AuthProvider.EmailOTP, jwtOrPayload: null, encryptionKey: null);
9991002
this.smartWalletAccountOverride = smartWalletAccountOverride;
@@ -1076,5 +1079,10 @@ public enum AuthProvider
10761079
/// Custom Authentication Flow, checks payload against developer-set Auth Endpoint.
10771080
/// </summary>
10781081
AuthEndpoint,
1082+
1083+
/// <summary>
1084+
/// Phone Number OTP Flow.
1085+
/// </summary>
1086+
PhoneOTP
10791087
}
10801088
}

Assets/Thirdweb/Core/Scripts/Wallets/ThirdwebEmbeddedWallet.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ public async Task<string> Connect(WalletConnection walletConnection, string rpc)
3333
GameObject.Instantiate(ThirdwebManager.Instance.EmbeddedWalletPrefab);
3434
}
3535

36-
var user = await EmbeddedWalletUI.Instance.Connect(_embeddedWallet, walletConnection.email, walletConnection.authOptions);
36+
var user = await EmbeddedWalletUI.Instance.Connect(_embeddedWallet, walletConnection.email, walletConnection.phoneNumber, walletConnection.authOptions);
3737
_account = user.Account;
3838
_email = user.EmailAddress;
3939
_web3 = new Web3(_account, rpc);

Assets/Thirdweb/Core/Scripts/WalletsUI/EmbeddedWalletUI.cs

Lines changed: 73 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,11 @@ public class EmbeddedWalletUI : MonoBehaviour
2828
public Button RecoveryCodesCopy;
2929

3030
[Tooltip("Invoked when the user submits an invalid OTP and can retry.")]
31-
public UnityEvent OnEmailOTPVerificationFailed;
31+
public UnityEvent OnOTPVerificationFailed;
3232

3333
protected EmbeddedWallet _embeddedWallet;
3434
protected string _email;
35+
protected string _phone;
3536
protected User _user;
3637
protected Exception _exception;
3738
protected string _callbackUrl;
@@ -61,14 +62,15 @@ private void Awake()
6162

6263
#region Connection Flow
6364

64-
public virtual async Task<User> Connect(EmbeddedWallet embeddedWallet, string email, AuthOptions authOptions)
65+
public virtual async Task<User> Connect(EmbeddedWallet embeddedWallet, string email, string phoneNumber, AuthOptions authOptions)
6566
{
6667
var config = Resources.Load<ThirdwebConfig>("ThirdwebConfig");
6768
_customScheme = config != null ? config.customScheme : null;
6869
if (!string.IsNullOrEmpty(_customScheme))
6970
_customScheme = _customScheme.EndsWith("://") ? _customScheme : $"{_customScheme}://";
7071
_embeddedWallet = embeddedWallet;
7172
_email = email;
73+
_phone = phoneNumber;
7274
_user = null;
7375
_exception = null;
7476
OTPInput.text = "";
@@ -88,6 +90,7 @@ public virtual async Task<User> Connect(EmbeddedWallet embeddedWallet, string em
8890
AuthProvider.Apple => "Apple",
8991
AuthProvider.Facebook => "Facebook",
9092
AuthProvider.JWT => "CustomAuth",
93+
AuthProvider.PhoneOTP => "PhoneOTP",
9194
_ => throw new UnityException($"Unsupported auth provider: {authOptions.authProvider}"),
9295
};
9396
return await _embeddedWallet.GetUserAsync(_email, authProvider);
@@ -119,6 +122,9 @@ public virtual async Task<User> Connect(EmbeddedWallet embeddedWallet, string em
119122
case AuthProvider.AuthEndpoint:
120123
await LoginWithAuthEndpoint(authOptions.jwtOrPayload, authOptions.encryptionKey);
121124
break;
125+
case AuthProvider.PhoneOTP:
126+
await LoginWithPhoneNumber();
127+
break;
122128
default:
123129
throw new UnityException($"Unsupported auth provider: {authOptions.authProvider}");
124130
}
@@ -180,9 +186,72 @@ public virtual async void OnSubmitOTP()
180186
var res = await _embeddedWallet.VerifyOtpAsync(_email, otp, string.IsNullOrEmpty(RecoveryInput.text) ? null : RecoveryInput.text);
181187
if (res.User == null)
182188
{
183-
if (res.CanRetry && OnEmailOTPVerificationFailed.GetPersistentEventCount() > 0)
189+
if (res.CanRetry && OnOTPVerificationFailed.GetPersistentEventCount() > 0)
190+
{
191+
OnOTPVerificationFailed.Invoke();
192+
return;
193+
}
194+
_exception = new UnityException("User OTP Verification Failed.");
195+
return;
196+
}
197+
_user = res.User;
198+
ShowRecoveryCodes(res);
199+
}
200+
catch (Exception e)
201+
{
202+
_exception = e;
203+
}
204+
finally
205+
{
206+
OTPInput.interactable = true;
207+
RecoveryInput.interactable = true;
208+
SubmitButton.interactable = true;
209+
}
210+
}
211+
212+
#endregion
213+
214+
#region Phone Number Flow
215+
216+
public virtual async Task LoginWithPhoneNumber()
217+
{
218+
if (_phone == null)
219+
throw new UnityException("Phone number is required!");
220+
221+
SubmitButton.onClick.AddListener(OnSubmitPhoneOTP);
222+
await OnSendPhoneOTP();
223+
EmbeddedWalletCanvas.SetActive(true);
224+
}
225+
226+
public virtual async Task OnSendPhoneOTP()
227+
{
228+
try
229+
{
230+
(bool isNewUser, bool isNewDevice, bool needsRecoveryCode) = await _embeddedWallet.SendOtpPhoneAsync(_phone);
231+
if (needsRecoveryCode && !isNewUser && isNewDevice)
232+
DisplayRecoveryInput(false);
233+
ThirdwebDebug.Log($"finished sending OTP: isNewUser {isNewUser}, isNewDevice {isNewDevice}");
234+
}
235+
catch (Exception e)
236+
{
237+
_exception = e;
238+
}
239+
}
240+
241+
public virtual async void OnSubmitPhoneOTP()
242+
{
243+
OTPInput.interactable = false;
244+
RecoveryInput.interactable = false;
245+
SubmitButton.interactable = false;
246+
try
247+
{
248+
string otp = OTPInput.text;
249+
var res = await _embeddedWallet.VerifyPhoneOtpAsync(_phone, otp, string.IsNullOrEmpty(RecoveryInput.text) ? null : RecoveryInput.text);
250+
if (res.User == null)
251+
{
252+
if (res.CanRetry && OnOTPVerificationFailed.GetPersistentEventCount() > 0)
184253
{
185-
OnEmailOTPVerificationFailed.Invoke();
254+
OnOTPVerificationFailed.Invoke();
186255
return;
187256
}
188257
_exception = new UnityException("User OTP Verification Failed.");

Assets/Thirdweb/Examples/Prefabs/Prefab_ConnectWallet.prefab

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1196,7 +1196,7 @@ GameObject:
11961196
m_Icon: {fileID: 0}
11971197
m_NavMeshLayer: 0
11981198
m_StaticEditorFlags: 0
1199-
m_IsActive: 0
1199+
m_IsActive: 1
12001200
--- !u!224 &894485209110800966
12011201
RectTransform:
12021202
m_ObjectHideFlags: 0
@@ -2946,7 +2946,7 @@ MonoBehaviour:
29462946
m_OnCullStateChanged:
29472947
m_PersistentCalls:
29482948
m_Calls: []
2949-
m_text: Email
2949+
m_text: Email & Phone
29502950
m_isRightToLeft: 0
29512951
m_fontAsset: {fileID: 11400000, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}
29522952
m_sharedMaterial: {fileID: 2180264, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}
@@ -6419,7 +6419,7 @@ MonoBehaviour:
64196419
m_OnCullStateChanged:
64206420
m_PersistentCalls:
64216421
m_Calls: []
6422-
m_text: Enter your email address
6422+
m_text: Enter your email address or phone number
64236423
m_isRightToLeft: 0
64246424
m_fontAsset: {fileID: 11400000, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}
64256425
m_sharedMaterial: {fileID: 2180264, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}
@@ -10157,14 +10157,14 @@ MonoBehaviour:
1015710157
m_VerticalScrollbarEventHandler: {fileID: 0}
1015810158
m_LayoutGroup: {fileID: 0}
1015910159
m_ScrollSensitivity: 1
10160-
m_ContentType: 6
10160+
m_ContentType: 0
1016110161
m_InputType: 0
1016210162
m_AsteriskChar: 42
10163-
m_KeyboardType: 7
10163+
m_KeyboardType: 0
1016410164
m_LineType: 0
1016510165
m_HideMobileInput: 0
1016610166
m_HideSoftKeyboard: 0
10167-
m_CharacterValidation: 7
10167+
m_CharacterValidation: 0
1016810168
m_RegexValue:
1016910169
m_GlobalPointSize: 14
1017010170
m_CharacterLimit: 0

0 commit comments

Comments
 (0)