Skip to content

Commit 17f87c5

Browse files
authored
Expose linking functionality at IThirdwebWallet level (#82)
1 parent 2726e5c commit 17f87c5

File tree

5 files changed

+125
-32
lines changed

5 files changed

+125
-32
lines changed

Thirdweb/Thirdweb.Wallets/IThirdwebWallet.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,20 @@ Task<string> RecoverAddressFromTypedDataV4<T, TDomain>(T data, TypedData<TDomain
129129
/// Disconnects the wallet (if using InAppWallet, clears session)
130130
/// </summary>
131131
Task Disconnect();
132+
133+
Task<List<LinkedAccount>> LinkAccount(
134+
IThirdwebWallet walletToLink,
135+
string otp = null,
136+
bool? isMobile = null,
137+
Action<string> browserOpenAction = null,
138+
string mobileRedirectScheme = "thirdweb://",
139+
IThirdwebBrowser browser = null,
140+
System.Numerics.BigInteger? chainId = null,
141+
string jwt = null,
142+
string payload = null
143+
);
144+
145+
Task<List<LinkedAccount>> GetLinkedAccounts();
132146
}
133147

134148
/// <summary>

Thirdweb/Thirdweb.Wallets/InAppWallet/EcosystemWallet/EcosystemWallet.cs

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -267,8 +267,8 @@ public string GetPhoneNumber()
267267

268268
#region Account Linking
269269

270-
public async Task<List<LinkedAccount>> LinkAccount(
271-
EcosystemWallet walletToLink,
270+
public override async Task<List<LinkedAccount>> LinkAccount(
271+
IThirdwebWallet walletToLink,
272272
string otp = null,
273273
bool? isMobile = null,
274274
Action<string> browserOpenAction = null,
@@ -289,51 +289,56 @@ public async Task<List<LinkedAccount>> LinkAccount(
289289
throw new ArgumentNullException(nameof(walletToLink), "Wallet to link cannot be null.");
290290
}
291291

292-
if (await walletToLink.IsConnected().ConfigureAwait(false))
292+
if (walletToLink is not EcosystemWallet ecosystemWallet)
293+
{
294+
throw new ArgumentException("Cannot link account with a non-EcosystemWallet wallet.");
295+
}
296+
297+
if (await ecosystemWallet.IsConnected().ConfigureAwait(false))
293298
{
294299
throw new ArgumentException("Cannot link account with a wallet that is already created and connected.");
295300
}
296301

297302
Server.VerifyResult serverRes = null;
298-
switch (walletToLink._authProvider)
303+
switch (ecosystemWallet._authProvider)
299304
{
300305
case "Email":
301-
if (string.IsNullOrEmpty(walletToLink._email))
306+
if (string.IsNullOrEmpty(ecosystemWallet._email))
302307
{
303308
throw new ArgumentException("Cannot link account with an email wallet that does not have an email address.");
304309
}
305-
serverRes = await walletToLink.PreAuth_Otp(otp).ConfigureAwait(false);
310+
serverRes = await ecosystemWallet.PreAuth_Otp(otp).ConfigureAwait(false);
306311
break;
307312
case "Phone":
308-
if (string.IsNullOrEmpty(walletToLink._phoneNumber))
313+
if (string.IsNullOrEmpty(ecosystemWallet._phoneNumber))
309314
{
310315
throw new ArgumentException("Cannot link account with a phone wallet that does not have a phone number.");
311316
}
312-
serverRes = await walletToLink.PreAuth_Otp(otp).ConfigureAwait(false);
317+
serverRes = await ecosystemWallet.PreAuth_Otp(otp).ConfigureAwait(false);
313318
break;
314319
case "Siwe":
315-
if (walletToLink._siweSigner == null || chainId == null)
320+
if (ecosystemWallet._siweSigner == null || chainId == null)
316321
{
317322
throw new ArgumentException("Cannot link account with a Siwe wallet without a signer and chain ID.");
318323
}
319-
serverRes = await walletToLink.PreAuth_Siwe(walletToLink._siweSigner, chainId.Value).ConfigureAwait(false);
324+
serverRes = await ecosystemWallet.PreAuth_Siwe(ecosystemWallet._siweSigner, chainId.Value).ConfigureAwait(false);
320325
break;
321326
case "JWT":
322327
if (string.IsNullOrEmpty(jwt))
323328
{
324329
throw new ArgumentException("Cannot link account with a JWT wallet without a JWT.");
325330
}
326-
serverRes = await walletToLink.PreAuth_JWT(jwt).ConfigureAwait(false);
331+
serverRes = await ecosystemWallet.PreAuth_JWT(jwt).ConfigureAwait(false);
327332
break;
328333
case "AuthEndpoint":
329334
if (string.IsNullOrEmpty(payload))
330335
{
331336
throw new ArgumentException("Cannot link account with an AuthEndpoint wallet without a payload.");
332337
}
333-
serverRes = await walletToLink.PreAuth_AuthEndpoint(payload).ConfigureAwait(false);
338+
serverRes = await ecosystemWallet.PreAuth_AuthEndpoint(payload).ConfigureAwait(false);
334339
break;
335340
case "Guest":
336-
serverRes = await walletToLink.PreAuth_Guest().ConfigureAwait(false);
341+
serverRes = await ecosystemWallet.PreAuth_Guest().ConfigureAwait(false);
337342
break;
338343
case "Google":
339344
case "Apple":
@@ -344,10 +349,10 @@ public async Task<List<LinkedAccount>> LinkAccount(
344349
case "Line":
345350
case "X":
346351
case "Coinbase":
347-
serverRes = await walletToLink.PreAuth_OAuth(isMobile ?? false, browserOpenAction, mobileRedirectScheme, browser).ConfigureAwait(false);
352+
serverRes = await ecosystemWallet.PreAuth_OAuth(isMobile ?? false, browserOpenAction, mobileRedirectScheme, browser).ConfigureAwait(false);
348353
break;
349354
default:
350-
throw new ArgumentException($"Cannot link account with an unsupported authentication provider:", walletToLink._authProvider);
355+
throw new ArgumentException($"Cannot link account with an unsupported authentication provider:", ecosystemWallet._authProvider);
351356
}
352357

353358
var currentAccountToken = this._embeddedWallet.GetSessionData()?.AuthToken;
@@ -374,7 +379,7 @@ public async Task<List<LinkedAccount>> LinkAccount(
374379
return linkedAccounts;
375380
}
376381

377-
public async Task<List<LinkedAccount>> GetLinkedAccounts()
382+
public override async Task<List<LinkedAccount>> GetLinkedAccounts()
378383
{
379384
var currentAccountToken = this._embeddedWallet.GetSessionData()?.AuthToken;
380385
var serverLinkedAccounts = await this._embeddedWallet.GetLinkedAccountsAsync(currentAccountToken).ConfigureAwait(false);

Thirdweb/Thirdweb.Wallets/InAppWallet/InAppWallet.cs

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -156,8 +156,8 @@ public Task<string> GetPhoneNumber()
156156

157157
#region Account Linking
158158

159-
public async Task<List<LinkedAccount>> LinkAccount(
160-
InAppWallet walletToLink,
159+
public override async Task<List<LinkedAccount>> LinkAccount(
160+
IThirdwebWallet walletToLink,
161161
string otp = null,
162162
bool? isMobile = null,
163163
Action<string> browserOpenAction = null,
@@ -178,51 +178,56 @@ public async Task<List<LinkedAccount>> LinkAccount(
178178
throw new ArgumentNullException(nameof(walletToLink), "Wallet to link cannot be null.");
179179
}
180180

181-
if (await walletToLink.IsConnected().ConfigureAwait(false))
181+
if (walletToLink is not InAppWallet inAppWallet)
182+
{
183+
throw new ArgumentException("Cannot link account with a wallet that is not an InAppWallet.");
184+
}
185+
186+
if (await inAppWallet.IsConnected().ConfigureAwait(false))
182187
{
183188
throw new ArgumentException("Cannot link account with a wallet that is already created and connected.");
184189
}
185190

186191
Server.VerifyResult serverRes = null;
187-
switch (walletToLink.AuthProvider)
192+
switch (inAppWallet.AuthProvider)
188193
{
189194
case "Email":
190-
if (string.IsNullOrEmpty(walletToLink.Email))
195+
if (string.IsNullOrEmpty(inAppWallet.Email))
191196
{
192197
throw new ArgumentException("Cannot link account with an email wallet that does not have an email address.");
193198
}
194-
serverRes = await walletToLink.PreAuth_Otp(otp).ConfigureAwait(false);
199+
serverRes = await inAppWallet.PreAuth_Otp(otp).ConfigureAwait(false);
195200
break;
196201
case "Phone":
197-
if (string.IsNullOrEmpty(walletToLink.PhoneNumber))
202+
if (string.IsNullOrEmpty(inAppWallet.PhoneNumber))
198203
{
199204
throw new ArgumentException("Cannot link account with a phone wallet that does not have a phone number.");
200205
}
201-
serverRes = await walletToLink.PreAuth_Otp(otp).ConfigureAwait(false);
206+
serverRes = await inAppWallet.PreAuth_Otp(otp).ConfigureAwait(false);
202207
break;
203208
case "Siwe":
204-
if (walletToLink.SiweSigner == null || chainId == null)
209+
if (inAppWallet.SiweSigner == null || chainId == null)
205210
{
206211
throw new ArgumentException("Cannot link account with a Siwe wallet without a signer and chain ID.");
207212
}
208-
serverRes = await walletToLink.PreAuth_Siwe(walletToLink.SiweSigner, chainId.Value).ConfigureAwait(false);
213+
serverRes = await inAppWallet.PreAuth_Siwe(inAppWallet.SiweSigner, chainId.Value).ConfigureAwait(false);
209214
break;
210215
case "JWT":
211216
if (string.IsNullOrEmpty(jwt))
212217
{
213218
throw new ArgumentException("Cannot link account with a JWT wallet without a JWT.");
214219
}
215-
serverRes = await walletToLink.PreAuth_JWT(jwt).ConfigureAwait(false);
220+
serverRes = await inAppWallet.PreAuth_JWT(jwt).ConfigureAwait(false);
216221
break;
217222
case "AuthEndpoint":
218223
if (string.IsNullOrEmpty(payload))
219224
{
220225
throw new ArgumentException("Cannot link account with an AuthEndpoint wallet without a payload.");
221226
}
222-
serverRes = await walletToLink.PreAuth_AuthEndpoint(payload).ConfigureAwait(false);
227+
serverRes = await inAppWallet.PreAuth_AuthEndpoint(payload).ConfigureAwait(false);
223228
break;
224229
case "Guest":
225-
serverRes = await walletToLink.PreAuth_Guest().ConfigureAwait(false);
230+
serverRes = await inAppWallet.PreAuth_Guest().ConfigureAwait(false);
226231
break;
227232
case "Google":
228233
case "Apple":
@@ -233,10 +238,10 @@ public async Task<List<LinkedAccount>> LinkAccount(
233238
case "Line":
234239
case "X":
235240
case "Coinbase":
236-
serverRes = await walletToLink.PreAuth_OAuth(isMobile ?? false, browserOpenAction, mobileRedirectScheme, browser).ConfigureAwait(false);
241+
serverRes = await inAppWallet.PreAuth_OAuth(isMobile ?? false, browserOpenAction, mobileRedirectScheme, browser).ConfigureAwait(false);
237242
break;
238243
default:
239-
throw new ArgumentException($"Cannot link account with an unsupported authentication provider:", walletToLink.AuthProvider);
244+
throw new ArgumentException($"Cannot link account with an unsupported authentication provider:", inAppWallet.AuthProvider);
240245
}
241246

242247
var currentAccountToken = this.EmbeddedWallet.GetSessionData()?.AuthToken;
@@ -263,7 +268,7 @@ public async Task<List<LinkedAccount>> LinkAccount(
263268
return linkedAccounts;
264269
}
265270

266-
public async Task<List<LinkedAccount>> GetLinkedAccounts()
271+
public override async Task<List<LinkedAccount>> GetLinkedAccounts()
267272
{
268273
var currentAccountToken = this.EmbeddedWallet.GetSessionData()?.AuthToken;
269274
var serverLinkedAccounts = await this.EmbeddedWallet.GetLinkedAccountsAsync(currentAccountToken).ConfigureAwait(false);

Thirdweb/Thirdweb.Wallets/PrivateKeyWallet/PrivateKeyWallet.cs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System.Numerics;
12
using System.Text;
23
using Nethereum.ABI.EIP712;
34
using Nethereum.Hex.HexConvertors.Extensions;
@@ -359,5 +360,25 @@ public virtual Task<ThirdwebTransactionReceipt> ExecuteTransaction(ThirdwebTrans
359360
throw new InvalidOperationException("ExecuteTransaction is not supported for private key wallets, please use the unified Contract or ThirdwebTransaction APIs.");
360361
}
361362

363+
public virtual Task<List<LinkedAccount>> LinkAccount(
364+
IThirdwebWallet walletToLink,
365+
string otp = null,
366+
bool? isMobile = null,
367+
Action<string> browserOpenAction = null,
368+
string mobileRedirectScheme = "thirdweb://",
369+
IThirdwebBrowser browser = null,
370+
BigInteger? chainId = null,
371+
string jwt = null,
372+
string payload = null
373+
)
374+
{
375+
throw new InvalidOperationException("LinkAccount is not supported for private key wallets.");
376+
}
377+
378+
public virtual Task<List<LinkedAccount>> GetLinkedAccounts()
379+
{
380+
throw new InvalidOperationException("GetLinkedAccounts is not supported for private key wallets.");
381+
}
382+
362383
#endregion
363384
}

Thirdweb/Thirdweb.Wallets/SmartWallet/SmartWallet.cs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1014,4 +1014,52 @@ public Task Disconnect()
10141014
this._accountContract = null;
10151015
return Task.CompletedTask;
10161016
}
1017+
1018+
public async Task<List<LinkedAccount>> LinkAccount(
1019+
IThirdwebWallet walletToLink,
1020+
string otp = null,
1021+
bool? isMobile = null,
1022+
Action<string> browserOpenAction = null,
1023+
string mobileRedirectScheme = "thirdweb://",
1024+
IThirdwebBrowser browser = null,
1025+
BigInteger? chainId = null,
1026+
string jwt = null,
1027+
string payload = null
1028+
)
1029+
{
1030+
var personalWallet = await this.GetPersonalWallet().ConfigureAwait(false);
1031+
if (personalWallet is not InAppWallet and not EcosystemWallet)
1032+
{
1033+
throw new Exception("SmartWallet.LinkAccount is only supported if the signer is an InAppWallet or EcosystemWallet");
1034+
}
1035+
else if (walletToLink is not InAppWallet and not EcosystemWallet)
1036+
{
1037+
throw new Exception("SmartWallet.LinkAccount is only supported if the wallet to link is an InAppWallet or EcosystemWallet");
1038+
}
1039+
else if (personalWallet is InAppWallet && walletToLink is not InAppWallet)
1040+
{
1041+
throw new Exception("SmartWallet.LinkAccount with an InAppWallet signer is only supported if the wallet to link is also an InAppWallet");
1042+
}
1043+
else if (personalWallet is EcosystemWallet && walletToLink is not EcosystemWallet)
1044+
{
1045+
throw new Exception("SmartWallet.LinkAccount with an EcosystemWallet signer is only supported if the wallet to link is also an EcosystemWallet");
1046+
}
1047+
else
1048+
{
1049+
return await personalWallet.LinkAccount(walletToLink, otp, isMobile, browserOpenAction, mobileRedirectScheme, browser, chainId, jwt, payload).ConfigureAwait(false);
1050+
}
1051+
}
1052+
1053+
public async Task<List<LinkedAccount>> GetLinkedAccounts()
1054+
{
1055+
var personalWallet = await this.GetPersonalWallet().ConfigureAwait(false);
1056+
if (personalWallet is not InAppWallet and not EcosystemWallet)
1057+
{
1058+
throw new Exception("SmartWallet.LinkAccount is only supported if the signer is an InAppWallet or EcosystemWallet");
1059+
}
1060+
else
1061+
{
1062+
return await personalWallet.GetLinkedAccounts().ConfigureAwait(false);
1063+
}
1064+
}
10171065
}

0 commit comments

Comments
 (0)