@rhinestone/1auth 0.1.0 → 0.1.2
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/README.md +130 -0
- package/dist/{chunk-UXYKIMGZ.mjs → chunk-TACK3LJN.mjs} +180 -21
- package/dist/chunk-TACK3LJN.mjs.map +1 -0
- package/dist/{client-C1inywuT.d.mts → client-DyYGKWj3.d.mts} +321 -33
- package/dist/{client-C1inywuT.d.ts → client-DyYGKWj3.d.ts} +321 -33
- package/dist/index.d.mts +29 -16
- package/dist/index.d.ts +29 -16
- package/dist/index.js +1041 -249
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +861 -229
- package/dist/index.mjs.map +1 -1
- package/dist/provider-CNTZPPFz.d.ts +33 -0
- package/dist/provider-Ctr7HQHR.d.mts +33 -0
- package/dist/react.d.mts +4 -4
- package/dist/react.d.ts +4 -4
- package/dist/react.js.map +1 -1
- package/dist/react.mjs.map +1 -1
- package/dist/server.d.mts +10 -44
- package/dist/server.d.ts +10 -44
- package/dist/server.js +11 -8
- package/dist/server.js.map +1 -1
- package/dist/server.mjs +11 -8
- package/dist/server.mjs.map +1 -1
- package/dist/wagmi.d.mts +5 -4
- package/dist/wagmi.d.ts +5 -4
- package/dist/wagmi.js +173 -23
- package/dist/wagmi.js.map +1 -1
- package/dist/wagmi.mjs +3 -2
- package/dist/wagmi.mjs.map +1 -1
- package/package.json +24 -11
- package/dist/chunk-UXYKIMGZ.mjs.map +0 -1
- package/dist/provider-Dgh51NRc.d.mts +0 -24
- package/dist/provider-q7M728Mn.d.ts +0 -24
package/dist/index.js
CHANGED
|
@@ -32,8 +32,11 @@ var index_exports = {};
|
|
|
32
32
|
__export(index_exports, {
|
|
33
33
|
BatchQueueProvider: () => BatchQueueProvider,
|
|
34
34
|
BatchQueueWidget: () => BatchQueueWidget,
|
|
35
|
+
ETHEREUM_MESSAGE_PREFIX: () => ETHEREUM_MESSAGE_PREFIX,
|
|
36
|
+
OneAuthClient: () => OneAuthClient,
|
|
35
37
|
PASSKEY_MESSAGE_PREFIX: () => PASSKEY_MESSAGE_PREFIX,
|
|
36
|
-
PasskeyProviderClient: () =>
|
|
38
|
+
PasskeyProviderClient: () => OneAuthClient,
|
|
39
|
+
createOneAuthProvider: () => createOneAuthProvider,
|
|
37
40
|
createPasskeyAccount: () => createPasskeyAccount,
|
|
38
41
|
createPasskeyProvider: () => createPasskeyProvider,
|
|
39
42
|
createPasskeyWalletClient: () => createPasskeyWalletClient,
|
|
@@ -155,7 +158,13 @@ function resolveTokenAddress(token, chainId) {
|
|
|
155
158
|
if ((0, import_viem.isAddress)(token)) {
|
|
156
159
|
return token;
|
|
157
160
|
}
|
|
158
|
-
|
|
161
|
+
const match = getSupportedTokens(chainId).find(
|
|
162
|
+
(t) => t.symbol.toUpperCase() === token.toUpperCase()
|
|
163
|
+
);
|
|
164
|
+
if (!match) {
|
|
165
|
+
return (0, import_sdk.getTokenAddress)(token, chainId);
|
|
166
|
+
}
|
|
167
|
+
return match.address;
|
|
159
168
|
}
|
|
160
169
|
function isTestnet(chainId) {
|
|
161
170
|
try {
|
|
@@ -189,10 +198,13 @@ var POPUP_HEIGHT = 600;
|
|
|
189
198
|
var DEFAULT_EMBED_WIDTH = "400px";
|
|
190
199
|
var DEFAULT_EMBED_HEIGHT = "500px";
|
|
191
200
|
var MODAL_WIDTH = 360;
|
|
192
|
-
var
|
|
201
|
+
var DEFAULT_PROVIDER_URL = "https://passkey.1auth.box";
|
|
202
|
+
var OneAuthClient = class {
|
|
193
203
|
constructor(config) {
|
|
194
|
-
|
|
195
|
-
|
|
204
|
+
const providerUrl = config.providerUrl || DEFAULT_PROVIDER_URL;
|
|
205
|
+
const dialogUrl = config.dialogUrl || providerUrl;
|
|
206
|
+
this.config = { ...config, providerUrl, dialogUrl };
|
|
207
|
+
this.theme = this.config.theme || {};
|
|
196
208
|
}
|
|
197
209
|
/**
|
|
198
210
|
* Update the theme configuration at runtime
|
|
@@ -254,9 +266,7 @@ var PasskeyProviderClient = class {
|
|
|
254
266
|
const response = await fetch(
|
|
255
267
|
`${this.config.providerUrl}/api/intent/status/${intentId}`,
|
|
256
268
|
{
|
|
257
|
-
headers: {
|
|
258
|
-
"x-client-id": this.config.clientId
|
|
259
|
-
}
|
|
269
|
+
headers: this.config.clientId ? { "x-client-id": this.config.clientId } : {}
|
|
260
270
|
}
|
|
261
271
|
);
|
|
262
272
|
if (response.ok) {
|
|
@@ -275,16 +285,16 @@ var PasskeyProviderClient = class {
|
|
|
275
285
|
return void 0;
|
|
276
286
|
}
|
|
277
287
|
/**
|
|
278
|
-
*
|
|
279
|
-
* This is the recommended method for authentication - shows a modal overlay
|
|
280
|
-
* with both sign in and create account options.
|
|
288
|
+
* Open the auth dialog (sign in + sign up).
|
|
281
289
|
*/
|
|
282
290
|
async authWithModal(options) {
|
|
283
291
|
const dialogUrl = this.getDialogUrl();
|
|
284
292
|
const params = new URLSearchParams({
|
|
285
|
-
clientId: this.config.clientId,
|
|
286
293
|
mode: "iframe"
|
|
287
294
|
});
|
|
295
|
+
if (this.config.clientId) {
|
|
296
|
+
params.set("clientId", this.config.clientId);
|
|
297
|
+
}
|
|
288
298
|
if (options?.username) {
|
|
289
299
|
params.set("username", options.username);
|
|
290
300
|
}
|
|
@@ -300,6 +310,55 @@ var PasskeyProviderClient = class {
|
|
|
300
310
|
const { dialog, iframe, cleanup } = this.createModalDialog(url);
|
|
301
311
|
return this.waitForModalAuthResponse(dialog, iframe, cleanup);
|
|
302
312
|
}
|
|
313
|
+
/**
|
|
314
|
+
* Open the connect dialog (lightweight connection without passkey auth).
|
|
315
|
+
*
|
|
316
|
+
* This method shows a simple connection confirmation dialog that doesn't
|
|
317
|
+
* require a passkey signature. Users can optionally enable "auto-connect"
|
|
318
|
+
* to skip this dialog in the future.
|
|
319
|
+
*
|
|
320
|
+
* If the user has never connected before, this will return action: "switch"
|
|
321
|
+
* to indicate that the full auth modal should be opened instead.
|
|
322
|
+
*
|
|
323
|
+
* @example
|
|
324
|
+
* ```typescript
|
|
325
|
+
* const result = await client.connectWithModal();
|
|
326
|
+
*
|
|
327
|
+
* if (result.success) {
|
|
328
|
+
* console.log('Connected as:', result.username);
|
|
329
|
+
* } else if (result.action === 'switch') {
|
|
330
|
+
* // User needs to sign in first
|
|
331
|
+
* const authResult = await client.authWithModal();
|
|
332
|
+
* }
|
|
333
|
+
* ```
|
|
334
|
+
*/
|
|
335
|
+
async connectWithModal(options) {
|
|
336
|
+
const dialogUrl = this.getDialogUrl();
|
|
337
|
+
const params = new URLSearchParams({
|
|
338
|
+
mode: "iframe"
|
|
339
|
+
});
|
|
340
|
+
if (this.config.clientId) {
|
|
341
|
+
params.set("clientId", this.config.clientId);
|
|
342
|
+
}
|
|
343
|
+
const themeParams = this.getThemeParams(options?.theme);
|
|
344
|
+
if (themeParams) {
|
|
345
|
+
const themeParsed = new URLSearchParams(themeParams);
|
|
346
|
+
themeParsed.forEach((value, key) => params.set(key, value));
|
|
347
|
+
}
|
|
348
|
+
const url = `${dialogUrl}/dialog/connect?${params.toString()}`;
|
|
349
|
+
const { dialog, iframe, cleanup } = this.createModalDialog(url);
|
|
350
|
+
const ready = await this.waitForDialogReady(dialog, iframe, cleanup, {
|
|
351
|
+
mode: "iframe"
|
|
352
|
+
});
|
|
353
|
+
if (!ready) {
|
|
354
|
+
return {
|
|
355
|
+
success: false,
|
|
356
|
+
action: "cancel",
|
|
357
|
+
error: { code: "USER_CANCELLED", message: "Connection was cancelled" }
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
return this.waitForConnectResponse(dialog, iframe, cleanup);
|
|
361
|
+
}
|
|
303
362
|
/**
|
|
304
363
|
* Authenticate a user with an optional challenge to sign.
|
|
305
364
|
*
|
|
@@ -335,9 +394,11 @@ var PasskeyProviderClient = class {
|
|
|
335
394
|
async authenticate(options) {
|
|
336
395
|
const dialogUrl = this.getDialogUrl();
|
|
337
396
|
const params = new URLSearchParams({
|
|
338
|
-
clientId: this.config.clientId,
|
|
339
397
|
mode: "iframe"
|
|
340
398
|
});
|
|
399
|
+
if (this.config.clientId) {
|
|
400
|
+
params.set("clientId", this.config.clientId);
|
|
401
|
+
}
|
|
341
402
|
if (options?.challenge) {
|
|
342
403
|
params.set("challenge", options.challenge);
|
|
343
404
|
}
|
|
@@ -351,28 +412,30 @@ var PasskeyProviderClient = class {
|
|
|
351
412
|
return this.waitForAuthenticateResponse(dialog, iframe, cleanup);
|
|
352
413
|
}
|
|
353
414
|
/**
|
|
354
|
-
* Show signing in a modal overlay (
|
|
415
|
+
* Show signing in a modal overlay (iframe dialog)
|
|
355
416
|
*/
|
|
356
417
|
async signWithModal(options) {
|
|
357
418
|
const dialogUrl = this.getDialogUrl();
|
|
358
419
|
const themeParams = this.getThemeParams(options?.theme);
|
|
359
420
|
const signingUrl = `${dialogUrl}/dialog/sign?mode=iframe${themeParams ? `&${themeParams}` : ""}`;
|
|
360
421
|
const { dialog, iframe, cleanup } = this.createModalDialog(signingUrl);
|
|
361
|
-
const
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
username: options.username,
|
|
369
|
-
description: options.description,
|
|
370
|
-
transaction: options.transaction,
|
|
371
|
-
metadata: options.metadata
|
|
372
|
-
}, dialogOrigin);
|
|
373
|
-
resolve();
|
|
374
|
-
};
|
|
422
|
+
const ready = await this.waitForDialogReady(dialog, iframe, cleanup, {
|
|
423
|
+
mode: "iframe",
|
|
424
|
+
challenge: options.challenge,
|
|
425
|
+
username: options.username,
|
|
426
|
+
description: options.description,
|
|
427
|
+
transaction: options.transaction,
|
|
428
|
+
metadata: options.metadata
|
|
375
429
|
});
|
|
430
|
+
if (!ready) {
|
|
431
|
+
return {
|
|
432
|
+
success: false,
|
|
433
|
+
error: {
|
|
434
|
+
code: "USER_REJECTED",
|
|
435
|
+
message: "User closed the dialog"
|
|
436
|
+
}
|
|
437
|
+
};
|
|
438
|
+
}
|
|
376
439
|
return this.waitForSigningResponse(dialog, iframe, cleanup);
|
|
377
440
|
}
|
|
378
441
|
/**
|
|
@@ -405,10 +468,24 @@ var PasskeyProviderClient = class {
|
|
|
405
468
|
* ```
|
|
406
469
|
*/
|
|
407
470
|
async sendIntent(options) {
|
|
408
|
-
const signedIntent = options.signedIntent
|
|
471
|
+
const signedIntent = options.signedIntent ? {
|
|
472
|
+
...options.signedIntent,
|
|
473
|
+
merchantId: options.signedIntent.merchantId || options.signedIntent.developerId
|
|
474
|
+
} : void 0;
|
|
409
475
|
const username = signedIntent?.username || options.username;
|
|
410
476
|
const targetChain = signedIntent?.targetChain || options.targetChain;
|
|
411
477
|
const calls = signedIntent?.calls || options.calls;
|
|
478
|
+
if (signedIntent && !signedIntent.merchantId) {
|
|
479
|
+
return {
|
|
480
|
+
success: false,
|
|
481
|
+
intentId: "",
|
|
482
|
+
status: "failed",
|
|
483
|
+
error: {
|
|
484
|
+
code: "INVALID_OPTIONS",
|
|
485
|
+
message: "Signed intent requires developerId (clientId)"
|
|
486
|
+
}
|
|
487
|
+
};
|
|
488
|
+
}
|
|
412
489
|
if (!username && !signedIntent?.accountAddress) {
|
|
413
490
|
return {
|
|
414
491
|
success: false,
|
|
@@ -431,16 +508,21 @@ var PasskeyProviderClient = class {
|
|
|
431
508
|
}
|
|
432
509
|
};
|
|
433
510
|
}
|
|
511
|
+
const serializedTokenRequests = options.tokenRequests?.map((r) => ({
|
|
512
|
+
token: r.token,
|
|
513
|
+
amount: r.amount.toString()
|
|
514
|
+
}));
|
|
434
515
|
let prepareResponse;
|
|
516
|
+
const requestBody = signedIntent || {
|
|
517
|
+
username: options.username,
|
|
518
|
+
targetChain: options.targetChain,
|
|
519
|
+
calls: options.calls,
|
|
520
|
+
tokenRequests: serializedTokenRequests,
|
|
521
|
+
sourceAssets: options.sourceAssets,
|
|
522
|
+
sourceChainId: options.sourceChainId,
|
|
523
|
+
...this.config.clientId && { clientId: this.config.clientId }
|
|
524
|
+
};
|
|
435
525
|
try {
|
|
436
|
-
const requestBody = signedIntent || {
|
|
437
|
-
username: options.username,
|
|
438
|
-
targetChain: options.targetChain,
|
|
439
|
-
calls: options.calls,
|
|
440
|
-
tokenRequests: options.tokenRequests,
|
|
441
|
-
sourceAssets: options.sourceAssets,
|
|
442
|
-
clientId: this.config.clientId
|
|
443
|
-
};
|
|
444
526
|
const response = await fetch(`${this.config.providerUrl}/api/intent/prepare`, {
|
|
445
527
|
method: "POST",
|
|
446
528
|
headers: {
|
|
@@ -477,112 +559,190 @@ var PasskeyProviderClient = class {
|
|
|
477
559
|
};
|
|
478
560
|
}
|
|
479
561
|
const dialogUrl = this.getDialogUrl();
|
|
480
|
-
const
|
|
562
|
+
const themeParams = this.getThemeParams();
|
|
563
|
+
const signingUrl = `${dialogUrl}/dialog/sign?mode=iframe${themeParams ? `&${themeParams}` : ""}`;
|
|
481
564
|
const { dialog, iframe, cleanup } = this.createModalDialog(signingUrl);
|
|
482
565
|
const dialogOrigin = this.getDialogOrigin();
|
|
483
|
-
await
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
accountAddress: prepareResponse.accountAddress,
|
|
497
|
-
intentId: prepareResponse.intentId
|
|
498
|
-
}, dialogOrigin);
|
|
499
|
-
resolve();
|
|
500
|
-
}
|
|
501
|
-
};
|
|
502
|
-
window.addEventListener("message", handleReady);
|
|
566
|
+
const ready = await this.waitForDialogReady(dialog, iframe, cleanup, {
|
|
567
|
+
mode: "iframe",
|
|
568
|
+
calls,
|
|
569
|
+
chainId: targetChain,
|
|
570
|
+
transaction: prepareResponse.transaction,
|
|
571
|
+
challenge: prepareResponse.challenge,
|
|
572
|
+
username,
|
|
573
|
+
accountAddress: prepareResponse.accountAddress,
|
|
574
|
+
originMessages: prepareResponse.originMessages,
|
|
575
|
+
tokenRequests: serializedTokenRequests,
|
|
576
|
+
expiresAt: prepareResponse.expiresAt,
|
|
577
|
+
userId: prepareResponse.userId,
|
|
578
|
+
intentOp: prepareResponse.intentOp
|
|
503
579
|
});
|
|
504
|
-
|
|
580
|
+
if (!ready) {
|
|
581
|
+
return {
|
|
582
|
+
success: false,
|
|
583
|
+
intentId: "",
|
|
584
|
+
status: "failed",
|
|
585
|
+
error: { code: "USER_CANCELLED", message: "User closed the dialog" }
|
|
586
|
+
};
|
|
587
|
+
}
|
|
588
|
+
const signingResult = await this.waitForSigningWithRefresh(
|
|
589
|
+
dialog,
|
|
590
|
+
iframe,
|
|
591
|
+
cleanup,
|
|
592
|
+
dialogOrigin,
|
|
593
|
+
// Refresh callback - called when dialog requests a quote refresh
|
|
594
|
+
async () => {
|
|
595
|
+
console.log("[SDK] Dialog requested quote refresh, re-preparing intent");
|
|
596
|
+
try {
|
|
597
|
+
const refreshResponse = await fetch(`${this.config.providerUrl}/api/intent/prepare`, {
|
|
598
|
+
method: "POST",
|
|
599
|
+
headers: { "Content-Type": "application/json" },
|
|
600
|
+
body: JSON.stringify(requestBody),
|
|
601
|
+
credentials: "include"
|
|
602
|
+
});
|
|
603
|
+
if (!refreshResponse.ok) {
|
|
604
|
+
console.error("[SDK] Quote refresh failed:", await refreshResponse.text());
|
|
605
|
+
return null;
|
|
606
|
+
}
|
|
607
|
+
const refreshedData = await refreshResponse.json();
|
|
608
|
+
prepareResponse = refreshedData;
|
|
609
|
+
return {
|
|
610
|
+
intentOp: refreshedData.intentOp,
|
|
611
|
+
expiresAt: refreshedData.expiresAt,
|
|
612
|
+
challenge: refreshedData.challenge,
|
|
613
|
+
originMessages: refreshedData.originMessages,
|
|
614
|
+
transaction: refreshedData.transaction
|
|
615
|
+
};
|
|
616
|
+
} catch (error) {
|
|
617
|
+
console.error("[SDK] Quote refresh error:", error);
|
|
618
|
+
return null;
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
);
|
|
505
622
|
if (!signingResult.success) {
|
|
506
623
|
return {
|
|
507
624
|
success: false,
|
|
508
|
-
intentId:
|
|
625
|
+
intentId: "",
|
|
626
|
+
// No intentId yet - signing was cancelled before execute
|
|
509
627
|
status: "failed",
|
|
510
628
|
error: signingResult.error
|
|
511
629
|
};
|
|
512
630
|
}
|
|
631
|
+
const dialogExecutedIntent = "intentId" in signingResult && signingResult.intentId;
|
|
513
632
|
let executeResponse;
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
633
|
+
if (dialogExecutedIntent) {
|
|
634
|
+
executeResponse = {
|
|
635
|
+
success: true,
|
|
636
|
+
intentId: signingResult.intentId,
|
|
637
|
+
status: "pending"
|
|
638
|
+
};
|
|
639
|
+
} else {
|
|
640
|
+
try {
|
|
641
|
+
const response = await fetch(`${this.config.providerUrl}/api/intent/execute`, {
|
|
642
|
+
method: "POST",
|
|
643
|
+
headers: {
|
|
644
|
+
"Content-Type": "application/json"
|
|
645
|
+
},
|
|
646
|
+
body: JSON.stringify({
|
|
647
|
+
// Data from prepare response (no intentId yet - created on execute)
|
|
648
|
+
intentOp: prepareResponse.intentOp,
|
|
649
|
+
userId: prepareResponse.userId,
|
|
650
|
+
targetChain: prepareResponse.targetChain,
|
|
651
|
+
calls: prepareResponse.calls,
|
|
652
|
+
expiresAt: prepareResponse.expiresAt,
|
|
653
|
+
// Signature from dialog
|
|
654
|
+
signature: signingResult.signature,
|
|
655
|
+
passkey: signingResult.passkey
|
|
656
|
+
// Include passkey info for signature encoding
|
|
657
|
+
})
|
|
658
|
+
});
|
|
659
|
+
if (!response.ok) {
|
|
660
|
+
const errorData = await response.json().catch(() => ({}));
|
|
661
|
+
this.sendTransactionStatus(iframe, "failed");
|
|
662
|
+
await this.waitForDialogClose(dialog, cleanup);
|
|
663
|
+
return {
|
|
664
|
+
success: false,
|
|
665
|
+
intentId: "",
|
|
666
|
+
// No intentId - execute failed before creation
|
|
667
|
+
status: "failed",
|
|
668
|
+
error: {
|
|
669
|
+
code: "EXECUTE_FAILED",
|
|
670
|
+
message: errorData.error || "Failed to execute intent"
|
|
671
|
+
}
|
|
672
|
+
};
|
|
673
|
+
}
|
|
674
|
+
executeResponse = await response.json();
|
|
675
|
+
} catch (error) {
|
|
529
676
|
this.sendTransactionStatus(iframe, "failed");
|
|
530
677
|
await this.waitForDialogClose(dialog, cleanup);
|
|
531
678
|
return {
|
|
532
679
|
success: false,
|
|
533
|
-
intentId:
|
|
680
|
+
intentId: "",
|
|
681
|
+
// No intentId - network error before creation
|
|
534
682
|
status: "failed",
|
|
535
683
|
error: {
|
|
536
|
-
code: "
|
|
537
|
-
message:
|
|
684
|
+
code: "NETWORK_ERROR",
|
|
685
|
+
message: error instanceof Error ? error.message : "Network error"
|
|
538
686
|
}
|
|
539
687
|
};
|
|
540
688
|
}
|
|
541
|
-
executeResponse = await response.json();
|
|
542
|
-
} catch (error) {
|
|
543
|
-
this.sendTransactionStatus(iframe, "failed");
|
|
544
|
-
await this.waitForDialogClose(dialog, cleanup);
|
|
545
|
-
return {
|
|
546
|
-
success: false,
|
|
547
|
-
intentId: prepareResponse.intentId,
|
|
548
|
-
status: "failed",
|
|
549
|
-
error: {
|
|
550
|
-
code: "NETWORK_ERROR",
|
|
551
|
-
message: error instanceof Error ? error.message : "Network error"
|
|
552
|
-
}
|
|
553
|
-
};
|
|
554
689
|
}
|
|
555
|
-
const closeOn = options.closeOn || "preconfirmed";
|
|
556
|
-
const acceptPreconfirmations = closeOn !== "completed";
|
|
557
690
|
let finalStatus = executeResponse.status;
|
|
558
691
|
let finalTxHash = executeResponse.transactionHash;
|
|
559
692
|
if (finalStatus === "pending") {
|
|
560
|
-
this.sendTransactionStatus(iframe, "
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
693
|
+
this.sendTransactionStatus(iframe, "pending");
|
|
694
|
+
const maxAttempts = 120;
|
|
695
|
+
const pollIntervalMs = 1500;
|
|
696
|
+
let lastStatus = "pending";
|
|
697
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
698
|
+
try {
|
|
699
|
+
const statusResponse = await fetch(
|
|
700
|
+
`${this.config.providerUrl}/api/intent/status/${executeResponse.intentId}`,
|
|
701
|
+
{
|
|
702
|
+
method: "GET",
|
|
703
|
+
headers: this.config.clientId ? { "x-client-id": this.config.clientId } : {}
|
|
704
|
+
}
|
|
705
|
+
);
|
|
706
|
+
if (statusResponse.ok) {
|
|
707
|
+
const statusResult = await statusResponse.json();
|
|
708
|
+
finalStatus = statusResult.status;
|
|
709
|
+
finalTxHash = statusResult.transactionHash;
|
|
710
|
+
if (finalStatus !== lastStatus) {
|
|
711
|
+
this.sendTransactionStatus(iframe, finalStatus, finalTxHash);
|
|
712
|
+
lastStatus = finalStatus;
|
|
713
|
+
}
|
|
714
|
+
const closeOn2 = options.closeOn || "preconfirmed";
|
|
715
|
+
const successStatuses2 = {
|
|
716
|
+
claimed: ["claimed", "preconfirmed", "filled", "completed"],
|
|
717
|
+
preconfirmed: ["preconfirmed", "filled", "completed"],
|
|
718
|
+
filled: ["filled", "completed"],
|
|
719
|
+
completed: ["completed"]
|
|
720
|
+
};
|
|
721
|
+
const isTerminal = finalStatus === "failed" || finalStatus === "expired";
|
|
722
|
+
const isSuccess = successStatuses2[closeOn2]?.includes(finalStatus) ?? false;
|
|
723
|
+
if (isTerminal || isSuccess) {
|
|
724
|
+
break;
|
|
567
725
|
}
|
|
568
726
|
}
|
|
569
|
-
)
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
finalTxHash = waitResult.transactionHash;
|
|
574
|
-
} else {
|
|
575
|
-
console.error("Wait endpoint failed:", await waitResponse.text());
|
|
576
|
-
}
|
|
577
|
-
} catch (waitError) {
|
|
578
|
-
console.error("Failed to wait for intent:", waitError);
|
|
727
|
+
} catch (pollError) {
|
|
728
|
+
console.error("Failed to poll intent status:", pollError);
|
|
729
|
+
}
|
|
730
|
+
await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
|
|
579
731
|
}
|
|
580
732
|
}
|
|
581
|
-
const
|
|
733
|
+
const closeOn = options.closeOn || "preconfirmed";
|
|
734
|
+
const successStatuses = {
|
|
735
|
+
claimed: ["claimed", "preconfirmed", "filled", "completed"],
|
|
736
|
+
preconfirmed: ["preconfirmed", "filled", "completed"],
|
|
737
|
+
filled: ["filled", "completed"],
|
|
738
|
+
completed: ["completed"]
|
|
739
|
+
};
|
|
740
|
+
const isSuccessStatus = successStatuses[closeOn]?.includes(finalStatus) ?? false;
|
|
741
|
+
const displayStatus = isSuccessStatus ? "confirmed" : finalStatus;
|
|
582
742
|
this.sendTransactionStatus(iframe, displayStatus, finalTxHash);
|
|
583
743
|
await this.waitForDialogClose(dialog, cleanup);
|
|
584
744
|
if (options.waitForHash && !finalTxHash) {
|
|
585
|
-
const hash = await this.waitForTransactionHash(
|
|
745
|
+
const hash = await this.waitForTransactionHash(executeResponse.intentId, {
|
|
586
746
|
timeoutMs: options.hashTimeoutMs,
|
|
587
747
|
intervalMs: options.hashIntervalMs
|
|
588
748
|
});
|
|
@@ -593,7 +753,7 @@ var PasskeyProviderClient = class {
|
|
|
593
753
|
finalStatus = "failed";
|
|
594
754
|
return {
|
|
595
755
|
success: false,
|
|
596
|
-
intentId:
|
|
756
|
+
intentId: executeResponse.intentId,
|
|
597
757
|
status: finalStatus,
|
|
598
758
|
transactionHash: finalTxHash,
|
|
599
759
|
operationId: executeResponse.operationId,
|
|
@@ -605,14 +765,204 @@ var PasskeyProviderClient = class {
|
|
|
605
765
|
}
|
|
606
766
|
}
|
|
607
767
|
return {
|
|
608
|
-
success:
|
|
609
|
-
intentId:
|
|
768
|
+
success: isSuccessStatus,
|
|
769
|
+
intentId: executeResponse.intentId,
|
|
610
770
|
status: finalStatus,
|
|
611
771
|
transactionHash: finalTxHash,
|
|
612
772
|
operationId: executeResponse.operationId,
|
|
613
773
|
error: executeResponse.error
|
|
614
774
|
};
|
|
615
775
|
}
|
|
776
|
+
/**
|
|
777
|
+
* Send a batch of intents for multi-chain execution with a single passkey tap.
|
|
778
|
+
*
|
|
779
|
+
* This method prepares multiple intents, shows a paginated review,
|
|
780
|
+
* and signs all intents with a single passkey tap via a shared merkle tree.
|
|
781
|
+
*
|
|
782
|
+
* @example
|
|
783
|
+
* ```typescript
|
|
784
|
+
* const result = await client.sendBatchIntent({
|
|
785
|
+
* username: 'alice',
|
|
786
|
+
* intents: [
|
|
787
|
+
* {
|
|
788
|
+
* targetChain: 8453, // Base
|
|
789
|
+
* calls: [{ to: '0x...', data: '0x...', label: 'Swap on Base' }],
|
|
790
|
+
* },
|
|
791
|
+
* {
|
|
792
|
+
* targetChain: 42161, // Arbitrum
|
|
793
|
+
* calls: [{ to: '0x...', data: '0x...', label: 'Mint on Arbitrum' }],
|
|
794
|
+
* },
|
|
795
|
+
* ],
|
|
796
|
+
* });
|
|
797
|
+
*
|
|
798
|
+
* if (result.success) {
|
|
799
|
+
* console.log(`${result.successCount} intents submitted`);
|
|
800
|
+
* }
|
|
801
|
+
* ```
|
|
802
|
+
*/
|
|
803
|
+
async sendBatchIntent(options) {
|
|
804
|
+
if (!options.username) {
|
|
805
|
+
return {
|
|
806
|
+
success: false,
|
|
807
|
+
results: [],
|
|
808
|
+
successCount: 0,
|
|
809
|
+
failureCount: 0
|
|
810
|
+
};
|
|
811
|
+
}
|
|
812
|
+
if (!options.intents?.length) {
|
|
813
|
+
return {
|
|
814
|
+
success: false,
|
|
815
|
+
results: [],
|
|
816
|
+
successCount: 0,
|
|
817
|
+
failureCount: 0
|
|
818
|
+
};
|
|
819
|
+
}
|
|
820
|
+
const serializedIntents = options.intents.map((intent) => ({
|
|
821
|
+
targetChain: intent.targetChain,
|
|
822
|
+
calls: intent.calls,
|
|
823
|
+
tokenRequests: intent.tokenRequests?.map((r) => ({
|
|
824
|
+
token: r.token,
|
|
825
|
+
amount: r.amount.toString()
|
|
826
|
+
})),
|
|
827
|
+
sourceAssets: intent.sourceAssets,
|
|
828
|
+
sourceChainId: intent.sourceChainId
|
|
829
|
+
}));
|
|
830
|
+
const requestBody = {
|
|
831
|
+
username: options.username,
|
|
832
|
+
intents: serializedIntents,
|
|
833
|
+
...this.config.clientId && { clientId: this.config.clientId }
|
|
834
|
+
};
|
|
835
|
+
let prepareResponse;
|
|
836
|
+
try {
|
|
837
|
+
const response = await fetch(`${this.config.providerUrl}/api/intent/batch-prepare`, {
|
|
838
|
+
method: "POST",
|
|
839
|
+
headers: { "Content-Type": "application/json" },
|
|
840
|
+
body: JSON.stringify(requestBody)
|
|
841
|
+
});
|
|
842
|
+
if (!response.ok) {
|
|
843
|
+
const errorData = await response.json().catch(() => ({}));
|
|
844
|
+
const errorMessage = errorData.error || "Failed to prepare batch intent";
|
|
845
|
+
if (errorMessage.includes("User not found")) {
|
|
846
|
+
localStorage.removeItem("1auth-user");
|
|
847
|
+
}
|
|
848
|
+
return {
|
|
849
|
+
success: false,
|
|
850
|
+
results: [],
|
|
851
|
+
successCount: 0,
|
|
852
|
+
failureCount: 0
|
|
853
|
+
};
|
|
854
|
+
}
|
|
855
|
+
prepareResponse = await response.json();
|
|
856
|
+
} catch {
|
|
857
|
+
return {
|
|
858
|
+
success: false,
|
|
859
|
+
results: [],
|
|
860
|
+
successCount: 0,
|
|
861
|
+
failureCount: 0
|
|
862
|
+
};
|
|
863
|
+
}
|
|
864
|
+
const dialogUrl = this.getDialogUrl();
|
|
865
|
+
const themeParams = this.getThemeParams();
|
|
866
|
+
const signingUrl = `${dialogUrl}/dialog/sign?mode=iframe${themeParams ? `&${themeParams}` : ""}`;
|
|
867
|
+
const { dialog, iframe, cleanup } = this.createModalDialog(signingUrl);
|
|
868
|
+
const dialogOrigin = this.getDialogOrigin();
|
|
869
|
+
const ready = await this.waitForDialogReady(dialog, iframe, cleanup, {
|
|
870
|
+
mode: "iframe",
|
|
871
|
+
batchMode: true,
|
|
872
|
+
batchIntents: prepareResponse.intents,
|
|
873
|
+
challenge: prepareResponse.challenge,
|
|
874
|
+
username: options.username,
|
|
875
|
+
accountAddress: prepareResponse.accountAddress,
|
|
876
|
+
userId: prepareResponse.userId,
|
|
877
|
+
expiresAt: prepareResponse.expiresAt
|
|
878
|
+
});
|
|
879
|
+
if (!ready) {
|
|
880
|
+
return {
|
|
881
|
+
success: false,
|
|
882
|
+
results: [],
|
|
883
|
+
successCount: 0,
|
|
884
|
+
failureCount: 0
|
|
885
|
+
};
|
|
886
|
+
}
|
|
887
|
+
const batchResult = await new Promise((resolve) => {
|
|
888
|
+
const handleMessage = async (event) => {
|
|
889
|
+
if (event.origin !== dialogOrigin) return;
|
|
890
|
+
const message = event.data;
|
|
891
|
+
if (message?.type === "PASSKEY_REFRESH_QUOTE") {
|
|
892
|
+
console.log("[SDK] Batch dialog requested quote refresh, re-preparing all intents");
|
|
893
|
+
try {
|
|
894
|
+
const refreshResponse = await fetch(`${this.config.providerUrl}/api/intent/batch-prepare`, {
|
|
895
|
+
method: "POST",
|
|
896
|
+
headers: { "Content-Type": "application/json" },
|
|
897
|
+
body: JSON.stringify(requestBody)
|
|
898
|
+
});
|
|
899
|
+
if (refreshResponse.ok) {
|
|
900
|
+
const refreshed = await refreshResponse.json();
|
|
901
|
+
prepareResponse = refreshed;
|
|
902
|
+
iframe.contentWindow?.postMessage({
|
|
903
|
+
type: "PASSKEY_REFRESH_COMPLETE",
|
|
904
|
+
batchIntents: refreshed.intents,
|
|
905
|
+
challenge: refreshed.challenge,
|
|
906
|
+
expiresAt: refreshed.expiresAt
|
|
907
|
+
}, dialogOrigin);
|
|
908
|
+
} else {
|
|
909
|
+
iframe.contentWindow?.postMessage({
|
|
910
|
+
type: "PASSKEY_REFRESH_ERROR",
|
|
911
|
+
error: "Failed to refresh batch quotes"
|
|
912
|
+
}, dialogOrigin);
|
|
913
|
+
}
|
|
914
|
+
} catch {
|
|
915
|
+
iframe.contentWindow?.postMessage({
|
|
916
|
+
type: "PASSKEY_REFRESH_ERROR",
|
|
917
|
+
error: "Failed to refresh batch quotes"
|
|
918
|
+
}, dialogOrigin);
|
|
919
|
+
}
|
|
920
|
+
return;
|
|
921
|
+
}
|
|
922
|
+
if (message?.type === "PASSKEY_SIGNING_RESULT") {
|
|
923
|
+
window.removeEventListener("message", handleMessage);
|
|
924
|
+
if (message.success && message.data?.batchResults) {
|
|
925
|
+
const rawResults = message.data.batchResults;
|
|
926
|
+
const results = rawResults.map((r) => ({
|
|
927
|
+
index: r.index,
|
|
928
|
+
success: r.success ?? r.status !== "FAILED",
|
|
929
|
+
intentId: r.intentId || r.operationId || "",
|
|
930
|
+
status: r.status === "FAILED" ? "failed" : "pending",
|
|
931
|
+
error: r.error ? { code: "EXECUTE_FAILED", message: r.error } : void 0
|
|
932
|
+
}));
|
|
933
|
+
const successCount = results.filter((r) => r.success).length;
|
|
934
|
+
await this.waitForDialogClose(dialog, cleanup);
|
|
935
|
+
resolve({
|
|
936
|
+
success: successCount === results.length,
|
|
937
|
+
results,
|
|
938
|
+
successCount,
|
|
939
|
+
failureCount: results.length - successCount
|
|
940
|
+
});
|
|
941
|
+
} else {
|
|
942
|
+
cleanup();
|
|
943
|
+
resolve({
|
|
944
|
+
success: false,
|
|
945
|
+
results: [],
|
|
946
|
+
successCount: 0,
|
|
947
|
+
failureCount: 0
|
|
948
|
+
});
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
if (message?.type === "PASSKEY_CLOSE") {
|
|
952
|
+
window.removeEventListener("message", handleMessage);
|
|
953
|
+
cleanup();
|
|
954
|
+
resolve({
|
|
955
|
+
success: false,
|
|
956
|
+
results: [],
|
|
957
|
+
successCount: 0,
|
|
958
|
+
failureCount: 0
|
|
959
|
+
});
|
|
960
|
+
}
|
|
961
|
+
};
|
|
962
|
+
window.addEventListener("message", handleMessage);
|
|
963
|
+
});
|
|
964
|
+
return batchResult;
|
|
965
|
+
}
|
|
616
966
|
/**
|
|
617
967
|
* Send transaction status to the dialog iframe
|
|
618
968
|
*/
|
|
@@ -689,7 +1039,77 @@ var PasskeyProviderClient = class {
|
|
|
689
1039
|
const payload = message?.data;
|
|
690
1040
|
if (message?.type === "PASSKEY_SIGNING_RESULT") {
|
|
691
1041
|
window.removeEventListener("message", handleMessage);
|
|
692
|
-
if (message.success && payload?.
|
|
1042
|
+
if (message.success && payload?.intentId) {
|
|
1043
|
+
resolve({
|
|
1044
|
+
success: true,
|
|
1045
|
+
intentId: payload.intentId
|
|
1046
|
+
});
|
|
1047
|
+
} else if (message.success && payload?.signature) {
|
|
1048
|
+
resolve({
|
|
1049
|
+
success: true,
|
|
1050
|
+
signature: payload.signature,
|
|
1051
|
+
passkey: payload.passkey,
|
|
1052
|
+
signedHash: payload.signedHash
|
|
1053
|
+
});
|
|
1054
|
+
} else {
|
|
1055
|
+
resolve({
|
|
1056
|
+
success: false,
|
|
1057
|
+
error: message.error || {
|
|
1058
|
+
code: "SIGNING_FAILED",
|
|
1059
|
+
message: "Signing failed"
|
|
1060
|
+
}
|
|
1061
|
+
});
|
|
1062
|
+
}
|
|
1063
|
+
} else if (message?.type === "PASSKEY_CLOSE") {
|
|
1064
|
+
window.removeEventListener("message", handleMessage);
|
|
1065
|
+
cleanup();
|
|
1066
|
+
resolve({
|
|
1067
|
+
success: false,
|
|
1068
|
+
error: {
|
|
1069
|
+
code: "USER_REJECTED",
|
|
1070
|
+
message: "User closed the dialog"
|
|
1071
|
+
}
|
|
1072
|
+
});
|
|
1073
|
+
}
|
|
1074
|
+
};
|
|
1075
|
+
window.addEventListener("message", handleMessage);
|
|
1076
|
+
});
|
|
1077
|
+
}
|
|
1078
|
+
/**
|
|
1079
|
+
* Wait for signing result with auto-refresh support
|
|
1080
|
+
* This method handles both signing results and quote refresh requests from the dialog
|
|
1081
|
+
*/
|
|
1082
|
+
waitForSigningWithRefresh(dialog, iframe, cleanup, dialogOrigin, onRefresh) {
|
|
1083
|
+
console.log("[SDK] waitForSigningWithRefresh, expecting origin:", dialogOrigin);
|
|
1084
|
+
return new Promise((resolve) => {
|
|
1085
|
+
const handleMessage = async (event) => {
|
|
1086
|
+
if (event.origin !== dialogOrigin) return;
|
|
1087
|
+
const message = event.data;
|
|
1088
|
+
if (message?.type === "PASSKEY_REFRESH_QUOTE") {
|
|
1089
|
+
console.log("[SDK] Received quote refresh request from dialog");
|
|
1090
|
+
const refreshedData = await onRefresh();
|
|
1091
|
+
if (refreshedData) {
|
|
1092
|
+
iframe.contentWindow?.postMessage({
|
|
1093
|
+
type: "PASSKEY_REFRESH_COMPLETE",
|
|
1094
|
+
...refreshedData
|
|
1095
|
+
}, dialogOrigin);
|
|
1096
|
+
} else {
|
|
1097
|
+
iframe.contentWindow?.postMessage({
|
|
1098
|
+
type: "PASSKEY_REFRESH_ERROR",
|
|
1099
|
+
error: "Failed to refresh quote"
|
|
1100
|
+
}, dialogOrigin);
|
|
1101
|
+
}
|
|
1102
|
+
return;
|
|
1103
|
+
}
|
|
1104
|
+
const payload = message?.data;
|
|
1105
|
+
if (message?.type === "PASSKEY_SIGNING_RESULT") {
|
|
1106
|
+
window.removeEventListener("message", handleMessage);
|
|
1107
|
+
if (message.success && payload?.intentId) {
|
|
1108
|
+
resolve({
|
|
1109
|
+
success: true,
|
|
1110
|
+
intentId: payload.intentId
|
|
1111
|
+
});
|
|
1112
|
+
} else if (message.success && payload?.signature) {
|
|
693
1113
|
resolve({
|
|
694
1114
|
success: true,
|
|
695
1115
|
signature: payload.signature,
|
|
@@ -755,9 +1175,7 @@ var PasskeyProviderClient = class {
|
|
|
755
1175
|
const response = await fetch(
|
|
756
1176
|
`${this.config.providerUrl}/api/intent/status/${intentId}`,
|
|
757
1177
|
{
|
|
758
|
-
headers: {
|
|
759
|
-
"x-client-id": this.config.clientId
|
|
760
|
-
}
|
|
1178
|
+
headers: this.config.clientId ? { "x-client-id": this.config.clientId } : {}
|
|
761
1179
|
}
|
|
762
1180
|
);
|
|
763
1181
|
if (!response.ok) {
|
|
@@ -792,6 +1210,43 @@ var PasskeyProviderClient = class {
|
|
|
792
1210
|
};
|
|
793
1211
|
}
|
|
794
1212
|
}
|
|
1213
|
+
/**
|
|
1214
|
+
* Get the history of intents for the authenticated user.
|
|
1215
|
+
*
|
|
1216
|
+
* Requires an active session (user must be logged in).
|
|
1217
|
+
*
|
|
1218
|
+
* @example
|
|
1219
|
+
* ```typescript
|
|
1220
|
+
* // Get recent intents
|
|
1221
|
+
* const history = await client.getIntentHistory({ limit: 10 });
|
|
1222
|
+
*
|
|
1223
|
+
* // Filter by status
|
|
1224
|
+
* const pending = await client.getIntentHistory({ status: 'pending' });
|
|
1225
|
+
*
|
|
1226
|
+
* // Filter by date range
|
|
1227
|
+
* const lastWeek = await client.getIntentHistory({
|
|
1228
|
+
* from: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString(),
|
|
1229
|
+
* });
|
|
1230
|
+
* ```
|
|
1231
|
+
*/
|
|
1232
|
+
async getIntentHistory(options) {
|
|
1233
|
+
const queryParams = new URLSearchParams();
|
|
1234
|
+
if (options?.limit) queryParams.set("limit", String(options.limit));
|
|
1235
|
+
if (options?.offset) queryParams.set("offset", String(options.offset));
|
|
1236
|
+
if (options?.status) queryParams.set("status", options.status);
|
|
1237
|
+
if (options?.from) queryParams.set("from", options.from);
|
|
1238
|
+
if (options?.to) queryParams.set("to", options.to);
|
|
1239
|
+
const url = `${this.config.providerUrl}/api/intent/history${queryParams.toString() ? `?${queryParams}` : ""}`;
|
|
1240
|
+
const response = await fetch(url, {
|
|
1241
|
+
headers: this.config.clientId ? { "x-client-id": this.config.clientId } : {},
|
|
1242
|
+
credentials: "include"
|
|
1243
|
+
});
|
|
1244
|
+
if (!response.ok) {
|
|
1245
|
+
const errorData = await response.json().catch(() => ({}));
|
|
1246
|
+
throw new Error(errorData.error || "Failed to get intent history");
|
|
1247
|
+
}
|
|
1248
|
+
return response.json();
|
|
1249
|
+
}
|
|
795
1250
|
/**
|
|
796
1251
|
* Send a swap intent through the Rhinestone orchestrator
|
|
797
1252
|
*
|
|
@@ -854,17 +1309,21 @@ var PasskeyProviderClient = class {
|
|
|
854
1309
|
};
|
|
855
1310
|
}
|
|
856
1311
|
};
|
|
857
|
-
|
|
858
|
-
if (
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
1312
|
+
let fromTokenAddress;
|
|
1313
|
+
if (options.fromToken) {
|
|
1314
|
+
const fromTokenResult = resolveToken(options.fromToken, "fromToken");
|
|
1315
|
+
if (!fromTokenResult.address) {
|
|
1316
|
+
return {
|
|
1317
|
+
success: false,
|
|
1318
|
+
intentId: "",
|
|
1319
|
+
status: "failed",
|
|
1320
|
+
error: {
|
|
1321
|
+
code: "INVALID_TOKEN",
|
|
1322
|
+
message: fromTokenResult.error || `Unknown fromToken: ${options.fromToken}`
|
|
1323
|
+
}
|
|
1324
|
+
};
|
|
1325
|
+
}
|
|
1326
|
+
fromTokenAddress = fromTokenResult.address;
|
|
868
1327
|
}
|
|
869
1328
|
const toTokenResult = resolveToken(options.toToken, "toToken");
|
|
870
1329
|
if (!toTokenResult.address) {
|
|
@@ -878,18 +1337,17 @@ var PasskeyProviderClient = class {
|
|
|
878
1337
|
}
|
|
879
1338
|
};
|
|
880
1339
|
}
|
|
881
|
-
const fromTokenAddress = fromTokenResult.address;
|
|
882
1340
|
const toTokenAddress = toTokenResult.address;
|
|
883
1341
|
console.log("[SDK sendSwap] Token resolution:", {
|
|
884
|
-
fromToken: options.fromToken,
|
|
885
|
-
fromTokenAddress,
|
|
1342
|
+
fromToken: options.fromToken ?? "Any",
|
|
1343
|
+
fromTokenAddress: fromTokenAddress ?? "orchestrator picks",
|
|
886
1344
|
toToken: options.toToken,
|
|
887
1345
|
toTokenAddress,
|
|
888
1346
|
targetChain: options.targetChain
|
|
889
1347
|
});
|
|
890
1348
|
const formatTokenLabel = (token, fallback) => {
|
|
891
1349
|
if (!token.startsWith("0x")) {
|
|
892
|
-
return token
|
|
1350
|
+
return token;
|
|
893
1351
|
}
|
|
894
1352
|
try {
|
|
895
1353
|
return getTokenSymbol(token, options.targetChain);
|
|
@@ -897,15 +1355,11 @@ var PasskeyProviderClient = class {
|
|
|
897
1355
|
return fallback;
|
|
898
1356
|
}
|
|
899
1357
|
};
|
|
900
|
-
const fromSymbol = formatTokenLabel(
|
|
901
|
-
options.fromToken,
|
|
902
|
-
`${options.fromToken.slice(0, 6)}...${options.fromToken.slice(-4)}`
|
|
903
|
-
);
|
|
904
1358
|
const toSymbol = formatTokenLabel(
|
|
905
1359
|
options.toToken,
|
|
906
1360
|
`${options.toToken.slice(0, 6)}...${options.toToken.slice(-4)}`
|
|
907
1361
|
);
|
|
908
|
-
const isFromNativeEth = fromTokenAddress === "0x0000000000000000000000000000000000000000";
|
|
1362
|
+
const isFromNativeEth = fromTokenAddress ? fromTokenAddress === "0x0000000000000000000000000000000000000000" : false;
|
|
909
1363
|
const isToNativeEth = toTokenAddress === "0x0000000000000000000000000000000000000000";
|
|
910
1364
|
const KNOWN_DECIMALS = {
|
|
911
1365
|
ETH: 18,
|
|
@@ -915,31 +1369,33 @@ var PasskeyProviderClient = class {
|
|
|
915
1369
|
USDT0: 6
|
|
916
1370
|
};
|
|
917
1371
|
const getDecimals = (symbol, chainId) => {
|
|
918
|
-
const upperSymbol = symbol.toUpperCase();
|
|
919
1372
|
try {
|
|
920
|
-
const
|
|
921
|
-
|
|
1373
|
+
const match = getSupportedTokens(chainId).find(
|
|
1374
|
+
(t) => t.symbol.toUpperCase() === symbol.toUpperCase()
|
|
1375
|
+
);
|
|
1376
|
+
if (match) {
|
|
1377
|
+
console.log(`[SDK] getTokenDecimals(${match.symbol}, ${chainId}) = ${match.decimals}`);
|
|
1378
|
+
return match.decimals;
|
|
1379
|
+
}
|
|
1380
|
+
const decimals = (0, import_sdk.getTokenDecimals)(symbol, chainId);
|
|
1381
|
+
console.log(`[SDK] getTokenDecimals(${symbol}, ${chainId}) = ${decimals}`);
|
|
922
1382
|
return decimals;
|
|
923
1383
|
} catch (e) {
|
|
924
|
-
|
|
1384
|
+
const upperSymbol = symbol.toUpperCase();
|
|
1385
|
+
console.warn(`[SDK] getTokenDecimals failed for ${symbol}, using fallback`, e);
|
|
925
1386
|
return KNOWN_DECIMALS[upperSymbol] ?? 18;
|
|
926
1387
|
}
|
|
927
1388
|
};
|
|
928
|
-
const fromDecimals = getDecimals(options.fromToken, options.targetChain);
|
|
929
1389
|
const toDecimals = getDecimals(options.toToken, options.targetChain);
|
|
930
|
-
const isBridge = options.fromToken.toUpperCase() === options.toToken.toUpperCase();
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
amount: (0, import_viem2.parseUnits)(options.amount, toDecimals).toString()
|
|
936
|
-
}];
|
|
937
|
-
}
|
|
1390
|
+
const isBridge = options.fromToken ? options.fromToken.toUpperCase() === options.toToken.toUpperCase() : false;
|
|
1391
|
+
const tokenRequests = [{
|
|
1392
|
+
token: toTokenAddress,
|
|
1393
|
+
amount: (0, import_viem2.parseUnits)(options.amount, toDecimals)
|
|
1394
|
+
}];
|
|
938
1395
|
console.log("[SDK sendSwap] Building intent:", {
|
|
939
1396
|
isBridge,
|
|
940
1397
|
isFromNativeEth,
|
|
941
1398
|
isToNativeEth,
|
|
942
|
-
fromDecimals,
|
|
943
1399
|
toDecimals,
|
|
944
1400
|
tokenRequests
|
|
945
1401
|
});
|
|
@@ -950,15 +1406,20 @@ var PasskeyProviderClient = class {
|
|
|
950
1406
|
{
|
|
951
1407
|
// Minimal call - just signals to orchestrator we want the tokenRequests delivered
|
|
952
1408
|
to: toTokenAddress,
|
|
953
|
-
value: "0"
|
|
1409
|
+
value: "0",
|
|
1410
|
+
// SDK provides labels so dialog shows "Buy ETH" not "Send ETH / To: 0x000..."
|
|
1411
|
+
label: `Buy ${toSymbol}`,
|
|
1412
|
+
sublabel: `${options.amount} ${toSymbol}`
|
|
954
1413
|
}
|
|
955
1414
|
],
|
|
956
1415
|
// Request specific output tokens - this is what actually matters for swaps
|
|
957
1416
|
tokenRequests,
|
|
958
1417
|
// Constrain orchestrator to use only the fromToken as input
|
|
959
1418
|
// This ensures the swap uses the correct source token
|
|
960
|
-
//
|
|
961
|
-
sourceAssets: options.sourceAssets || [options.fromToken
|
|
1419
|
+
// Use canonical symbol casing from registry (e.g. "MockUSD" not "MOCKUSD")
|
|
1420
|
+
sourceAssets: options.sourceAssets || (options.fromToken ? [options.fromToken] : void 0),
|
|
1421
|
+
// Pass source chain ID so orchestrator knows which chain to look for tokens on
|
|
1422
|
+
sourceChainId: options.sourceChainId,
|
|
962
1423
|
closeOn: options.closeOn || "preconfirmed",
|
|
963
1424
|
waitForHash: options.waitForHash,
|
|
964
1425
|
hashTimeoutMs: options.hashTimeoutMs,
|
|
@@ -967,7 +1428,7 @@ var PasskeyProviderClient = class {
|
|
|
967
1428
|
return {
|
|
968
1429
|
...result,
|
|
969
1430
|
quote: result.success ? {
|
|
970
|
-
fromToken: fromTokenAddress,
|
|
1431
|
+
fromToken: fromTokenAddress ?? options.fromToken,
|
|
971
1432
|
toToken: toTokenAddress,
|
|
972
1433
|
amountIn: options.amount,
|
|
973
1434
|
amountOut: "",
|
|
@@ -1009,26 +1470,23 @@ var PasskeyProviderClient = class {
|
|
|
1009
1470
|
const themeParams = this.getThemeParams(options?.theme);
|
|
1010
1471
|
const signingUrl = `${dialogUrl}/dialog/sign?mode=iframe${themeParams ? `&${themeParams}` : ""}`;
|
|
1011
1472
|
const { dialog, iframe, cleanup } = this.createModalDialog(signingUrl);
|
|
1012
|
-
const
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
}, dialogOrigin);
|
|
1027
|
-
resolve();
|
|
1473
|
+
const ready = await this.waitForDialogReady(dialog, iframe, cleanup, {
|
|
1474
|
+
mode: "iframe",
|
|
1475
|
+
message: options.message,
|
|
1476
|
+
challenge: options.challenge || options.message,
|
|
1477
|
+
username: options.username,
|
|
1478
|
+
description: options.description,
|
|
1479
|
+
metadata: options.metadata
|
|
1480
|
+
});
|
|
1481
|
+
if (!ready) {
|
|
1482
|
+
return {
|
|
1483
|
+
success: false,
|
|
1484
|
+
error: {
|
|
1485
|
+
code: "USER_REJECTED",
|
|
1486
|
+
message: "User closed the dialog"
|
|
1028
1487
|
}
|
|
1029
1488
|
};
|
|
1030
|
-
|
|
1031
|
-
});
|
|
1489
|
+
}
|
|
1032
1490
|
const signingResult = await this.waitForSigningResponse(dialog, iframe, cleanup);
|
|
1033
1491
|
cleanup();
|
|
1034
1492
|
if (signingResult.success) {
|
|
@@ -1097,31 +1555,28 @@ var PasskeyProviderClient = class {
|
|
|
1097
1555
|
const themeParams = this.getThemeParams(options?.theme);
|
|
1098
1556
|
const signingUrl = `${dialogUrl}/dialog/sign?mode=iframe${themeParams ? `&${themeParams}` : ""}`;
|
|
1099
1557
|
const { dialog, iframe, cleanup } = this.createModalDialog(signingUrl);
|
|
1100
|
-
const
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
}, dialogOrigin);
|
|
1120
|
-
resolve();
|
|
1558
|
+
const ready = await this.waitForDialogReady(dialog, iframe, cleanup, {
|
|
1559
|
+
mode: "iframe",
|
|
1560
|
+
signingMode: "typedData",
|
|
1561
|
+
typedData: {
|
|
1562
|
+
domain: options.domain,
|
|
1563
|
+
types: options.types,
|
|
1564
|
+
primaryType: options.primaryType,
|
|
1565
|
+
message: options.message
|
|
1566
|
+
},
|
|
1567
|
+
challenge: signedHash,
|
|
1568
|
+
username: options.username,
|
|
1569
|
+
description: options.description
|
|
1570
|
+
});
|
|
1571
|
+
if (!ready) {
|
|
1572
|
+
return {
|
|
1573
|
+
success: false,
|
|
1574
|
+
error: {
|
|
1575
|
+
code: "USER_REJECTED",
|
|
1576
|
+
message: "User closed the dialog"
|
|
1121
1577
|
}
|
|
1122
1578
|
};
|
|
1123
|
-
|
|
1124
|
-
});
|
|
1579
|
+
}
|
|
1125
1580
|
const signingResult = await this.waitForSigningResponse(dialog, iframe, cleanup);
|
|
1126
1581
|
cleanup();
|
|
1127
1582
|
if (signingResult.success) {
|
|
@@ -1275,9 +1730,7 @@ var PasskeyProviderClient = class {
|
|
|
1275
1730
|
const response = await fetch(
|
|
1276
1731
|
`${this.config.providerUrl}/api/users/${encodeURIComponent(username)}/passkeys`,
|
|
1277
1732
|
{
|
|
1278
|
-
headers: {
|
|
1279
|
-
"x-client-id": this.config.clientId
|
|
1280
|
-
}
|
|
1733
|
+
headers: this.config.clientId ? { "x-client-id": this.config.clientId } : {}
|
|
1281
1734
|
}
|
|
1282
1735
|
);
|
|
1283
1736
|
if (!response.ok) {
|
|
@@ -1296,7 +1749,7 @@ var PasskeyProviderClient = class {
|
|
|
1296
1749
|
"Content-Type": "application/json"
|
|
1297
1750
|
},
|
|
1298
1751
|
body: JSON.stringify({
|
|
1299
|
-
clientId: this.config.clientId,
|
|
1752
|
+
...this.config.clientId && { clientId: this.config.clientId },
|
|
1300
1753
|
username: options.username,
|
|
1301
1754
|
challenge: options.challenge,
|
|
1302
1755
|
description: options.description,
|
|
@@ -1323,7 +1776,45 @@ var PasskeyProviderClient = class {
|
|
|
1323
1776
|
);
|
|
1324
1777
|
}
|
|
1325
1778
|
/**
|
|
1326
|
-
*
|
|
1779
|
+
* Wait for the dialog iframe to signal ready, then send init data.
|
|
1780
|
+
* Also handles early close (X button, escape, backdrop) during the ready phase.
|
|
1781
|
+
* Returns true if dialog is ready, false if it was closed before becoming ready.
|
|
1782
|
+
*/
|
|
1783
|
+
waitForDialogReady(dialog, iframe, cleanup, initMessage) {
|
|
1784
|
+
const dialogOrigin = this.getDialogOrigin();
|
|
1785
|
+
return new Promise((resolve) => {
|
|
1786
|
+
let settled = false;
|
|
1787
|
+
const teardown = () => {
|
|
1788
|
+
if (settled) return;
|
|
1789
|
+
settled = true;
|
|
1790
|
+
window.removeEventListener("message", handleMessage);
|
|
1791
|
+
dialog.removeEventListener("close", handleClose);
|
|
1792
|
+
};
|
|
1793
|
+
const handleMessage = (event) => {
|
|
1794
|
+
if (event.origin !== dialogOrigin) return;
|
|
1795
|
+
if (event.data?.type === "PASSKEY_READY") {
|
|
1796
|
+
teardown();
|
|
1797
|
+
iframe.contentWindow?.postMessage({
|
|
1798
|
+
type: "PASSKEY_INIT",
|
|
1799
|
+
...initMessage
|
|
1800
|
+
}, dialogOrigin);
|
|
1801
|
+
resolve(true);
|
|
1802
|
+
} else if (event.data?.type === "PASSKEY_CLOSE") {
|
|
1803
|
+
teardown();
|
|
1804
|
+
cleanup();
|
|
1805
|
+
resolve(false);
|
|
1806
|
+
}
|
|
1807
|
+
};
|
|
1808
|
+
const handleClose = () => {
|
|
1809
|
+
teardown();
|
|
1810
|
+
resolve(false);
|
|
1811
|
+
};
|
|
1812
|
+
window.addEventListener("message", handleMessage);
|
|
1813
|
+
dialog.addEventListener("close", handleClose);
|
|
1814
|
+
});
|
|
1815
|
+
}
|
|
1816
|
+
/**
|
|
1817
|
+
* Create a modal dialog with an iframe inside.
|
|
1327
1818
|
*/
|
|
1328
1819
|
createModalDialog(url) {
|
|
1329
1820
|
const dialogUrl = this.getDialogUrl();
|
|
@@ -1466,12 +1957,24 @@ var PasskeyProviderClient = class {
|
|
|
1466
1957
|
};
|
|
1467
1958
|
return { dialog, iframe, cleanup };
|
|
1468
1959
|
}
|
|
1469
|
-
waitForModalAuthResponse(_dialog,
|
|
1960
|
+
waitForModalAuthResponse(_dialog, iframe, cleanup) {
|
|
1470
1961
|
const dialogOrigin = this.getDialogOrigin();
|
|
1471
1962
|
return new Promise((resolve) => {
|
|
1963
|
+
let dialogReady = false;
|
|
1472
1964
|
const handleMessage = (event) => {
|
|
1473
1965
|
if (event.origin !== dialogOrigin) return;
|
|
1474
1966
|
const data = event.data;
|
|
1967
|
+
if (data?.type === "PASSKEY_READY") {
|
|
1968
|
+
dialogReady = true;
|
|
1969
|
+
iframe.contentWindow?.postMessage({
|
|
1970
|
+
type: "PASSKEY_INIT",
|
|
1971
|
+
mode: "iframe"
|
|
1972
|
+
}, dialogOrigin);
|
|
1973
|
+
return;
|
|
1974
|
+
}
|
|
1975
|
+
if (!dialogReady && data?.type === "PASSKEY_CLOSE") {
|
|
1976
|
+
return;
|
|
1977
|
+
}
|
|
1475
1978
|
if (data?.type === "PASSKEY_LOGIN_RESULT") {
|
|
1476
1979
|
window.removeEventListener("message", handleMessage);
|
|
1477
1980
|
cleanup();
|
|
@@ -1501,6 +2004,11 @@ var PasskeyProviderClient = class {
|
|
|
1501
2004
|
error: data.error
|
|
1502
2005
|
});
|
|
1503
2006
|
}
|
|
2007
|
+
} else if (data?.type === "PASSKEY_RETRY_POPUP") {
|
|
2008
|
+
window.removeEventListener("message", handleMessage);
|
|
2009
|
+
cleanup();
|
|
2010
|
+
const popupUrl = data.data?.url?.replace("mode=iframe", "mode=popup") || `${this.getDialogUrl()}/dialog/auth?mode=popup${this.config.clientId ? `&clientId=${this.config.clientId}` : ""}`;
|
|
2011
|
+
this.waitForPopupAuthResponse(popupUrl).then(resolve);
|
|
1504
2012
|
} else if (data?.type === "PASSKEY_CLOSE") {
|
|
1505
2013
|
window.removeEventListener("message", handleMessage);
|
|
1506
2014
|
cleanup();
|
|
@@ -1516,6 +2024,77 @@ var PasskeyProviderClient = class {
|
|
|
1516
2024
|
window.addEventListener("message", handleMessage);
|
|
1517
2025
|
});
|
|
1518
2026
|
}
|
|
2027
|
+
/**
|
|
2028
|
+
* Open a popup for auth and wait for the result.
|
|
2029
|
+
* Used when iframe mode fails (e.g., due to password manager interference).
|
|
2030
|
+
*/
|
|
2031
|
+
waitForPopupAuthResponse(url) {
|
|
2032
|
+
const dialogOrigin = this.getDialogOrigin();
|
|
2033
|
+
const popup = this.openPopup(url);
|
|
2034
|
+
return new Promise((resolve) => {
|
|
2035
|
+
const pollTimer = setInterval(() => {
|
|
2036
|
+
if (popup?.closed) {
|
|
2037
|
+
clearInterval(pollTimer);
|
|
2038
|
+
window.removeEventListener("message", handleMessage);
|
|
2039
|
+
resolve({
|
|
2040
|
+
success: false,
|
|
2041
|
+
error: {
|
|
2042
|
+
code: "USER_CANCELLED",
|
|
2043
|
+
message: "Authentication was cancelled"
|
|
2044
|
+
}
|
|
2045
|
+
});
|
|
2046
|
+
}
|
|
2047
|
+
}, 500);
|
|
2048
|
+
const handleMessage = (event) => {
|
|
2049
|
+
if (event.origin !== dialogOrigin) return;
|
|
2050
|
+
const data = event.data;
|
|
2051
|
+
if (data?.type === "PASSKEY_LOGIN_RESULT") {
|
|
2052
|
+
clearInterval(pollTimer);
|
|
2053
|
+
window.removeEventListener("message", handleMessage);
|
|
2054
|
+
popup?.close();
|
|
2055
|
+
if (data.success) {
|
|
2056
|
+
resolve({
|
|
2057
|
+
success: true,
|
|
2058
|
+
username: data.data?.username,
|
|
2059
|
+
user: data.data?.user
|
|
2060
|
+
});
|
|
2061
|
+
} else {
|
|
2062
|
+
resolve({
|
|
2063
|
+
success: false,
|
|
2064
|
+
error: data.error
|
|
2065
|
+
});
|
|
2066
|
+
}
|
|
2067
|
+
} else if (data?.type === "PASSKEY_REGISTER_RESULT") {
|
|
2068
|
+
clearInterval(pollTimer);
|
|
2069
|
+
window.removeEventListener("message", handleMessage);
|
|
2070
|
+
popup?.close();
|
|
2071
|
+
if (data.success) {
|
|
2072
|
+
resolve({
|
|
2073
|
+
success: true,
|
|
2074
|
+
username: data.data?.username
|
|
2075
|
+
});
|
|
2076
|
+
} else {
|
|
2077
|
+
resolve({
|
|
2078
|
+
success: false,
|
|
2079
|
+
error: data.error
|
|
2080
|
+
});
|
|
2081
|
+
}
|
|
2082
|
+
} else if (data?.type === "PASSKEY_CLOSE") {
|
|
2083
|
+
clearInterval(pollTimer);
|
|
2084
|
+
window.removeEventListener("message", handleMessage);
|
|
2085
|
+
popup?.close();
|
|
2086
|
+
resolve({
|
|
2087
|
+
success: false,
|
|
2088
|
+
error: {
|
|
2089
|
+
code: "USER_CANCELLED",
|
|
2090
|
+
message: "Authentication was cancelled"
|
|
2091
|
+
}
|
|
2092
|
+
});
|
|
2093
|
+
}
|
|
2094
|
+
};
|
|
2095
|
+
window.addEventListener("message", handleMessage);
|
|
2096
|
+
});
|
|
2097
|
+
}
|
|
1519
2098
|
waitForAuthenticateResponse(_dialog, _iframe, cleanup) {
|
|
1520
2099
|
const dialogOrigin = this.getDialogOrigin();
|
|
1521
2100
|
return new Promise((resolve) => {
|
|
@@ -1555,6 +2134,44 @@ var PasskeyProviderClient = class {
|
|
|
1555
2134
|
window.addEventListener("message", handleMessage);
|
|
1556
2135
|
});
|
|
1557
2136
|
}
|
|
2137
|
+
waitForConnectResponse(_dialog, _iframe, cleanup) {
|
|
2138
|
+
const dialogOrigin = this.getDialogOrigin();
|
|
2139
|
+
return new Promise((resolve) => {
|
|
2140
|
+
const handleMessage = (event) => {
|
|
2141
|
+
if (event.origin !== dialogOrigin) return;
|
|
2142
|
+
const data = event.data;
|
|
2143
|
+
if (data?.type === "PASSKEY_CONNECT_RESULT") {
|
|
2144
|
+
window.removeEventListener("message", handleMessage);
|
|
2145
|
+
cleanup();
|
|
2146
|
+
if (data.success) {
|
|
2147
|
+
resolve({
|
|
2148
|
+
success: true,
|
|
2149
|
+
username: data.data?.username,
|
|
2150
|
+
autoConnected: data.data?.autoConnected
|
|
2151
|
+
});
|
|
2152
|
+
} else {
|
|
2153
|
+
resolve({
|
|
2154
|
+
success: false,
|
|
2155
|
+
action: data.data?.action,
|
|
2156
|
+
error: data.error
|
|
2157
|
+
});
|
|
2158
|
+
}
|
|
2159
|
+
} else if (data?.type === "PASSKEY_CLOSE") {
|
|
2160
|
+
window.removeEventListener("message", handleMessage);
|
|
2161
|
+
cleanup();
|
|
2162
|
+
resolve({
|
|
2163
|
+
success: false,
|
|
2164
|
+
action: "cancel",
|
|
2165
|
+
error: {
|
|
2166
|
+
code: "USER_CANCELLED",
|
|
2167
|
+
message: "Connection was cancelled"
|
|
2168
|
+
}
|
|
2169
|
+
});
|
|
2170
|
+
}
|
|
2171
|
+
};
|
|
2172
|
+
window.addEventListener("message", handleMessage);
|
|
2173
|
+
});
|
|
2174
|
+
}
|
|
1558
2175
|
waitForModalSigningResponse(requestId, _dialog, _iframe, cleanup) {
|
|
1559
2176
|
const dialogOrigin = this.getDialogOrigin();
|
|
1560
2177
|
return new Promise((resolve) => {
|
|
@@ -1643,9 +2260,7 @@ var PasskeyProviderClient = class {
|
|
|
1643
2260
|
const response = await fetch(
|
|
1644
2261
|
`${this.config.providerUrl}/api/sign/request/${requestId}`,
|
|
1645
2262
|
{
|
|
1646
|
-
headers: {
|
|
1647
|
-
"x-client-id": this.config.clientId
|
|
1648
|
-
}
|
|
2263
|
+
headers: this.config.clientId ? { "x-client-id": this.config.clientId } : {}
|
|
1649
2264
|
}
|
|
1650
2265
|
);
|
|
1651
2266
|
if (!response.ok) {
|
|
@@ -1749,7 +2364,7 @@ function buildTransactionReview(calls) {
|
|
|
1749
2364
|
|
|
1750
2365
|
// src/provider.ts
|
|
1751
2366
|
var DEFAULT_STORAGE_KEY = "1auth-user";
|
|
1752
|
-
function
|
|
2367
|
+
function createOneAuthProvider(options) {
|
|
1753
2368
|
const { client } = options;
|
|
1754
2369
|
let chainId = options.chainId;
|
|
1755
2370
|
const storageKey = options.storageKey || DEFAULT_STORAGE_KEY;
|
|
@@ -1780,12 +2395,11 @@ function createPasskeyProvider(options) {
|
|
|
1780
2395
|
localStorage.removeItem(storageKey);
|
|
1781
2396
|
};
|
|
1782
2397
|
const resolveAccountAddress = async (username) => {
|
|
2398
|
+
const clientId = client.getClientId();
|
|
1783
2399
|
const response = await fetch(
|
|
1784
2400
|
`${client.getProviderUrl()}/api/users/${encodeURIComponent(username)}/account`,
|
|
1785
2401
|
{
|
|
1786
|
-
headers: {
|
|
1787
|
-
"x-client-id": client.getClientId()
|
|
1788
|
-
}
|
|
2402
|
+
headers: clientId ? { "x-client-id": clientId } : {}
|
|
1789
2403
|
}
|
|
1790
2404
|
);
|
|
1791
2405
|
if (!response.ok) {
|
|
@@ -1800,12 +2414,21 @@ function createPasskeyProvider(options) {
|
|
|
1800
2414
|
if (stored) {
|
|
1801
2415
|
return [stored.address];
|
|
1802
2416
|
}
|
|
1803
|
-
const
|
|
1804
|
-
|
|
1805
|
-
|
|
2417
|
+
const connectResult = await client.connectWithModal();
|
|
2418
|
+
let username;
|
|
2419
|
+
if (connectResult.success && connectResult.username) {
|
|
2420
|
+
username = connectResult.username;
|
|
2421
|
+
} else if (connectResult.action === "switch") {
|
|
2422
|
+
const authResult = await client.authWithModal();
|
|
2423
|
+
if (!authResult.success || !authResult.username) {
|
|
2424
|
+
throw new Error(authResult.error?.message || "Authentication failed");
|
|
2425
|
+
}
|
|
2426
|
+
username = authResult.username;
|
|
2427
|
+
} else {
|
|
2428
|
+
throw new Error(connectResult.error?.message || "Connection cancelled");
|
|
1806
2429
|
}
|
|
1807
|
-
const address = await resolveAccountAddress(
|
|
1808
|
-
setStoredUser({ username
|
|
2430
|
+
const address = await resolveAccountAddress(username);
|
|
2431
|
+
setStoredUser({ username, address });
|
|
1809
2432
|
emit("accountsChanged", [address]);
|
|
1810
2433
|
emit("connect", { chainId: (0, import_viem4.numberToHex)(chainId) });
|
|
1811
2434
|
return [address];
|
|
@@ -1862,6 +2485,16 @@ function createPasskeyProvider(options) {
|
|
|
1862
2485
|
};
|
|
1863
2486
|
});
|
|
1864
2487
|
};
|
|
2488
|
+
const normalizeTokenRequests = (requests) => {
|
|
2489
|
+
if (!Array.isArray(requests)) return void 0;
|
|
2490
|
+
return requests.map((r) => {
|
|
2491
|
+
const req = r;
|
|
2492
|
+
return {
|
|
2493
|
+
token: req.token,
|
|
2494
|
+
amount: typeof req.amount === "bigint" ? req.amount : BigInt(String(req.amount || "0"))
|
|
2495
|
+
};
|
|
2496
|
+
});
|
|
2497
|
+
};
|
|
1865
2498
|
const decodeMessage = (value) => {
|
|
1866
2499
|
if (!(0, import_viem4.isHex)(value)) return value;
|
|
1867
2500
|
try {
|
|
@@ -1896,19 +2529,40 @@ function createPasskeyProvider(options) {
|
|
|
1896
2529
|
}
|
|
1897
2530
|
return encodeWebAuthnSignature(result.signature);
|
|
1898
2531
|
};
|
|
2532
|
+
const resolveIntentPayload = async (payload) => {
|
|
2533
|
+
if (!options.signIntent) {
|
|
2534
|
+
return {
|
|
2535
|
+
username: payload.username,
|
|
2536
|
+
targetChain: payload.targetChain,
|
|
2537
|
+
calls: payload.calls,
|
|
2538
|
+
tokenRequests: payload.tokenRequests
|
|
2539
|
+
};
|
|
2540
|
+
}
|
|
2541
|
+
const signedIntent = await options.signIntent({
|
|
2542
|
+
username: payload.username,
|
|
2543
|
+
accountAddress: payload.accountAddress,
|
|
2544
|
+
targetChain: payload.targetChain,
|
|
2545
|
+
calls: payload.calls,
|
|
2546
|
+
tokenRequests: payload.tokenRequests
|
|
2547
|
+
});
|
|
2548
|
+
return { signedIntent };
|
|
2549
|
+
};
|
|
1899
2550
|
const sendIntent = async (payload) => {
|
|
1900
|
-
const closeOn = options.waitForHash ?? true ? "completed" : "preconfirmed";
|
|
2551
|
+
const closeOn = options.closeOn ?? (options.waitForHash ?? true ? "completed" : "preconfirmed");
|
|
2552
|
+
const intentPayload = await resolveIntentPayload(payload);
|
|
1901
2553
|
const result = await client.sendIntent({
|
|
1902
|
-
...
|
|
2554
|
+
...intentPayload,
|
|
2555
|
+
tokenRequests: payload.tokenRequests,
|
|
2556
|
+
sourceChainId: payload.sourceChainId,
|
|
1903
2557
|
closeOn,
|
|
1904
2558
|
waitForHash: options.waitForHash ?? true,
|
|
1905
2559
|
hashTimeoutMs: options.hashTimeoutMs,
|
|
1906
2560
|
hashIntervalMs: options.hashIntervalMs
|
|
1907
2561
|
});
|
|
1908
|
-
if (!result.success
|
|
2562
|
+
if (!result.success) {
|
|
1909
2563
|
throw new Error(result.error?.message || "Transaction failed");
|
|
1910
2564
|
}
|
|
1911
|
-
return result.
|
|
2565
|
+
return result.intentId;
|
|
1912
2566
|
};
|
|
1913
2567
|
const request = async ({ method, params }) => {
|
|
1914
2568
|
switch (method) {
|
|
@@ -1961,10 +2615,15 @@ function createPasskeyProvider(options) {
|
|
|
1961
2615
|
const user = await ensureUser();
|
|
1962
2616
|
const targetChain = parseChainId(tx.chainId) ?? chainId;
|
|
1963
2617
|
const calls = normalizeCalls([tx]);
|
|
2618
|
+
const tokenRequests = normalizeTokenRequests(tx.tokenRequests);
|
|
2619
|
+
const txSourceChainId = parseChainId(tx.sourceChainId);
|
|
1964
2620
|
return sendIntent({
|
|
1965
2621
|
username: user.username,
|
|
2622
|
+
accountAddress: user.address,
|
|
1966
2623
|
targetChain,
|
|
1967
|
-
calls
|
|
2624
|
+
calls,
|
|
2625
|
+
tokenRequests,
|
|
2626
|
+
sourceChainId: txSourceChainId
|
|
1968
2627
|
});
|
|
1969
2628
|
}
|
|
1970
2629
|
case "wallet_sendCalls": {
|
|
@@ -1973,21 +2632,128 @@ function createPasskeyProvider(options) {
|
|
|
1973
2632
|
const user = await ensureUser();
|
|
1974
2633
|
const targetChain = parseChainId(payload.chainId) ?? chainId;
|
|
1975
2634
|
const calls = normalizeCalls(payload.calls || []);
|
|
2635
|
+
const tokenRequests = normalizeTokenRequests(payload.tokenRequests);
|
|
2636
|
+
const sourceChainId = parseChainId(payload.sourceChainId);
|
|
1976
2637
|
if (!calls.length) throw new Error("No calls provided");
|
|
1977
2638
|
return sendIntent({
|
|
1978
2639
|
username: user.username,
|
|
2640
|
+
accountAddress: user.address,
|
|
1979
2641
|
targetChain,
|
|
1980
|
-
calls
|
|
2642
|
+
calls,
|
|
2643
|
+
tokenRequests,
|
|
2644
|
+
sourceChainId
|
|
1981
2645
|
});
|
|
1982
2646
|
}
|
|
1983
2647
|
case "wallet_getCapabilities": {
|
|
2648
|
+
const paramList = Array.isArray(params) ? params : [];
|
|
2649
|
+
const requestedChains = paramList[1];
|
|
1984
2650
|
const chainIds = getSupportedChainIds();
|
|
1985
|
-
const
|
|
1986
|
-
|
|
2651
|
+
const capabilities = {};
|
|
2652
|
+
for (const chainId2 of chainIds) {
|
|
2653
|
+
const hexChainId = `0x${chainId2.toString(16)}`;
|
|
2654
|
+
if (requestedChains && !requestedChains.includes(hexChainId)) {
|
|
2655
|
+
continue;
|
|
2656
|
+
}
|
|
2657
|
+
capabilities[hexChainId] = {
|
|
2658
|
+
atomic: { status: "supported" },
|
|
2659
|
+
paymasterService: { supported: true },
|
|
2660
|
+
auxiliaryFunds: { supported: true }
|
|
2661
|
+
};
|
|
2662
|
+
}
|
|
2663
|
+
return capabilities;
|
|
2664
|
+
}
|
|
2665
|
+
case "wallet_getAssets": {
|
|
2666
|
+
const { username } = await ensureUser();
|
|
2667
|
+
const clientId = client.getClientId();
|
|
2668
|
+
const response = await fetch(
|
|
2669
|
+
`${client.getProviderUrl()}/api/users/${encodeURIComponent(username)}/portfolio`,
|
|
2670
|
+
{
|
|
2671
|
+
headers: clientId ? { "x-client-id": clientId } : {}
|
|
2672
|
+
}
|
|
1987
2673
|
);
|
|
2674
|
+
if (!response.ok) {
|
|
2675
|
+
const data = await response.json().catch(() => ({}));
|
|
2676
|
+
throw new Error(data.error || "Failed to get assets");
|
|
2677
|
+
}
|
|
2678
|
+
return response.json();
|
|
2679
|
+
}
|
|
2680
|
+
case "wallet_getCallsStatus": {
|
|
2681
|
+
const paramList = Array.isArray(params) ? params : [];
|
|
2682
|
+
const callsId = paramList[0];
|
|
2683
|
+
if (!callsId) {
|
|
2684
|
+
throw new Error("callsId is required");
|
|
2685
|
+
}
|
|
2686
|
+
const statusClientId = client.getClientId();
|
|
2687
|
+
const response = await fetch(
|
|
2688
|
+
`${client.getProviderUrl()}/api/intent/status/${encodeURIComponent(callsId)}`,
|
|
2689
|
+
{
|
|
2690
|
+
headers: statusClientId ? { "x-client-id": statusClientId } : {}
|
|
2691
|
+
}
|
|
2692
|
+
);
|
|
2693
|
+
if (!response.ok) {
|
|
2694
|
+
const data2 = await response.json().catch(() => ({}));
|
|
2695
|
+
throw new Error(data2.error || "Failed to get calls status");
|
|
2696
|
+
}
|
|
2697
|
+
const data = await response.json();
|
|
2698
|
+
const statusMap = {
|
|
2699
|
+
pending: "PENDING",
|
|
2700
|
+
preconfirmed: "PENDING",
|
|
2701
|
+
completed: "CONFIRMED",
|
|
2702
|
+
failed: "CONFIRMED",
|
|
2703
|
+
expired: "CONFIRMED"
|
|
2704
|
+
};
|
|
1988
2705
|
return {
|
|
1989
|
-
|
|
1990
|
-
|
|
2706
|
+
status: statusMap[data.status] || "PENDING",
|
|
2707
|
+
receipts: data.transactionHash ? [
|
|
2708
|
+
{
|
|
2709
|
+
logs: [],
|
|
2710
|
+
status: data.status === "completed" ? "0x1" : "0x0",
|
|
2711
|
+
blockHash: data.blockHash,
|
|
2712
|
+
blockNumber: data.blockNumber,
|
|
2713
|
+
transactionHash: data.transactionHash
|
|
2714
|
+
}
|
|
2715
|
+
] : []
|
|
2716
|
+
};
|
|
2717
|
+
}
|
|
2718
|
+
case "wallet_getCallsHistory": {
|
|
2719
|
+
const paramList = Array.isArray(params) ? params : [];
|
|
2720
|
+
const options2 = paramList[0] || {};
|
|
2721
|
+
const queryParams = new URLSearchParams();
|
|
2722
|
+
if (options2.limit) queryParams.set("limit", String(options2.limit));
|
|
2723
|
+
if (options2.offset) queryParams.set("offset", String(options2.offset));
|
|
2724
|
+
if (options2.status) queryParams.set("status", options2.status);
|
|
2725
|
+
if (options2.from) queryParams.set("from", options2.from);
|
|
2726
|
+
if (options2.to) queryParams.set("to", options2.to);
|
|
2727
|
+
const url = `${client.getProviderUrl()}/api/intent/history${queryParams.toString() ? `?${queryParams}` : ""}`;
|
|
2728
|
+
const historyClientId = client.getClientId();
|
|
2729
|
+
const response = await fetch(url, {
|
|
2730
|
+
headers: historyClientId ? { "x-client-id": historyClientId } : {},
|
|
2731
|
+
credentials: "include"
|
|
2732
|
+
});
|
|
2733
|
+
if (!response.ok) {
|
|
2734
|
+
const data2 = await response.json().catch(() => ({}));
|
|
2735
|
+
throw new Error(data2.error || "Failed to get calls history");
|
|
2736
|
+
}
|
|
2737
|
+
const data = await response.json();
|
|
2738
|
+
const statusMap = {
|
|
2739
|
+
pending: "PENDING",
|
|
2740
|
+
preconfirmed: "PENDING",
|
|
2741
|
+
completed: "CONFIRMED",
|
|
2742
|
+
failed: "CONFIRMED",
|
|
2743
|
+
expired: "CONFIRMED"
|
|
2744
|
+
};
|
|
2745
|
+
return {
|
|
2746
|
+
calls: data.intents.map(
|
|
2747
|
+
(intent) => ({
|
|
2748
|
+
callsId: intent.intentId,
|
|
2749
|
+
// intentId is the orchestrator's ID
|
|
2750
|
+
status: statusMap[intent.status] || "PENDING",
|
|
2751
|
+
receipts: intent.transactionHash ? [{ transactionHash: intent.transactionHash }] : [],
|
|
2752
|
+
chainId: `0x${intent.targetChain.toString(16)}`
|
|
2753
|
+
})
|
|
2754
|
+
),
|
|
2755
|
+
total: data.total,
|
|
2756
|
+
hasMore: data.hasMore
|
|
1991
2757
|
};
|
|
1992
2758
|
}
|
|
1993
2759
|
default:
|
|
@@ -2010,6 +2776,7 @@ function createPasskeyProvider(options) {
|
|
|
2010
2776
|
disconnect
|
|
2011
2777
|
};
|
|
2012
2778
|
}
|
|
2779
|
+
var createPasskeyProvider = createOneAuthProvider;
|
|
2013
2780
|
|
|
2014
2781
|
// src/account.ts
|
|
2015
2782
|
var import_viem5 = require("viem");
|
|
@@ -2088,7 +2855,7 @@ function createPasskeyAccount(client, params) {
|
|
|
2088
2855
|
var import_viem6 = require("viem");
|
|
2089
2856
|
var import_accounts2 = require("viem/accounts");
|
|
2090
2857
|
function createPasskeyWalletClient(config) {
|
|
2091
|
-
const provider = new
|
|
2858
|
+
const provider = new OneAuthClient({
|
|
2092
2859
|
providerUrl: config.providerUrl,
|
|
2093
2860
|
clientId: config.clientId,
|
|
2094
2861
|
dialogUrl: config.dialogUrl
|
|
@@ -2112,6 +2879,9 @@ function createPasskeyWalletClient(config) {
|
|
|
2112
2879
|
if (!result.success) {
|
|
2113
2880
|
throw new Error(result.error?.message || "Signing failed");
|
|
2114
2881
|
}
|
|
2882
|
+
if (!result.signature) {
|
|
2883
|
+
throw new Error("No signature received");
|
|
2884
|
+
}
|
|
2115
2885
|
return encodeWebAuthnSignature(result.signature);
|
|
2116
2886
|
};
|
|
2117
2887
|
const signTransactionImpl = async (transaction) => {
|
|
@@ -2132,6 +2902,9 @@ function createPasskeyWalletClient(config) {
|
|
|
2132
2902
|
if (!result.success) {
|
|
2133
2903
|
throw new Error(result.error?.message || "Signing failed");
|
|
2134
2904
|
}
|
|
2905
|
+
if (!result.signature) {
|
|
2906
|
+
throw new Error("No signature received");
|
|
2907
|
+
}
|
|
2135
2908
|
return encodeWebAuthnSignature(result.signature);
|
|
2136
2909
|
};
|
|
2137
2910
|
const signTypedDataImpl = async (typedData) => {
|
|
@@ -2153,8 +2926,35 @@ function createPasskeyWalletClient(config) {
|
|
|
2153
2926
|
if (!result.success) {
|
|
2154
2927
|
throw new Error(result.error?.message || "Signing failed");
|
|
2155
2928
|
}
|
|
2929
|
+
if (!result.signature) {
|
|
2930
|
+
throw new Error("No signature received");
|
|
2931
|
+
}
|
|
2156
2932
|
return encodeWebAuthnSignature(result.signature);
|
|
2157
2933
|
};
|
|
2934
|
+
const buildIntentPayload = async (calls, targetChainOverride) => {
|
|
2935
|
+
const targetChain = targetChainOverride ?? config.chain.id;
|
|
2936
|
+
const intentCalls = calls.map((call) => ({
|
|
2937
|
+
to: call.to,
|
|
2938
|
+
data: call.data || "0x",
|
|
2939
|
+
value: call.value !== void 0 ? call.value.toString() : "0",
|
|
2940
|
+
label: call.label,
|
|
2941
|
+
sublabel: call.sublabel
|
|
2942
|
+
}));
|
|
2943
|
+
if (config.signIntent) {
|
|
2944
|
+
const signedIntent = await config.signIntent({
|
|
2945
|
+
username: config.username,
|
|
2946
|
+
accountAddress: config.accountAddress,
|
|
2947
|
+
targetChain,
|
|
2948
|
+
calls: intentCalls
|
|
2949
|
+
});
|
|
2950
|
+
return { signedIntent };
|
|
2951
|
+
}
|
|
2952
|
+
return {
|
|
2953
|
+
username: config.username,
|
|
2954
|
+
targetChain,
|
|
2955
|
+
calls: intentCalls
|
|
2956
|
+
};
|
|
2957
|
+
};
|
|
2158
2958
|
const account = (0, import_accounts2.toAccount)({
|
|
2159
2959
|
address: config.accountAddress,
|
|
2160
2960
|
signMessage: ({ message }) => signMessageImpl(message),
|
|
@@ -2171,24 +2971,18 @@ function createPasskeyWalletClient(config) {
|
|
|
2171
2971
|
* Send a single transaction via intent flow
|
|
2172
2972
|
*/
|
|
2173
2973
|
async sendTransaction(transaction) {
|
|
2974
|
+
const targetChain = typeof transaction.chainId === "number" ? transaction.chainId : transaction.chain?.id;
|
|
2174
2975
|
const calls = [
|
|
2175
2976
|
{
|
|
2176
2977
|
to: transaction.to,
|
|
2177
|
-
data: transaction.data,
|
|
2978
|
+
data: transaction.data || "0x",
|
|
2178
2979
|
value: transaction.value
|
|
2179
2980
|
}
|
|
2180
2981
|
];
|
|
2181
2982
|
const closeOn = config.waitForHash ?? true ? "completed" : "preconfirmed";
|
|
2983
|
+
const intentPayload = await buildIntentPayload(calls, targetChain);
|
|
2182
2984
|
const result = await provider.sendIntent({
|
|
2183
|
-
|
|
2184
|
-
targetChain: config.chain.id,
|
|
2185
|
-
calls: calls.map((call) => ({
|
|
2186
|
-
to: call.to,
|
|
2187
|
-
data: call.data,
|
|
2188
|
-
value: call.value ? call.value.toString() : "0",
|
|
2189
|
-
label: call.label,
|
|
2190
|
-
sublabel: call.sublabel
|
|
2191
|
-
})),
|
|
2985
|
+
...intentPayload,
|
|
2192
2986
|
closeOn,
|
|
2193
2987
|
waitForHash: config.waitForHash ?? true,
|
|
2194
2988
|
hashTimeoutMs: config.hashTimeoutMs,
|
|
@@ -2203,18 +2997,12 @@ function createPasskeyWalletClient(config) {
|
|
|
2203
2997
|
* Send multiple calls as a single batched transaction
|
|
2204
2998
|
*/
|
|
2205
2999
|
async sendCalls(params) {
|
|
2206
|
-
const { calls } = params;
|
|
3000
|
+
const { calls, chainId: targetChain, tokenRequests } = params;
|
|
2207
3001
|
const closeOn = config.waitForHash ?? true ? "completed" : "preconfirmed";
|
|
3002
|
+
const intentPayload = await buildIntentPayload(calls, targetChain);
|
|
2208
3003
|
const result = await provider.sendIntent({
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
calls: calls.map((call) => ({
|
|
2212
|
-
to: call.to,
|
|
2213
|
-
data: call.data,
|
|
2214
|
-
value: call.value ? call.value.toString() : "0",
|
|
2215
|
-
label: call.label,
|
|
2216
|
-
sublabel: call.sublabel
|
|
2217
|
-
})),
|
|
3004
|
+
...intentPayload,
|
|
3005
|
+
tokenRequests,
|
|
2218
3006
|
closeOn,
|
|
2219
3007
|
waitForHash: config.waitForHash ?? true,
|
|
2220
3008
|
hashTimeoutMs: config.hashTimeoutMs,
|
|
@@ -2658,9 +3446,10 @@ function BatchQueueWidget({ onSignAll }) {
|
|
|
2658
3446
|
|
|
2659
3447
|
// src/verify.ts
|
|
2660
3448
|
var import_viem7 = require("viem");
|
|
2661
|
-
var
|
|
3449
|
+
var ETHEREUM_MESSAGE_PREFIX = "Ethereum Signed Message:\n";
|
|
3450
|
+
var PASSKEY_MESSAGE_PREFIX = ETHEREUM_MESSAGE_PREFIX;
|
|
2662
3451
|
function hashMessage2(message) {
|
|
2663
|
-
const prefixed =
|
|
3452
|
+
const prefixed = ETHEREUM_MESSAGE_PREFIX + message.length.toString() + message;
|
|
2664
3453
|
return (0, import_viem7.keccak256)((0, import_viem7.toBytes)(prefixed));
|
|
2665
3454
|
}
|
|
2666
3455
|
function verifyMessageHash(message, signedHash) {
|
|
@@ -2672,8 +3461,11 @@ function verifyMessageHash(message, signedHash) {
|
|
|
2672
3461
|
0 && (module.exports = {
|
|
2673
3462
|
BatchQueueProvider,
|
|
2674
3463
|
BatchQueueWidget,
|
|
3464
|
+
ETHEREUM_MESSAGE_PREFIX,
|
|
3465
|
+
OneAuthClient,
|
|
2675
3466
|
PASSKEY_MESSAGE_PREFIX,
|
|
2676
3467
|
PasskeyProviderClient,
|
|
3468
|
+
createOneAuthProvider,
|
|
2677
3469
|
createPasskeyAccount,
|
|
2678
3470
|
createPasskeyProvider,
|
|
2679
3471
|
createPasskeyWalletClient,
|