@rhinestone/1auth 0.1.1 → 0.4.0
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-U7KZ4XMQ.mjs → chunk-X73ALCGW.mjs} +202 -36
- package/dist/chunk-X73ALCGW.mjs.map +1 -0
- package/dist/{client-B-HGKKaJ.d.mts → client-DKuPEx83.d.mts} +462 -27
- package/dist/{client-B-HGKKaJ.d.ts → client-DKuPEx83.d.ts} +462 -27
- package/dist/index.d.mts +19 -10
- package/dist/index.d.ts +19 -10
- package/dist/index.js +1417 -282
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1235 -266
- package/dist/index.mjs.map +1 -1
- package/dist/provider-CmJarV7y.d.mts +33 -0
- package/dist/provider-Dj5l4bWn.d.ts +33 -0
- package/dist/react.d.mts +2 -2
- package/dist/react.d.ts +2 -2
- package/dist/react.js +16 -9
- package/dist/react.js.map +1 -1
- package/dist/react.mjs +16 -9
- package/dist/react.mjs.map +1 -1
- package/dist/wagmi.d.mts +3 -3
- package/dist/wagmi.d.ts +3 -3
- package/dist/wagmi.js +193 -37
- package/dist/wagmi.js.map +1 -1
- package/dist/wagmi.mjs +1 -1
- package/package.json +19 -5
- package/dist/chunk-U7KZ4XMQ.mjs.map +0 -1
- package/dist/provider-CDIRlA4y.d.mts +0 -25
- package/dist/provider-Cy1StrOe.d.ts +0 -25
package/dist/index.js
CHANGED
|
@@ -32,9 +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,
|
|
35
36
|
OneAuthClient: () => OneAuthClient,
|
|
36
37
|
PASSKEY_MESSAGE_PREFIX: () => PASSKEY_MESSAGE_PREFIX,
|
|
37
38
|
PasskeyProviderClient: () => OneAuthClient,
|
|
39
|
+
createOneAuthProvider: () => createOneAuthProvider,
|
|
38
40
|
createPasskeyAccount: () => createPasskeyAccount,
|
|
39
41
|
createPasskeyProvider: () => createPasskeyProvider,
|
|
40
42
|
createPasskeyWalletClient: () => createPasskeyWalletClient,
|
|
@@ -69,12 +71,15 @@ var import_viem = require("viem");
|
|
|
69
71
|
var viemChains = __toESM(require("viem/chains"));
|
|
70
72
|
var import_sdk = require("@rhinestone/sdk");
|
|
71
73
|
var env = typeof process !== "undefined" ? process.env : {};
|
|
72
|
-
var
|
|
73
|
-
|
|
74
|
-
);
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
)
|
|
74
|
+
var VIEM_CHAIN_BY_ID = /* @__PURE__ */ new Map();
|
|
75
|
+
for (const value of Object.values(viemChains)) {
|
|
76
|
+
if (typeof value !== "object" || value === null || !("id" in value) || !("name" in value)) continue;
|
|
77
|
+
const chain = value;
|
|
78
|
+
const existing = VIEM_CHAIN_BY_ID.get(chain.id);
|
|
79
|
+
if (!existing || existing.testnet && !chain.testnet) {
|
|
80
|
+
VIEM_CHAIN_BY_ID.set(chain.id, chain);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
78
83
|
var SUPPORTED_CHAIN_IDS = new Set(
|
|
79
84
|
(0, import_sdk.getAllSupportedChainsAndTokens)().map((entry) => entry.chainId)
|
|
80
85
|
);
|
|
@@ -156,7 +161,13 @@ function resolveTokenAddress(token, chainId) {
|
|
|
156
161
|
if ((0, import_viem.isAddress)(token)) {
|
|
157
162
|
return token;
|
|
158
163
|
}
|
|
159
|
-
|
|
164
|
+
const match = getSupportedTokens(chainId).find(
|
|
165
|
+
(t) => t.symbol.toUpperCase() === token.toUpperCase()
|
|
166
|
+
);
|
|
167
|
+
if (!match) {
|
|
168
|
+
return (0, import_sdk.getTokenAddress)(token, chainId);
|
|
169
|
+
}
|
|
170
|
+
return match.address;
|
|
160
171
|
}
|
|
161
172
|
function isTestnet(chainId) {
|
|
162
173
|
try {
|
|
@@ -189,7 +200,7 @@ var POPUP_WIDTH = 450;
|
|
|
189
200
|
var POPUP_HEIGHT = 600;
|
|
190
201
|
var DEFAULT_EMBED_WIDTH = "400px";
|
|
191
202
|
var DEFAULT_EMBED_HEIGHT = "500px";
|
|
192
|
-
var MODAL_WIDTH =
|
|
203
|
+
var MODAL_WIDTH = 340;
|
|
193
204
|
var DEFAULT_PROVIDER_URL = "https://passkey.1auth.box";
|
|
194
205
|
var OneAuthClient = class {
|
|
195
206
|
constructor(config) {
|
|
@@ -197,6 +208,12 @@ var OneAuthClient = class {
|
|
|
197
208
|
const dialogUrl = config.dialogUrl || providerUrl;
|
|
198
209
|
this.config = { ...config, providerUrl, dialogUrl };
|
|
199
210
|
this.theme = this.config.theme || {};
|
|
211
|
+
if (typeof document !== "undefined") {
|
|
212
|
+
this.injectPreconnect(providerUrl);
|
|
213
|
+
if (dialogUrl !== providerUrl) {
|
|
214
|
+
this.injectPreconnect(dialogUrl);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
200
217
|
}
|
|
201
218
|
/**
|
|
202
219
|
* Update the theme configuration at runtime
|
|
@@ -258,9 +275,7 @@ var OneAuthClient = class {
|
|
|
258
275
|
const response = await fetch(
|
|
259
276
|
`${this.config.providerUrl}/api/intent/status/${intentId}`,
|
|
260
277
|
{
|
|
261
|
-
headers: {
|
|
262
|
-
"x-client-id": this.config.clientId
|
|
263
|
-
}
|
|
278
|
+
headers: this.config.clientId ? { "x-client-id": this.config.clientId } : {}
|
|
264
279
|
}
|
|
265
280
|
);
|
|
266
281
|
if (response.ok) {
|
|
@@ -284,9 +299,11 @@ var OneAuthClient = class {
|
|
|
284
299
|
async authWithModal(options) {
|
|
285
300
|
const dialogUrl = this.getDialogUrl();
|
|
286
301
|
const params = new URLSearchParams({
|
|
287
|
-
clientId: this.config.clientId,
|
|
288
302
|
mode: "iframe"
|
|
289
303
|
});
|
|
304
|
+
if (this.config.clientId) {
|
|
305
|
+
params.set("clientId", this.config.clientId);
|
|
306
|
+
}
|
|
290
307
|
if (options?.username) {
|
|
291
308
|
params.set("username", options.username);
|
|
292
309
|
}
|
|
@@ -302,6 +319,182 @@ var OneAuthClient = class {
|
|
|
302
319
|
const { dialog, iframe, cleanup } = this.createModalDialog(url);
|
|
303
320
|
return this.waitForModalAuthResponse(dialog, iframe, cleanup);
|
|
304
321
|
}
|
|
322
|
+
/**
|
|
323
|
+
* Open the connect dialog (lightweight connection without passkey auth).
|
|
324
|
+
*
|
|
325
|
+
* This method shows a simple connection confirmation dialog that doesn't
|
|
326
|
+
* require a passkey signature. Users can optionally enable "auto-connect"
|
|
327
|
+
* to skip this dialog in the future.
|
|
328
|
+
*
|
|
329
|
+
* If the user has never connected before, this will return action: "switch"
|
|
330
|
+
* to indicate that the full auth modal should be opened instead.
|
|
331
|
+
*
|
|
332
|
+
* @example
|
|
333
|
+
* ```typescript
|
|
334
|
+
* const result = await client.connectWithModal();
|
|
335
|
+
*
|
|
336
|
+
* if (result.success) {
|
|
337
|
+
* console.log('Connected as:', result.username);
|
|
338
|
+
* } else if (result.action === 'switch') {
|
|
339
|
+
* // User needs to sign in first
|
|
340
|
+
* const authResult = await client.authWithModal();
|
|
341
|
+
* }
|
|
342
|
+
* ```
|
|
343
|
+
*/
|
|
344
|
+
async connectWithModal(options) {
|
|
345
|
+
const dialogUrl = this.getDialogUrl();
|
|
346
|
+
const params = new URLSearchParams({
|
|
347
|
+
mode: "iframe"
|
|
348
|
+
});
|
|
349
|
+
if (this.config.clientId) {
|
|
350
|
+
params.set("clientId", this.config.clientId);
|
|
351
|
+
}
|
|
352
|
+
const themeParams = this.getThemeParams(options?.theme);
|
|
353
|
+
if (themeParams) {
|
|
354
|
+
const themeParsed = new URLSearchParams(themeParams);
|
|
355
|
+
themeParsed.forEach((value, key) => params.set(key, value));
|
|
356
|
+
}
|
|
357
|
+
const url = `${dialogUrl}/dialog/connect?${params.toString()}`;
|
|
358
|
+
const { dialog, iframe, cleanup } = this.createModalDialog(url);
|
|
359
|
+
const ready = await this.waitForDialogReady(dialog, iframe, cleanup, {
|
|
360
|
+
mode: "iframe"
|
|
361
|
+
});
|
|
362
|
+
if (!ready) {
|
|
363
|
+
return {
|
|
364
|
+
success: false,
|
|
365
|
+
action: "cancel",
|
|
366
|
+
error: { code: "USER_CANCELLED", message: "Connection was cancelled" }
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
return this.waitForConnectResponse(dialog, iframe, cleanup);
|
|
370
|
+
}
|
|
371
|
+
/**
|
|
372
|
+
* Check if a user has already granted consent for the requested fields.
|
|
373
|
+
* This is a read-only check — no dialog is shown.
|
|
374
|
+
*
|
|
375
|
+
* @example
|
|
376
|
+
* ```typescript
|
|
377
|
+
* const result = await client.checkConsent({
|
|
378
|
+
* username: "alice",
|
|
379
|
+
* fields: ["email"],
|
|
380
|
+
* });
|
|
381
|
+
* if (result.hasConsent) {
|
|
382
|
+
* console.log(result.data?.email);
|
|
383
|
+
* }
|
|
384
|
+
* ```
|
|
385
|
+
*/
|
|
386
|
+
async checkConsent(options) {
|
|
387
|
+
const clientId = options.clientId ?? this.config.clientId;
|
|
388
|
+
if (!clientId) {
|
|
389
|
+
return {
|
|
390
|
+
hasConsent: false
|
|
391
|
+
};
|
|
392
|
+
}
|
|
393
|
+
const username = options.username ?? options.accountAddress;
|
|
394
|
+
if (!username) {
|
|
395
|
+
return {
|
|
396
|
+
hasConsent: false
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
try {
|
|
400
|
+
const res = await fetch(`${this.config.providerUrl || "https://passkey.1auth.box"}/api/consent`, {
|
|
401
|
+
method: "POST",
|
|
402
|
+
headers: {
|
|
403
|
+
"Content-Type": "application/json",
|
|
404
|
+
...clientId ? { "x-client-id": clientId } : {}
|
|
405
|
+
},
|
|
406
|
+
body: JSON.stringify({
|
|
407
|
+
username,
|
|
408
|
+
requestedFields: options.fields,
|
|
409
|
+
clientId
|
|
410
|
+
})
|
|
411
|
+
});
|
|
412
|
+
if (!res.ok) {
|
|
413
|
+
return { hasConsent: false };
|
|
414
|
+
}
|
|
415
|
+
const data = await res.json();
|
|
416
|
+
return {
|
|
417
|
+
hasConsent: data.hasConsent ?? false,
|
|
418
|
+
data: data.data,
|
|
419
|
+
grantedAt: data.grantedAt
|
|
420
|
+
};
|
|
421
|
+
} catch {
|
|
422
|
+
return { hasConsent: false };
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
/**
|
|
426
|
+
* Request consent from the user to share their data.
|
|
427
|
+
*
|
|
428
|
+
* First checks if consent was already granted (returns cached data immediately).
|
|
429
|
+
* If not, opens the consent dialog where the user can review and approve sharing.
|
|
430
|
+
*
|
|
431
|
+
* @example
|
|
432
|
+
* ```typescript
|
|
433
|
+
* const result = await client.requestConsent({
|
|
434
|
+
* username: "alice",
|
|
435
|
+
* fields: ["email", "deviceNames"],
|
|
436
|
+
* });
|
|
437
|
+
* if (result.success) {
|
|
438
|
+
* console.log(result.data?.email);
|
|
439
|
+
* console.log(result.cached); // true if no dialog was shown
|
|
440
|
+
* }
|
|
441
|
+
* ```
|
|
442
|
+
*/
|
|
443
|
+
async requestConsent(options) {
|
|
444
|
+
const clientId = options.clientId ?? this.config.clientId;
|
|
445
|
+
if (!clientId) {
|
|
446
|
+
return {
|
|
447
|
+
success: false,
|
|
448
|
+
error: { code: "INVALID_REQUEST", message: "clientId is required (set in config or options)" }
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
const username = options.username ?? options.accountAddress;
|
|
452
|
+
if (!username) {
|
|
453
|
+
return {
|
|
454
|
+
success: false,
|
|
455
|
+
error: { code: "INVALID_REQUEST", message: "username or accountAddress is required" }
|
|
456
|
+
};
|
|
457
|
+
}
|
|
458
|
+
if (!options.fields || options.fields.length === 0) {
|
|
459
|
+
return {
|
|
460
|
+
success: false,
|
|
461
|
+
error: { code: "INVALID_REQUEST", message: "At least one field is required" }
|
|
462
|
+
};
|
|
463
|
+
}
|
|
464
|
+
const existing = await this.checkConsent({ ...options, clientId });
|
|
465
|
+
if (existing.hasConsent && existing.data) {
|
|
466
|
+
return {
|
|
467
|
+
success: true,
|
|
468
|
+
data: existing.data,
|
|
469
|
+
grantedAt: existing.grantedAt,
|
|
470
|
+
cached: true
|
|
471
|
+
};
|
|
472
|
+
}
|
|
473
|
+
const dialogUrl = this.getDialogUrl();
|
|
474
|
+
const params = new URLSearchParams({
|
|
475
|
+
mode: "iframe",
|
|
476
|
+
username,
|
|
477
|
+
clientId,
|
|
478
|
+
fields: options.fields.join(",")
|
|
479
|
+
});
|
|
480
|
+
const themeParams = this.getThemeParams(options.theme);
|
|
481
|
+
if (themeParams) {
|
|
482
|
+
const themeParsed = new URLSearchParams(themeParams);
|
|
483
|
+
themeParsed.forEach((value, key) => params.set(key, value));
|
|
484
|
+
}
|
|
485
|
+
const url = `${dialogUrl}/dialog/consent?${params.toString()}`;
|
|
486
|
+
const { dialog, iframe, cleanup } = this.createModalDialog(url);
|
|
487
|
+
const ready = await this.waitForDialogReady(dialog, iframe, cleanup, {
|
|
488
|
+
mode: "iframe"
|
|
489
|
+
});
|
|
490
|
+
if (!ready) {
|
|
491
|
+
return {
|
|
492
|
+
success: false,
|
|
493
|
+
error: { code: "USER_CANCELLED", message: "User closed the dialog" }
|
|
494
|
+
};
|
|
495
|
+
}
|
|
496
|
+
return this.waitForConsentResponse(dialog, iframe, cleanup);
|
|
497
|
+
}
|
|
305
498
|
/**
|
|
306
499
|
* Authenticate a user with an optional challenge to sign.
|
|
307
500
|
*
|
|
@@ -337,9 +530,11 @@ var OneAuthClient = class {
|
|
|
337
530
|
async authenticate(options) {
|
|
338
531
|
const dialogUrl = this.getDialogUrl();
|
|
339
532
|
const params = new URLSearchParams({
|
|
340
|
-
clientId: this.config.clientId,
|
|
341
533
|
mode: "iframe"
|
|
342
534
|
});
|
|
535
|
+
if (this.config.clientId) {
|
|
536
|
+
params.set("clientId", this.config.clientId);
|
|
537
|
+
}
|
|
343
538
|
if (options?.challenge) {
|
|
344
539
|
params.set("challenge", options.challenge);
|
|
345
540
|
}
|
|
@@ -353,28 +548,30 @@ var OneAuthClient = class {
|
|
|
353
548
|
return this.waitForAuthenticateResponse(dialog, iframe, cleanup);
|
|
354
549
|
}
|
|
355
550
|
/**
|
|
356
|
-
* Show signing in a modal overlay (
|
|
551
|
+
* Show signing in a modal overlay (iframe dialog)
|
|
357
552
|
*/
|
|
358
553
|
async signWithModal(options) {
|
|
359
554
|
const dialogUrl = this.getDialogUrl();
|
|
360
555
|
const themeParams = this.getThemeParams(options?.theme);
|
|
361
556
|
const signingUrl = `${dialogUrl}/dialog/sign?mode=iframe${themeParams ? `&${themeParams}` : ""}`;
|
|
362
557
|
const { dialog, iframe, cleanup } = this.createModalDialog(signingUrl);
|
|
363
|
-
const
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
username: options.username,
|
|
371
|
-
description: options.description,
|
|
372
|
-
transaction: options.transaction,
|
|
373
|
-
metadata: options.metadata
|
|
374
|
-
}, dialogOrigin);
|
|
375
|
-
resolve();
|
|
376
|
-
};
|
|
558
|
+
const ready = await this.waitForDialogReady(dialog, iframe, cleanup, {
|
|
559
|
+
mode: "iframe",
|
|
560
|
+
challenge: options.challenge,
|
|
561
|
+
username: options.username,
|
|
562
|
+
description: options.description,
|
|
563
|
+
transaction: options.transaction,
|
|
564
|
+
metadata: options.metadata
|
|
377
565
|
});
|
|
566
|
+
if (!ready) {
|
|
567
|
+
return {
|
|
568
|
+
success: false,
|
|
569
|
+
error: {
|
|
570
|
+
code: "USER_REJECTED",
|
|
571
|
+
message: "User closed the dialog"
|
|
572
|
+
}
|
|
573
|
+
};
|
|
574
|
+
}
|
|
378
575
|
return this.waitForSigningResponse(dialog, iframe, cleanup);
|
|
379
576
|
}
|
|
380
577
|
/**
|
|
@@ -425,7 +622,8 @@ var OneAuthClient = class {
|
|
|
425
622
|
}
|
|
426
623
|
};
|
|
427
624
|
}
|
|
428
|
-
|
|
625
|
+
const accountAddress = signedIntent?.accountAddress || options.accountAddress;
|
|
626
|
+
if (!username && !accountAddress) {
|
|
429
627
|
return {
|
|
430
628
|
success: false,
|
|
431
629
|
intentId: "",
|
|
@@ -447,158 +645,264 @@ var OneAuthClient = class {
|
|
|
447
645
|
}
|
|
448
646
|
};
|
|
449
647
|
}
|
|
648
|
+
const serializedTokenRequests = options.tokenRequests?.map((r) => ({
|
|
649
|
+
token: r.token,
|
|
650
|
+
amount: r.amount.toString()
|
|
651
|
+
}));
|
|
450
652
|
let prepareResponse;
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
653
|
+
const requestBody = signedIntent || {
|
|
654
|
+
username: options.username,
|
|
655
|
+
accountAddress: options.accountAddress,
|
|
656
|
+
targetChain: options.targetChain,
|
|
657
|
+
calls: options.calls,
|
|
658
|
+
tokenRequests: serializedTokenRequests,
|
|
659
|
+
sourceAssets: options.sourceAssets,
|
|
660
|
+
sourceChainId: options.sourceChainId,
|
|
661
|
+
...this.config.clientId && { clientId: this.config.clientId }
|
|
662
|
+
};
|
|
663
|
+
const dialogUrl = this.getDialogUrl();
|
|
664
|
+
const themeParams = this.getThemeParams();
|
|
665
|
+
const signingUrl = `${dialogUrl}/dialog/sign?mode=iframe${themeParams ? `&${themeParams}` : ""}`;
|
|
666
|
+
const { dialog, iframe, cleanup } = this.createModalDialog(signingUrl);
|
|
667
|
+
const [prepareResult, dialogResult] = await Promise.all([
|
|
668
|
+
this.prepareIntent(requestBody),
|
|
669
|
+
this.waitForDialogReadyDeferred(dialog, iframe, cleanup)
|
|
670
|
+
]);
|
|
671
|
+
if (!dialogResult.ready) {
|
|
672
|
+
return {
|
|
673
|
+
success: false,
|
|
674
|
+
intentId: "",
|
|
675
|
+
status: "failed",
|
|
676
|
+
error: { code: "USER_CANCELLED", message: "User closed the dialog" }
|
|
459
677
|
};
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
},
|
|
465
|
-
body: JSON.stringify(requestBody)
|
|
466
|
-
});
|
|
467
|
-
if (!response.ok) {
|
|
468
|
-
const errorData = await response.json().catch(() => ({}));
|
|
469
|
-
const errorMessage = errorData.error || "Failed to prepare intent";
|
|
470
|
-
if (errorMessage.includes("User not found")) {
|
|
471
|
-
localStorage.removeItem("1auth-user");
|
|
472
|
-
}
|
|
473
|
-
return {
|
|
474
|
-
success: false,
|
|
475
|
-
intentId: "",
|
|
476
|
-
status: "failed",
|
|
477
|
-
error: {
|
|
478
|
-
code: errorMessage.includes("User not found") ? "USER_NOT_FOUND" : "PREPARE_FAILED",
|
|
479
|
-
message: errorMessage
|
|
480
|
-
}
|
|
481
|
-
};
|
|
482
|
-
}
|
|
483
|
-
prepareResponse = await response.json();
|
|
484
|
-
} catch (error) {
|
|
678
|
+
}
|
|
679
|
+
if (!prepareResult.success) {
|
|
680
|
+
this.sendPrepareError(iframe, prepareResult.error.message);
|
|
681
|
+
await this.waitForDialogClose(dialog, cleanup);
|
|
485
682
|
return {
|
|
486
683
|
success: false,
|
|
487
684
|
intentId: "",
|
|
488
685
|
status: "failed",
|
|
489
|
-
error:
|
|
490
|
-
code: "NETWORK_ERROR",
|
|
491
|
-
message: error instanceof Error ? error.message : "Network error"
|
|
492
|
-
}
|
|
686
|
+
error: prepareResult.error
|
|
493
687
|
};
|
|
494
688
|
}
|
|
495
|
-
|
|
496
|
-
const signingUrl = `${dialogUrl}/dialog/sign?mode=iframe`;
|
|
497
|
-
const { dialog, iframe, cleanup } = this.createModalDialog(signingUrl);
|
|
689
|
+
prepareResponse = prepareResult.data;
|
|
498
690
|
const dialogOrigin = this.getDialogOrigin();
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
691
|
+
const initPayload = {
|
|
692
|
+
mode: "iframe",
|
|
693
|
+
calls,
|
|
694
|
+
chainId: targetChain,
|
|
695
|
+
transaction: prepareResponse.transaction,
|
|
696
|
+
challenge: prepareResponse.challenge,
|
|
697
|
+
username,
|
|
698
|
+
accountAddress: prepareResponse.accountAddress,
|
|
699
|
+
originMessages: prepareResponse.originMessages,
|
|
700
|
+
tokenRequests: serializedTokenRequests,
|
|
701
|
+
expiresAt: prepareResponse.expiresAt,
|
|
702
|
+
userId: prepareResponse.userId,
|
|
703
|
+
intentOp: prepareResponse.intentOp,
|
|
704
|
+
digestResult: prepareResponse.digestResult,
|
|
705
|
+
tier: prepareResult.tier
|
|
706
|
+
};
|
|
707
|
+
dialogResult.sendInit(initPayload);
|
|
708
|
+
const handleReReady = (event) => {
|
|
709
|
+
if (event.origin !== dialogOrigin) return;
|
|
710
|
+
if (event.data?.type === "PASSKEY_READY") {
|
|
711
|
+
iframe.contentWindow?.postMessage(
|
|
712
|
+
{ type: "PASSKEY_INIT", ...initPayload },
|
|
713
|
+
dialogOrigin
|
|
714
|
+
);
|
|
715
|
+
}
|
|
716
|
+
};
|
|
717
|
+
window.addEventListener("message", handleReReady);
|
|
718
|
+
const signingResult = await this.waitForSigningWithRefresh(
|
|
719
|
+
dialog,
|
|
720
|
+
iframe,
|
|
721
|
+
cleanup,
|
|
722
|
+
dialogOrigin,
|
|
723
|
+
// Refresh callback - called when dialog requests a quote refresh
|
|
724
|
+
async () => {
|
|
725
|
+
console.log("[SDK] Dialog requested quote refresh, re-preparing intent");
|
|
726
|
+
try {
|
|
727
|
+
const refreshResponse = await fetch(`${this.config.providerUrl}/api/intent/prepare`, {
|
|
728
|
+
method: "POST",
|
|
729
|
+
headers: { "Content-Type": "application/json" },
|
|
730
|
+
body: JSON.stringify(requestBody),
|
|
731
|
+
credentials: "include"
|
|
732
|
+
});
|
|
733
|
+
if (!refreshResponse.ok) {
|
|
734
|
+
console.error("[SDK] Quote refresh failed:", await refreshResponse.text());
|
|
735
|
+
return null;
|
|
736
|
+
}
|
|
737
|
+
const refreshedData = await refreshResponse.json();
|
|
738
|
+
prepareResponse = refreshedData;
|
|
739
|
+
return {
|
|
740
|
+
intentOp: refreshedData.intentOp,
|
|
741
|
+
expiresAt: refreshedData.expiresAt,
|
|
742
|
+
challenge: refreshedData.challenge,
|
|
743
|
+
originMessages: refreshedData.originMessages,
|
|
744
|
+
transaction: refreshedData.transaction,
|
|
745
|
+
digestResult: refreshedData.digestResult
|
|
746
|
+
};
|
|
747
|
+
} catch (error) {
|
|
748
|
+
console.error("[SDK] Quote refresh error:", error);
|
|
749
|
+
return null;
|
|
516
750
|
}
|
|
517
|
-
}
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
const signingResult = await this.waitForSigningResponse(dialog, iframe, cleanup);
|
|
751
|
+
}
|
|
752
|
+
);
|
|
753
|
+
window.removeEventListener("message", handleReReady);
|
|
521
754
|
if (!signingResult.success) {
|
|
522
755
|
return {
|
|
523
756
|
success: false,
|
|
524
|
-
intentId:
|
|
757
|
+
intentId: "",
|
|
758
|
+
// No intentId yet - signing was cancelled before execute
|
|
525
759
|
status: "failed",
|
|
526
760
|
error: signingResult.error
|
|
527
761
|
};
|
|
528
762
|
}
|
|
763
|
+
const dialogExecutedIntent = "intentId" in signingResult && signingResult.intentId;
|
|
529
764
|
let executeResponse;
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
765
|
+
if (dialogExecutedIntent) {
|
|
766
|
+
executeResponse = {
|
|
767
|
+
success: true,
|
|
768
|
+
intentId: signingResult.intentId,
|
|
769
|
+
status: "pending"
|
|
770
|
+
};
|
|
771
|
+
} else {
|
|
772
|
+
try {
|
|
773
|
+
const response = await fetch(`${this.config.providerUrl}/api/intent/execute`, {
|
|
774
|
+
method: "POST",
|
|
775
|
+
headers: {
|
|
776
|
+
"Content-Type": "application/json"
|
|
777
|
+
},
|
|
778
|
+
body: JSON.stringify({
|
|
779
|
+
// Data from prepare response (no intentId yet - created on execute)
|
|
780
|
+
intentOp: prepareResponse.intentOp,
|
|
781
|
+
userId: prepareResponse.userId,
|
|
782
|
+
targetChain: prepareResponse.targetChain,
|
|
783
|
+
calls: prepareResponse.calls,
|
|
784
|
+
expiresAt: prepareResponse.expiresAt,
|
|
785
|
+
digestResult: prepareResponse.digestResult,
|
|
786
|
+
// Signature from dialog
|
|
787
|
+
signature: signingResult.signature,
|
|
788
|
+
passkey: signingResult.passkey
|
|
789
|
+
// Include passkey info for signature encoding
|
|
790
|
+
})
|
|
791
|
+
});
|
|
792
|
+
if (!response.ok) {
|
|
793
|
+
const errorData = await response.json().catch(() => ({}));
|
|
794
|
+
this.sendTransactionStatus(iframe, "failed");
|
|
795
|
+
await this.waitForDialogClose(dialog, cleanup);
|
|
796
|
+
return {
|
|
797
|
+
success: false,
|
|
798
|
+
intentId: "",
|
|
799
|
+
// No intentId - execute failed before creation
|
|
800
|
+
status: "failed",
|
|
801
|
+
error: {
|
|
802
|
+
code: "EXECUTE_FAILED",
|
|
803
|
+
message: errorData.error || "Failed to execute intent"
|
|
804
|
+
}
|
|
805
|
+
};
|
|
806
|
+
}
|
|
807
|
+
executeResponse = await response.json();
|
|
808
|
+
} catch (error) {
|
|
545
809
|
this.sendTransactionStatus(iframe, "failed");
|
|
546
810
|
await this.waitForDialogClose(dialog, cleanup);
|
|
547
811
|
return {
|
|
548
812
|
success: false,
|
|
549
|
-
intentId:
|
|
813
|
+
intentId: "",
|
|
814
|
+
// No intentId - network error before creation
|
|
550
815
|
status: "failed",
|
|
551
816
|
error: {
|
|
552
|
-
code: "
|
|
553
|
-
message:
|
|
817
|
+
code: "NETWORK_ERROR",
|
|
818
|
+
message: error instanceof Error ? error.message : "Network error"
|
|
554
819
|
}
|
|
555
820
|
};
|
|
556
821
|
}
|
|
557
|
-
executeResponse = await response.json();
|
|
558
|
-
} catch (error) {
|
|
559
|
-
this.sendTransactionStatus(iframe, "failed");
|
|
560
|
-
await this.waitForDialogClose(dialog, cleanup);
|
|
561
|
-
return {
|
|
562
|
-
success: false,
|
|
563
|
-
intentId: prepareResponse.intentId,
|
|
564
|
-
status: "failed",
|
|
565
|
-
error: {
|
|
566
|
-
code: "NETWORK_ERROR",
|
|
567
|
-
message: error instanceof Error ? error.message : "Network error"
|
|
568
|
-
}
|
|
569
|
-
};
|
|
570
822
|
}
|
|
571
|
-
const closeOn = options.closeOn || "preconfirmed";
|
|
572
|
-
const acceptPreconfirmations = closeOn !== "completed";
|
|
573
823
|
let finalStatus = executeResponse.status;
|
|
574
824
|
let finalTxHash = executeResponse.transactionHash;
|
|
575
825
|
if (finalStatus === "pending") {
|
|
576
|
-
this.sendTransactionStatus(iframe, "
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
826
|
+
this.sendTransactionStatus(iframe, "pending");
|
|
827
|
+
let userClosedEarly = false;
|
|
828
|
+
const dialogOrigin2 = this.getDialogOrigin();
|
|
829
|
+
const earlyCloseHandler = (event) => {
|
|
830
|
+
if (event.origin !== dialogOrigin2) return;
|
|
831
|
+
if (event.data?.type === "PASSKEY_CLOSE") {
|
|
832
|
+
userClosedEarly = true;
|
|
833
|
+
cleanup();
|
|
834
|
+
}
|
|
835
|
+
};
|
|
836
|
+
window.addEventListener("message", earlyCloseHandler);
|
|
837
|
+
const maxAttempts = 120;
|
|
838
|
+
const pollIntervalMs = 1500;
|
|
839
|
+
let lastStatus = "pending";
|
|
840
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
841
|
+
if (userClosedEarly) break;
|
|
842
|
+
try {
|
|
843
|
+
const statusResponse = await fetch(
|
|
844
|
+
`${this.config.providerUrl}/api/intent/status/${executeResponse.intentId}`,
|
|
845
|
+
{
|
|
846
|
+
method: "GET",
|
|
847
|
+
headers: this.config.clientId ? { "x-client-id": this.config.clientId } : {}
|
|
848
|
+
}
|
|
849
|
+
);
|
|
850
|
+
if (statusResponse.ok) {
|
|
851
|
+
const statusResult = await statusResponse.json();
|
|
852
|
+
finalStatus = statusResult.status;
|
|
853
|
+
finalTxHash = statusResult.transactionHash;
|
|
854
|
+
if (finalStatus !== lastStatus) {
|
|
855
|
+
this.sendTransactionStatus(iframe, finalStatus, finalTxHash);
|
|
856
|
+
lastStatus = finalStatus;
|
|
857
|
+
}
|
|
858
|
+
const closeOn2 = options.closeOn || "preconfirmed";
|
|
859
|
+
const successStatuses2 = {
|
|
860
|
+
claimed: ["claimed", "preconfirmed", "filled", "completed"],
|
|
861
|
+
preconfirmed: ["preconfirmed", "filled", "completed"],
|
|
862
|
+
filled: ["filled", "completed"],
|
|
863
|
+
completed: ["completed"]
|
|
864
|
+
};
|
|
865
|
+
const isTerminal = finalStatus === "failed" || finalStatus === "expired";
|
|
866
|
+
const isSuccess = successStatuses2[closeOn2]?.includes(finalStatus) ?? false;
|
|
867
|
+
if (isTerminal || isSuccess) {
|
|
868
|
+
break;
|
|
583
869
|
}
|
|
584
870
|
}
|
|
585
|
-
)
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
871
|
+
} catch (pollError) {
|
|
872
|
+
console.error("Failed to poll intent status:", pollError);
|
|
873
|
+
}
|
|
874
|
+
await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
|
|
875
|
+
}
|
|
876
|
+
window.removeEventListener("message", earlyCloseHandler);
|
|
877
|
+
if (userClosedEarly) {
|
|
878
|
+
cleanup();
|
|
879
|
+
return {
|
|
880
|
+
success: false,
|
|
881
|
+
intentId: executeResponse.intentId,
|
|
882
|
+
status: finalStatus,
|
|
883
|
+
transactionHash: finalTxHash,
|
|
884
|
+
operationId: executeResponse.operationId,
|
|
885
|
+
error: {
|
|
886
|
+
code: "USER_CANCELLED",
|
|
887
|
+
message: "User closed the dialog"
|
|
888
|
+
}
|
|
889
|
+
};
|
|
595
890
|
}
|
|
596
891
|
}
|
|
597
|
-
const
|
|
892
|
+
const closeOn = options.closeOn || "preconfirmed";
|
|
893
|
+
const successStatuses = {
|
|
894
|
+
claimed: ["claimed", "preconfirmed", "filled", "completed"],
|
|
895
|
+
preconfirmed: ["preconfirmed", "filled", "completed"],
|
|
896
|
+
filled: ["filled", "completed"],
|
|
897
|
+
completed: ["completed"]
|
|
898
|
+
};
|
|
899
|
+
const isSuccessStatus = successStatuses[closeOn]?.includes(finalStatus) ?? false;
|
|
900
|
+
const displayStatus = isSuccessStatus ? "confirmed" : finalStatus;
|
|
901
|
+
const closePromise = this.waitForDialogClose(dialog, cleanup);
|
|
598
902
|
this.sendTransactionStatus(iframe, displayStatus, finalTxHash);
|
|
599
|
-
await
|
|
903
|
+
await closePromise;
|
|
600
904
|
if (options.waitForHash && !finalTxHash) {
|
|
601
|
-
const hash = await this.waitForTransactionHash(
|
|
905
|
+
const hash = await this.waitForTransactionHash(executeResponse.intentId, {
|
|
602
906
|
timeoutMs: options.hashTimeoutMs,
|
|
603
907
|
intervalMs: options.hashIntervalMs
|
|
604
908
|
});
|
|
@@ -609,7 +913,7 @@ var OneAuthClient = class {
|
|
|
609
913
|
finalStatus = "failed";
|
|
610
914
|
return {
|
|
611
915
|
success: false,
|
|
612
|
-
intentId:
|
|
916
|
+
intentId: executeResponse.intentId,
|
|
613
917
|
status: finalStatus,
|
|
614
918
|
transactionHash: finalTxHash,
|
|
615
919
|
operationId: executeResponse.operationId,
|
|
@@ -621,14 +925,223 @@ var OneAuthClient = class {
|
|
|
621
925
|
}
|
|
622
926
|
}
|
|
623
927
|
return {
|
|
624
|
-
success:
|
|
625
|
-
intentId:
|
|
928
|
+
success: isSuccessStatus,
|
|
929
|
+
intentId: executeResponse.intentId,
|
|
626
930
|
status: finalStatus,
|
|
627
931
|
transactionHash: finalTxHash,
|
|
628
932
|
operationId: executeResponse.operationId,
|
|
629
933
|
error: executeResponse.error
|
|
630
934
|
};
|
|
631
935
|
}
|
|
936
|
+
/**
|
|
937
|
+
* Send a batch of intents for multi-chain execution with a single passkey tap.
|
|
938
|
+
*
|
|
939
|
+
* This method prepares multiple intents, shows a paginated review,
|
|
940
|
+
* and signs all intents with a single passkey tap via a shared merkle tree.
|
|
941
|
+
*
|
|
942
|
+
* @example
|
|
943
|
+
* ```typescript
|
|
944
|
+
* const result = await client.sendBatchIntent({
|
|
945
|
+
* username: 'alice',
|
|
946
|
+
* intents: [
|
|
947
|
+
* {
|
|
948
|
+
* targetChain: 8453, // Base
|
|
949
|
+
* calls: [{ to: '0x...', data: '0x...', label: 'Swap on Base' }],
|
|
950
|
+
* },
|
|
951
|
+
* {
|
|
952
|
+
* targetChain: 42161, // Arbitrum
|
|
953
|
+
* calls: [{ to: '0x...', data: '0x...', label: 'Mint on Arbitrum' }],
|
|
954
|
+
* },
|
|
955
|
+
* ],
|
|
956
|
+
* });
|
|
957
|
+
*
|
|
958
|
+
* if (result.success) {
|
|
959
|
+
* console.log(`${result.successCount} intents submitted`);
|
|
960
|
+
* }
|
|
961
|
+
* ```
|
|
962
|
+
*/
|
|
963
|
+
async sendBatchIntent(options) {
|
|
964
|
+
if (!options.username && !options.accountAddress) {
|
|
965
|
+
return {
|
|
966
|
+
success: false,
|
|
967
|
+
results: [],
|
|
968
|
+
successCount: 0,
|
|
969
|
+
failureCount: 0
|
|
970
|
+
};
|
|
971
|
+
}
|
|
972
|
+
if (!options.intents?.length) {
|
|
973
|
+
return {
|
|
974
|
+
success: false,
|
|
975
|
+
results: [],
|
|
976
|
+
successCount: 0,
|
|
977
|
+
failureCount: 0
|
|
978
|
+
};
|
|
979
|
+
}
|
|
980
|
+
const serializedIntents = options.intents.map((intent) => ({
|
|
981
|
+
targetChain: intent.targetChain,
|
|
982
|
+
calls: intent.calls,
|
|
983
|
+
tokenRequests: intent.tokenRequests?.map((r) => ({
|
|
984
|
+
token: r.token,
|
|
985
|
+
amount: r.amount.toString()
|
|
986
|
+
})),
|
|
987
|
+
sourceAssets: intent.sourceAssets,
|
|
988
|
+
sourceChainId: intent.sourceChainId,
|
|
989
|
+
moduleInstall: intent.moduleInstall
|
|
990
|
+
}));
|
|
991
|
+
const requestBody = {
|
|
992
|
+
...options.username && { username: options.username },
|
|
993
|
+
...options.accountAddress && { accountAddress: options.accountAddress },
|
|
994
|
+
intents: serializedIntents,
|
|
995
|
+
...this.config.clientId && { clientId: this.config.clientId }
|
|
996
|
+
};
|
|
997
|
+
const dialogUrl = this.getDialogUrl();
|
|
998
|
+
const themeParams = this.getThemeParams();
|
|
999
|
+
const signingUrl = `${dialogUrl}/dialog/sign?mode=iframe${themeParams ? `&${themeParams}` : ""}`;
|
|
1000
|
+
const { dialog, iframe, cleanup } = this.createModalDialog(signingUrl);
|
|
1001
|
+
const [prepareResult, dialogResult] = await Promise.all([
|
|
1002
|
+
this.prepareBatchIntent(requestBody),
|
|
1003
|
+
this.waitForDialogReadyDeferred(dialog, iframe, cleanup)
|
|
1004
|
+
]);
|
|
1005
|
+
if (!dialogResult.ready) {
|
|
1006
|
+
return {
|
|
1007
|
+
success: false,
|
|
1008
|
+
results: [],
|
|
1009
|
+
successCount: 0,
|
|
1010
|
+
failureCount: 0
|
|
1011
|
+
};
|
|
1012
|
+
}
|
|
1013
|
+
if (!prepareResult.success) {
|
|
1014
|
+
const failedIntents = prepareResult.failedIntents;
|
|
1015
|
+
const failureResults = failedIntents?.map((f) => ({
|
|
1016
|
+
index: f.index,
|
|
1017
|
+
success: false,
|
|
1018
|
+
intentId: "",
|
|
1019
|
+
status: "failed",
|
|
1020
|
+
error: { message: f.error, code: "PREPARE_FAILED" }
|
|
1021
|
+
})) ?? [];
|
|
1022
|
+
this.sendPrepareError(iframe, prepareResult.error);
|
|
1023
|
+
await this.waitForDialogClose(dialog, cleanup);
|
|
1024
|
+
return {
|
|
1025
|
+
success: false,
|
|
1026
|
+
results: failureResults,
|
|
1027
|
+
successCount: 0,
|
|
1028
|
+
failureCount: failureResults.length,
|
|
1029
|
+
error: prepareResult.error
|
|
1030
|
+
};
|
|
1031
|
+
}
|
|
1032
|
+
let prepareResponse = prepareResult.data;
|
|
1033
|
+
const dialogOrigin = this.getDialogOrigin();
|
|
1034
|
+
const batchInitPayload = {
|
|
1035
|
+
mode: "iframe",
|
|
1036
|
+
batchMode: true,
|
|
1037
|
+
batchIntents: prepareResponse.intents,
|
|
1038
|
+
batchFailedIntents: prepareResponse.failedIntents,
|
|
1039
|
+
challenge: prepareResponse.challenge,
|
|
1040
|
+
username: options.username,
|
|
1041
|
+
accountAddress: prepareResponse.accountAddress,
|
|
1042
|
+
userId: prepareResponse.userId,
|
|
1043
|
+
expiresAt: prepareResponse.expiresAt,
|
|
1044
|
+
tier: prepareResult.tier
|
|
1045
|
+
};
|
|
1046
|
+
dialogResult.sendInit(batchInitPayload);
|
|
1047
|
+
const handleBatchReReady = (event) => {
|
|
1048
|
+
if (event.origin !== dialogOrigin) return;
|
|
1049
|
+
if (event.data?.type === "PASSKEY_READY") {
|
|
1050
|
+
iframe.contentWindow?.postMessage(
|
|
1051
|
+
{ type: "PASSKEY_INIT", ...batchInitPayload },
|
|
1052
|
+
dialogOrigin
|
|
1053
|
+
);
|
|
1054
|
+
}
|
|
1055
|
+
};
|
|
1056
|
+
window.addEventListener("message", handleBatchReReady);
|
|
1057
|
+
const batchResult = await new Promise((resolve) => {
|
|
1058
|
+
const handleMessage = async (event) => {
|
|
1059
|
+
if (event.origin !== dialogOrigin) return;
|
|
1060
|
+
const message = event.data;
|
|
1061
|
+
if (message?.type === "PASSKEY_REFRESH_QUOTE") {
|
|
1062
|
+
console.log("[SDK] Batch dialog requested quote refresh, re-preparing all intents");
|
|
1063
|
+
try {
|
|
1064
|
+
const refreshResponse = await fetch(`${this.config.providerUrl}/api/intent/batch-prepare`, {
|
|
1065
|
+
method: "POST",
|
|
1066
|
+
headers: { "Content-Type": "application/json" },
|
|
1067
|
+
body: JSON.stringify(requestBody)
|
|
1068
|
+
});
|
|
1069
|
+
if (refreshResponse.ok) {
|
|
1070
|
+
const refreshed = await refreshResponse.json();
|
|
1071
|
+
prepareResponse = refreshed;
|
|
1072
|
+
iframe.contentWindow?.postMessage({
|
|
1073
|
+
type: "PASSKEY_REFRESH_COMPLETE",
|
|
1074
|
+
batchIntents: refreshed.intents,
|
|
1075
|
+
challenge: refreshed.challenge,
|
|
1076
|
+
expiresAt: refreshed.expiresAt
|
|
1077
|
+
}, dialogOrigin);
|
|
1078
|
+
} else {
|
|
1079
|
+
iframe.contentWindow?.postMessage({
|
|
1080
|
+
type: "PASSKEY_REFRESH_ERROR",
|
|
1081
|
+
error: "Failed to refresh batch quotes"
|
|
1082
|
+
}, dialogOrigin);
|
|
1083
|
+
}
|
|
1084
|
+
} catch {
|
|
1085
|
+
iframe.contentWindow?.postMessage({
|
|
1086
|
+
type: "PASSKEY_REFRESH_ERROR",
|
|
1087
|
+
error: "Failed to refresh batch quotes"
|
|
1088
|
+
}, dialogOrigin);
|
|
1089
|
+
}
|
|
1090
|
+
return;
|
|
1091
|
+
}
|
|
1092
|
+
if (message?.type === "PASSKEY_SIGNING_RESULT") {
|
|
1093
|
+
window.removeEventListener("message", handleMessage);
|
|
1094
|
+
if (message.success && message.data?.batchResults) {
|
|
1095
|
+
const rawResults = message.data.batchResults;
|
|
1096
|
+
const results = rawResults.map((r) => ({
|
|
1097
|
+
index: r.index,
|
|
1098
|
+
success: r.success ?? r.status !== "FAILED",
|
|
1099
|
+
intentId: r.intentId || r.operationId || "",
|
|
1100
|
+
status: r.status === "FAILED" ? "failed" : "pending",
|
|
1101
|
+
error: r.error ? { code: "EXECUTE_FAILED", message: r.error } : void 0
|
|
1102
|
+
}));
|
|
1103
|
+
const prepareFailures = (prepareResponse.failedIntents ?? []).map((f) => ({
|
|
1104
|
+
index: f.index,
|
|
1105
|
+
success: false,
|
|
1106
|
+
intentId: "",
|
|
1107
|
+
status: "failed",
|
|
1108
|
+
error: { code: "PREPARE_FAILED", message: f.error }
|
|
1109
|
+
}));
|
|
1110
|
+
const allResults = [...results, ...prepareFailures].sort((a, b) => a.index - b.index);
|
|
1111
|
+
const successCount = allResults.filter((r) => r.success).length;
|
|
1112
|
+
await this.waitForDialogClose(dialog, cleanup);
|
|
1113
|
+
resolve({
|
|
1114
|
+
success: successCount === allResults.length,
|
|
1115
|
+
results: allResults,
|
|
1116
|
+
successCount,
|
|
1117
|
+
failureCount: allResults.length - successCount
|
|
1118
|
+
});
|
|
1119
|
+
} else {
|
|
1120
|
+
cleanup();
|
|
1121
|
+
resolve({
|
|
1122
|
+
success: false,
|
|
1123
|
+
results: [],
|
|
1124
|
+
successCount: 0,
|
|
1125
|
+
failureCount: 0
|
|
1126
|
+
});
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
1129
|
+
if (message?.type === "PASSKEY_CLOSE") {
|
|
1130
|
+
window.removeEventListener("message", handleMessage);
|
|
1131
|
+
cleanup();
|
|
1132
|
+
resolve({
|
|
1133
|
+
success: false,
|
|
1134
|
+
results: [],
|
|
1135
|
+
successCount: 0,
|
|
1136
|
+
failureCount: 0
|
|
1137
|
+
});
|
|
1138
|
+
}
|
|
1139
|
+
};
|
|
1140
|
+
window.addEventListener("message", handleMessage);
|
|
1141
|
+
});
|
|
1142
|
+
window.removeEventListener("message", handleBatchReReady);
|
|
1143
|
+
return batchResult;
|
|
1144
|
+
}
|
|
632
1145
|
/**
|
|
633
1146
|
* Send transaction status to the dialog iframe
|
|
634
1147
|
*/
|
|
@@ -705,7 +1218,77 @@ var OneAuthClient = class {
|
|
|
705
1218
|
const payload = message?.data;
|
|
706
1219
|
if (message?.type === "PASSKEY_SIGNING_RESULT") {
|
|
707
1220
|
window.removeEventListener("message", handleMessage);
|
|
708
|
-
if (message.success && payload?.
|
|
1221
|
+
if (message.success && payload?.intentId) {
|
|
1222
|
+
resolve({
|
|
1223
|
+
success: true,
|
|
1224
|
+
intentId: payload.intentId
|
|
1225
|
+
});
|
|
1226
|
+
} else if (message.success && payload?.signature) {
|
|
1227
|
+
resolve({
|
|
1228
|
+
success: true,
|
|
1229
|
+
signature: payload.signature,
|
|
1230
|
+
passkey: payload.passkey,
|
|
1231
|
+
signedHash: payload.signedHash
|
|
1232
|
+
});
|
|
1233
|
+
} else {
|
|
1234
|
+
resolve({
|
|
1235
|
+
success: false,
|
|
1236
|
+
error: message.error || {
|
|
1237
|
+
code: "SIGNING_FAILED",
|
|
1238
|
+
message: "Signing failed"
|
|
1239
|
+
}
|
|
1240
|
+
});
|
|
1241
|
+
}
|
|
1242
|
+
} else if (message?.type === "PASSKEY_CLOSE") {
|
|
1243
|
+
window.removeEventListener("message", handleMessage);
|
|
1244
|
+
cleanup();
|
|
1245
|
+
resolve({
|
|
1246
|
+
success: false,
|
|
1247
|
+
error: {
|
|
1248
|
+
code: "USER_REJECTED",
|
|
1249
|
+
message: "User closed the dialog"
|
|
1250
|
+
}
|
|
1251
|
+
});
|
|
1252
|
+
}
|
|
1253
|
+
};
|
|
1254
|
+
window.addEventListener("message", handleMessage);
|
|
1255
|
+
});
|
|
1256
|
+
}
|
|
1257
|
+
/**
|
|
1258
|
+
* Wait for signing result with auto-refresh support
|
|
1259
|
+
* This method handles both signing results and quote refresh requests from the dialog
|
|
1260
|
+
*/
|
|
1261
|
+
waitForSigningWithRefresh(dialog, iframe, cleanup, dialogOrigin, onRefresh) {
|
|
1262
|
+
console.log("[SDK] waitForSigningWithRefresh, expecting origin:", dialogOrigin);
|
|
1263
|
+
return new Promise((resolve) => {
|
|
1264
|
+
const handleMessage = async (event) => {
|
|
1265
|
+
if (event.origin !== dialogOrigin) return;
|
|
1266
|
+
const message = event.data;
|
|
1267
|
+
if (message?.type === "PASSKEY_REFRESH_QUOTE") {
|
|
1268
|
+
console.log("[SDK] Received quote refresh request from dialog");
|
|
1269
|
+
const refreshedData = await onRefresh();
|
|
1270
|
+
if (refreshedData) {
|
|
1271
|
+
iframe.contentWindow?.postMessage({
|
|
1272
|
+
type: "PASSKEY_REFRESH_COMPLETE",
|
|
1273
|
+
...refreshedData
|
|
1274
|
+
}, dialogOrigin);
|
|
1275
|
+
} else {
|
|
1276
|
+
iframe.contentWindow?.postMessage({
|
|
1277
|
+
type: "PASSKEY_REFRESH_ERROR",
|
|
1278
|
+
error: "Failed to refresh quote"
|
|
1279
|
+
}, dialogOrigin);
|
|
1280
|
+
}
|
|
1281
|
+
return;
|
|
1282
|
+
}
|
|
1283
|
+
const payload = message?.data;
|
|
1284
|
+
if (message?.type === "PASSKEY_SIGNING_RESULT") {
|
|
1285
|
+
window.removeEventListener("message", handleMessage);
|
|
1286
|
+
if (message.success && payload?.intentId) {
|
|
1287
|
+
resolve({
|
|
1288
|
+
success: true,
|
|
1289
|
+
intentId: payload.intentId
|
|
1290
|
+
});
|
|
1291
|
+
} else if (message.success && payload?.signature) {
|
|
709
1292
|
resolve({
|
|
710
1293
|
success: true,
|
|
711
1294
|
signature: payload.signature,
|
|
@@ -760,6 +1343,143 @@ var OneAuthClient = class {
|
|
|
760
1343
|
dialog.addEventListener("close", handleClose);
|
|
761
1344
|
});
|
|
762
1345
|
}
|
|
1346
|
+
/**
|
|
1347
|
+
* Inject a preconnect link tag to pre-warm DNS + TLS for a given URL.
|
|
1348
|
+
*/
|
|
1349
|
+
injectPreconnect(url) {
|
|
1350
|
+
try {
|
|
1351
|
+
const origin = new URL(url).origin;
|
|
1352
|
+
if (document.querySelector(`link[rel="preconnect"][href="${origin}"]`)) return;
|
|
1353
|
+
const link = document.createElement("link");
|
|
1354
|
+
link.rel = "preconnect";
|
|
1355
|
+
link.href = origin;
|
|
1356
|
+
link.crossOrigin = "anonymous";
|
|
1357
|
+
document.head.appendChild(link);
|
|
1358
|
+
} catch {
|
|
1359
|
+
}
|
|
1360
|
+
}
|
|
1361
|
+
/**
|
|
1362
|
+
* Wait for the dialog iframe to signal ready without sending init data.
|
|
1363
|
+
* Returns a sendInit function the caller uses once prepare data is available.
|
|
1364
|
+
*/
|
|
1365
|
+
waitForDialogReadyDeferred(dialog, iframe, cleanup) {
|
|
1366
|
+
const dialogOrigin = this.getDialogOrigin();
|
|
1367
|
+
return new Promise((resolve) => {
|
|
1368
|
+
let settled = false;
|
|
1369
|
+
const teardown = () => {
|
|
1370
|
+
if (settled) return;
|
|
1371
|
+
settled = true;
|
|
1372
|
+
clearTimeout(readyTimeout);
|
|
1373
|
+
window.removeEventListener("message", handleMessage);
|
|
1374
|
+
dialog.removeEventListener("close", handleClose);
|
|
1375
|
+
};
|
|
1376
|
+
const handleMessage = (event) => {
|
|
1377
|
+
if (event.origin !== dialogOrigin) return;
|
|
1378
|
+
if (event.data?.type === "PASSKEY_READY") {
|
|
1379
|
+
teardown();
|
|
1380
|
+
resolve({
|
|
1381
|
+
ready: true,
|
|
1382
|
+
sendInit: (initMessage) => {
|
|
1383
|
+
iframe.contentWindow?.postMessage(
|
|
1384
|
+
{ type: "PASSKEY_INIT", ...initMessage },
|
|
1385
|
+
dialogOrigin
|
|
1386
|
+
);
|
|
1387
|
+
}
|
|
1388
|
+
});
|
|
1389
|
+
} else if (event.data?.type === "PASSKEY_CLOSE") {
|
|
1390
|
+
teardown();
|
|
1391
|
+
cleanup();
|
|
1392
|
+
resolve({ ready: false });
|
|
1393
|
+
}
|
|
1394
|
+
};
|
|
1395
|
+
const handleClose = () => {
|
|
1396
|
+
teardown();
|
|
1397
|
+
resolve({ ready: false });
|
|
1398
|
+
};
|
|
1399
|
+
const readyTimeout = setTimeout(() => {
|
|
1400
|
+
teardown();
|
|
1401
|
+
cleanup();
|
|
1402
|
+
resolve({ ready: false });
|
|
1403
|
+
}, 1e4);
|
|
1404
|
+
window.addEventListener("message", handleMessage);
|
|
1405
|
+
dialog.addEventListener("close", handleClose);
|
|
1406
|
+
});
|
|
1407
|
+
}
|
|
1408
|
+
/**
|
|
1409
|
+
* Prepare an intent by calling the orchestrator for a quote.
|
|
1410
|
+
* Returns the prepare response and the origin tier from the response header.
|
|
1411
|
+
*/
|
|
1412
|
+
async prepareIntent(requestBody) {
|
|
1413
|
+
try {
|
|
1414
|
+
const response = await fetch(`${this.config.providerUrl}/api/intent/prepare`, {
|
|
1415
|
+
method: "POST",
|
|
1416
|
+
headers: { "Content-Type": "application/json" },
|
|
1417
|
+
body: JSON.stringify(requestBody)
|
|
1418
|
+
});
|
|
1419
|
+
if (!response.ok) {
|
|
1420
|
+
const errorData = await response.json().catch(() => ({}));
|
|
1421
|
+
const errorMessage = errorData.error || "Failed to prepare intent";
|
|
1422
|
+
if (errorMessage.includes("User not found")) {
|
|
1423
|
+
localStorage.removeItem("1auth-user");
|
|
1424
|
+
}
|
|
1425
|
+
return {
|
|
1426
|
+
success: false,
|
|
1427
|
+
error: {
|
|
1428
|
+
code: errorMessage.includes("User not found") ? "USER_NOT_FOUND" : "PREPARE_FAILED",
|
|
1429
|
+
message: errorMessage
|
|
1430
|
+
}
|
|
1431
|
+
};
|
|
1432
|
+
}
|
|
1433
|
+
const tier = response.headers.get("X-Origin-Tier");
|
|
1434
|
+
return { success: true, data: await response.json(), tier };
|
|
1435
|
+
} catch (error) {
|
|
1436
|
+
return {
|
|
1437
|
+
success: false,
|
|
1438
|
+
error: {
|
|
1439
|
+
code: "NETWORK_ERROR",
|
|
1440
|
+
message: error instanceof Error ? error.message : "Network error"
|
|
1441
|
+
}
|
|
1442
|
+
};
|
|
1443
|
+
}
|
|
1444
|
+
}
|
|
1445
|
+
/**
|
|
1446
|
+
* Prepare a batch intent by calling the orchestrator for quotes on all intents.
|
|
1447
|
+
*/
|
|
1448
|
+
async prepareBatchIntent(requestBody) {
|
|
1449
|
+
try {
|
|
1450
|
+
const response = await fetch(`${this.config.providerUrl}/api/intent/batch-prepare`, {
|
|
1451
|
+
method: "POST",
|
|
1452
|
+
headers: { "Content-Type": "application/json" },
|
|
1453
|
+
body: JSON.stringify(requestBody)
|
|
1454
|
+
});
|
|
1455
|
+
if (!response.ok) {
|
|
1456
|
+
const errorData = await response.json().catch(() => ({}));
|
|
1457
|
+
const errorMessage = errorData.error || "Failed to prepare batch intent";
|
|
1458
|
+
if (errorMessage.includes("User not found")) {
|
|
1459
|
+
localStorage.removeItem("1auth-user");
|
|
1460
|
+
}
|
|
1461
|
+
return {
|
|
1462
|
+
success: false,
|
|
1463
|
+
error: errorMessage,
|
|
1464
|
+
failedIntents: errorData.failedIntents
|
|
1465
|
+
};
|
|
1466
|
+
}
|
|
1467
|
+
const tier = response.headers.get("X-Origin-Tier");
|
|
1468
|
+
return { success: true, data: await response.json(), tier };
|
|
1469
|
+
} catch {
|
|
1470
|
+
return { success: false, error: "Network error" };
|
|
1471
|
+
}
|
|
1472
|
+
}
|
|
1473
|
+
/**
|
|
1474
|
+
* Send a prepare error message to the dialog iframe.
|
|
1475
|
+
*/
|
|
1476
|
+
sendPrepareError(iframe, error) {
|
|
1477
|
+
const dialogOrigin = this.getDialogOrigin();
|
|
1478
|
+
iframe.contentWindow?.postMessage(
|
|
1479
|
+
{ type: "PASSKEY_PREPARE_ERROR", error },
|
|
1480
|
+
dialogOrigin
|
|
1481
|
+
);
|
|
1482
|
+
}
|
|
763
1483
|
/**
|
|
764
1484
|
* Poll for intent status
|
|
765
1485
|
*
|
|
@@ -771,9 +1491,7 @@ var OneAuthClient = class {
|
|
|
771
1491
|
const response = await fetch(
|
|
772
1492
|
`${this.config.providerUrl}/api/intent/status/${intentId}`,
|
|
773
1493
|
{
|
|
774
|
-
headers: {
|
|
775
|
-
"x-client-id": this.config.clientId
|
|
776
|
-
}
|
|
1494
|
+
headers: this.config.clientId ? { "x-client-id": this.config.clientId } : {}
|
|
777
1495
|
}
|
|
778
1496
|
);
|
|
779
1497
|
if (!response.ok) {
|
|
@@ -808,6 +1526,43 @@ var OneAuthClient = class {
|
|
|
808
1526
|
};
|
|
809
1527
|
}
|
|
810
1528
|
}
|
|
1529
|
+
/**
|
|
1530
|
+
* Get the history of intents for the authenticated user.
|
|
1531
|
+
*
|
|
1532
|
+
* Requires an active session (user must be logged in).
|
|
1533
|
+
*
|
|
1534
|
+
* @example
|
|
1535
|
+
* ```typescript
|
|
1536
|
+
* // Get recent intents
|
|
1537
|
+
* const history = await client.getIntentHistory({ limit: 10 });
|
|
1538
|
+
*
|
|
1539
|
+
* // Filter by status
|
|
1540
|
+
* const pending = await client.getIntentHistory({ status: 'pending' });
|
|
1541
|
+
*
|
|
1542
|
+
* // Filter by date range
|
|
1543
|
+
* const lastWeek = await client.getIntentHistory({
|
|
1544
|
+
* from: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString(),
|
|
1545
|
+
* });
|
|
1546
|
+
* ```
|
|
1547
|
+
*/
|
|
1548
|
+
async getIntentHistory(options) {
|
|
1549
|
+
const queryParams = new URLSearchParams();
|
|
1550
|
+
if (options?.limit) queryParams.set("limit", String(options.limit));
|
|
1551
|
+
if (options?.offset) queryParams.set("offset", String(options.offset));
|
|
1552
|
+
if (options?.status) queryParams.set("status", options.status);
|
|
1553
|
+
if (options?.from) queryParams.set("from", options.from);
|
|
1554
|
+
if (options?.to) queryParams.set("to", options.to);
|
|
1555
|
+
const url = `${this.config.providerUrl}/api/intent/history${queryParams.toString() ? `?${queryParams}` : ""}`;
|
|
1556
|
+
const response = await fetch(url, {
|
|
1557
|
+
headers: this.config.clientId ? { "x-client-id": this.config.clientId } : {},
|
|
1558
|
+
credentials: "include"
|
|
1559
|
+
});
|
|
1560
|
+
if (!response.ok) {
|
|
1561
|
+
const errorData = await response.json().catch(() => ({}));
|
|
1562
|
+
throw new Error(errorData.error || "Failed to get intent history");
|
|
1563
|
+
}
|
|
1564
|
+
return response.json();
|
|
1565
|
+
}
|
|
811
1566
|
/**
|
|
812
1567
|
* Send a swap intent through the Rhinestone orchestrator
|
|
813
1568
|
*
|
|
@@ -869,18 +1624,22 @@ var OneAuthClient = class {
|
|
|
869
1624
|
error: error instanceof Error ? error.message : `Unsupported ${label}: ${token} on chain ${options.targetChain}`
|
|
870
1625
|
};
|
|
871
1626
|
}
|
|
872
|
-
};
|
|
873
|
-
|
|
874
|
-
if (
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
1627
|
+
};
|
|
1628
|
+
let fromTokenAddress;
|
|
1629
|
+
if (options.fromToken) {
|
|
1630
|
+
const fromTokenResult = resolveToken(options.fromToken, "fromToken");
|
|
1631
|
+
if (!fromTokenResult.address) {
|
|
1632
|
+
return {
|
|
1633
|
+
success: false,
|
|
1634
|
+
intentId: "",
|
|
1635
|
+
status: "failed",
|
|
1636
|
+
error: {
|
|
1637
|
+
code: "INVALID_TOKEN",
|
|
1638
|
+
message: fromTokenResult.error || `Unknown fromToken: ${options.fromToken}`
|
|
1639
|
+
}
|
|
1640
|
+
};
|
|
1641
|
+
}
|
|
1642
|
+
fromTokenAddress = fromTokenResult.address;
|
|
884
1643
|
}
|
|
885
1644
|
const toTokenResult = resolveToken(options.toToken, "toToken");
|
|
886
1645
|
if (!toTokenResult.address) {
|
|
@@ -894,18 +1653,17 @@ var OneAuthClient = class {
|
|
|
894
1653
|
}
|
|
895
1654
|
};
|
|
896
1655
|
}
|
|
897
|
-
const fromTokenAddress = fromTokenResult.address;
|
|
898
1656
|
const toTokenAddress = toTokenResult.address;
|
|
899
1657
|
console.log("[SDK sendSwap] Token resolution:", {
|
|
900
|
-
fromToken: options.fromToken,
|
|
901
|
-
fromTokenAddress,
|
|
1658
|
+
fromToken: options.fromToken ?? "Any",
|
|
1659
|
+
fromTokenAddress: fromTokenAddress ?? "orchestrator picks",
|
|
902
1660
|
toToken: options.toToken,
|
|
903
1661
|
toTokenAddress,
|
|
904
1662
|
targetChain: options.targetChain
|
|
905
1663
|
});
|
|
906
1664
|
const formatTokenLabel = (token, fallback) => {
|
|
907
1665
|
if (!token.startsWith("0x")) {
|
|
908
|
-
return token
|
|
1666
|
+
return token;
|
|
909
1667
|
}
|
|
910
1668
|
try {
|
|
911
1669
|
return getTokenSymbol(token, options.targetChain);
|
|
@@ -913,15 +1671,11 @@ var OneAuthClient = class {
|
|
|
913
1671
|
return fallback;
|
|
914
1672
|
}
|
|
915
1673
|
};
|
|
916
|
-
const fromSymbol = formatTokenLabel(
|
|
917
|
-
options.fromToken,
|
|
918
|
-
`${options.fromToken.slice(0, 6)}...${options.fromToken.slice(-4)}`
|
|
919
|
-
);
|
|
920
1674
|
const toSymbol = formatTokenLabel(
|
|
921
1675
|
options.toToken,
|
|
922
1676
|
`${options.toToken.slice(0, 6)}...${options.toToken.slice(-4)}`
|
|
923
1677
|
);
|
|
924
|
-
const isFromNativeEth = fromTokenAddress === "0x0000000000000000000000000000000000000000";
|
|
1678
|
+
const isFromNativeEth = fromTokenAddress ? fromTokenAddress === "0x0000000000000000000000000000000000000000" : false;
|
|
925
1679
|
const isToNativeEth = toTokenAddress === "0x0000000000000000000000000000000000000000";
|
|
926
1680
|
const KNOWN_DECIMALS = {
|
|
927
1681
|
ETH: 18,
|
|
@@ -931,31 +1685,33 @@ var OneAuthClient = class {
|
|
|
931
1685
|
USDT0: 6
|
|
932
1686
|
};
|
|
933
1687
|
const getDecimals = (symbol, chainId) => {
|
|
934
|
-
const upperSymbol = symbol.toUpperCase();
|
|
935
1688
|
try {
|
|
936
|
-
const
|
|
937
|
-
|
|
1689
|
+
const match = getSupportedTokens(chainId).find(
|
|
1690
|
+
(t) => t.symbol.toUpperCase() === symbol.toUpperCase()
|
|
1691
|
+
);
|
|
1692
|
+
if (match) {
|
|
1693
|
+
console.log(`[SDK] getTokenDecimals(${match.symbol}, ${chainId}) = ${match.decimals}`);
|
|
1694
|
+
return match.decimals;
|
|
1695
|
+
}
|
|
1696
|
+
const decimals = (0, import_sdk.getTokenDecimals)(symbol, chainId);
|
|
1697
|
+
console.log(`[SDK] getTokenDecimals(${symbol}, ${chainId}) = ${decimals}`);
|
|
938
1698
|
return decimals;
|
|
939
1699
|
} catch (e) {
|
|
940
|
-
|
|
1700
|
+
const upperSymbol = symbol.toUpperCase();
|
|
1701
|
+
console.warn(`[SDK] getTokenDecimals failed for ${symbol}, using fallback`, e);
|
|
941
1702
|
return KNOWN_DECIMALS[upperSymbol] ?? 18;
|
|
942
1703
|
}
|
|
943
1704
|
};
|
|
944
|
-
const fromDecimals = getDecimals(options.fromToken, options.targetChain);
|
|
945
1705
|
const toDecimals = getDecimals(options.toToken, options.targetChain);
|
|
946
|
-
const isBridge = options.fromToken.toUpperCase() === options.toToken.toUpperCase();
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
amount: (0, import_viem2.parseUnits)(options.amount, toDecimals).toString()
|
|
952
|
-
}];
|
|
953
|
-
}
|
|
1706
|
+
const isBridge = options.fromToken ? options.fromToken.toUpperCase() === options.toToken.toUpperCase() : false;
|
|
1707
|
+
const tokenRequests = [{
|
|
1708
|
+
token: toTokenAddress,
|
|
1709
|
+
amount: (0, import_viem2.parseUnits)(options.amount, toDecimals)
|
|
1710
|
+
}];
|
|
954
1711
|
console.log("[SDK sendSwap] Building intent:", {
|
|
955
1712
|
isBridge,
|
|
956
1713
|
isFromNativeEth,
|
|
957
1714
|
isToNativeEth,
|
|
958
|
-
fromDecimals,
|
|
959
1715
|
toDecimals,
|
|
960
1716
|
tokenRequests
|
|
961
1717
|
});
|
|
@@ -966,15 +1722,20 @@ var OneAuthClient = class {
|
|
|
966
1722
|
{
|
|
967
1723
|
// Minimal call - just signals to orchestrator we want the tokenRequests delivered
|
|
968
1724
|
to: toTokenAddress,
|
|
969
|
-
value: "0"
|
|
1725
|
+
value: "0",
|
|
1726
|
+
// SDK provides labels so dialog shows "Buy ETH" not "Send ETH / To: 0x000..."
|
|
1727
|
+
label: `Buy ${toSymbol}`,
|
|
1728
|
+
sublabel: `${options.amount} ${toSymbol}`
|
|
970
1729
|
}
|
|
971
1730
|
],
|
|
972
1731
|
// Request specific output tokens - this is what actually matters for swaps
|
|
973
1732
|
tokenRequests,
|
|
974
1733
|
// Constrain orchestrator to use only the fromToken as input
|
|
975
1734
|
// This ensures the swap uses the correct source token
|
|
976
|
-
//
|
|
977
|
-
sourceAssets: options.sourceAssets || [options.fromToken
|
|
1735
|
+
// Use canonical symbol casing from registry (e.g. "MockUSD" not "MOCKUSD")
|
|
1736
|
+
sourceAssets: options.sourceAssets || (options.fromToken ? [options.fromToken] : void 0),
|
|
1737
|
+
// Pass source chain ID so orchestrator knows which chain to look for tokens on
|
|
1738
|
+
sourceChainId: options.sourceChainId,
|
|
978
1739
|
closeOn: options.closeOn || "preconfirmed",
|
|
979
1740
|
waitForHash: options.waitForHash,
|
|
980
1741
|
hashTimeoutMs: options.hashTimeoutMs,
|
|
@@ -983,7 +1744,7 @@ var OneAuthClient = class {
|
|
|
983
1744
|
return {
|
|
984
1745
|
...result,
|
|
985
1746
|
quote: result.success ? {
|
|
986
|
-
fromToken: fromTokenAddress,
|
|
1747
|
+
fromToken: fromTokenAddress ?? options.fromToken,
|
|
987
1748
|
toToken: toTokenAddress,
|
|
988
1749
|
amountIn: options.amount,
|
|
989
1750
|
amountOut: "",
|
|
@@ -1025,26 +1786,24 @@ var OneAuthClient = class {
|
|
|
1025
1786
|
const themeParams = this.getThemeParams(options?.theme);
|
|
1026
1787
|
const signingUrl = `${dialogUrl}/dialog/sign?mode=iframe${themeParams ? `&${themeParams}` : ""}`;
|
|
1027
1788
|
const { dialog, iframe, cleanup } = this.createModalDialog(signingUrl);
|
|
1028
|
-
const
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
resolve();
|
|
1789
|
+
const ready = await this.waitForDialogReady(dialog, iframe, cleanup, {
|
|
1790
|
+
mode: "iframe",
|
|
1791
|
+
message: options.message,
|
|
1792
|
+
challenge: options.challenge || options.message,
|
|
1793
|
+
username: options.username,
|
|
1794
|
+
accountAddress: options.accountAddress,
|
|
1795
|
+
description: options.description,
|
|
1796
|
+
metadata: options.metadata
|
|
1797
|
+
});
|
|
1798
|
+
if (!ready) {
|
|
1799
|
+
return {
|
|
1800
|
+
success: false,
|
|
1801
|
+
error: {
|
|
1802
|
+
code: "USER_REJECTED",
|
|
1803
|
+
message: "User closed the dialog"
|
|
1044
1804
|
}
|
|
1045
1805
|
};
|
|
1046
|
-
|
|
1047
|
-
});
|
|
1806
|
+
}
|
|
1048
1807
|
const signingResult = await this.waitForSigningResponse(dialog, iframe, cleanup);
|
|
1049
1808
|
cleanup();
|
|
1050
1809
|
if (signingResult.success) {
|
|
@@ -1113,31 +1872,29 @@ var OneAuthClient = class {
|
|
|
1113
1872
|
const themeParams = this.getThemeParams(options?.theme);
|
|
1114
1873
|
const signingUrl = `${dialogUrl}/dialog/sign?mode=iframe${themeParams ? `&${themeParams}` : ""}`;
|
|
1115
1874
|
const { dialog, iframe, cleanup } = this.createModalDialog(signingUrl);
|
|
1116
|
-
const
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
resolve();
|
|
1875
|
+
const ready = await this.waitForDialogReady(dialog, iframe, cleanup, {
|
|
1876
|
+
mode: "iframe",
|
|
1877
|
+
signingMode: "typedData",
|
|
1878
|
+
typedData: {
|
|
1879
|
+
domain: options.domain,
|
|
1880
|
+
types: options.types,
|
|
1881
|
+
primaryType: options.primaryType,
|
|
1882
|
+
message: options.message
|
|
1883
|
+
},
|
|
1884
|
+
challenge: signedHash,
|
|
1885
|
+
username: options.username,
|
|
1886
|
+
accountAddress: options.accountAddress,
|
|
1887
|
+
description: options.description
|
|
1888
|
+
});
|
|
1889
|
+
if (!ready) {
|
|
1890
|
+
return {
|
|
1891
|
+
success: false,
|
|
1892
|
+
error: {
|
|
1893
|
+
code: "USER_REJECTED",
|
|
1894
|
+
message: "User closed the dialog"
|
|
1137
1895
|
}
|
|
1138
1896
|
};
|
|
1139
|
-
|
|
1140
|
-
});
|
|
1897
|
+
}
|
|
1141
1898
|
const signingResult = await this.waitForSigningResponse(dialog, iframe, cleanup);
|
|
1142
1899
|
cleanup();
|
|
1143
1900
|
if (signingResult.success) {
|
|
@@ -1200,7 +1957,7 @@ var OneAuthClient = class {
|
|
|
1200
1957
|
iframe.style.borderRadius = "12px";
|
|
1201
1958
|
iframe.style.boxShadow = "0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)";
|
|
1202
1959
|
iframe.id = `passkey-embed-${requestId}`;
|
|
1203
|
-
iframe.allow = "publickey-credentials-get *; publickey-credentials-create
|
|
1960
|
+
iframe.allow = "publickey-credentials-get *; publickey-credentials-create *; identity-credentials-get";
|
|
1204
1961
|
iframe.onload = () => {
|
|
1205
1962
|
options.onReady?.();
|
|
1206
1963
|
};
|
|
@@ -1291,9 +2048,7 @@ var OneAuthClient = class {
|
|
|
1291
2048
|
const response = await fetch(
|
|
1292
2049
|
`${this.config.providerUrl}/api/users/${encodeURIComponent(username)}/passkeys`,
|
|
1293
2050
|
{
|
|
1294
|
-
headers: {
|
|
1295
|
-
"x-client-id": this.config.clientId
|
|
1296
|
-
}
|
|
2051
|
+
headers: this.config.clientId ? { "x-client-id": this.config.clientId } : {}
|
|
1297
2052
|
}
|
|
1298
2053
|
);
|
|
1299
2054
|
if (!response.ok) {
|
|
@@ -1312,7 +2067,7 @@ var OneAuthClient = class {
|
|
|
1312
2067
|
"Content-Type": "application/json"
|
|
1313
2068
|
},
|
|
1314
2069
|
body: JSON.stringify({
|
|
1315
|
-
clientId: this.config.clientId,
|
|
2070
|
+
...this.config.clientId && { clientId: this.config.clientId },
|
|
1316
2071
|
username: options.username,
|
|
1317
2072
|
challenge: options.challenge,
|
|
1318
2073
|
description: options.description,
|
|
@@ -1338,6 +2093,50 @@ var OneAuthClient = class {
|
|
|
1338
2093
|
`width=${POPUP_WIDTH},height=${POPUP_HEIGHT},left=${left},top=${top},popup=true`
|
|
1339
2094
|
);
|
|
1340
2095
|
}
|
|
2096
|
+
/**
|
|
2097
|
+
* Wait for the dialog iframe to signal ready, then send init data.
|
|
2098
|
+
* Also handles early close (X button, escape, backdrop) during the ready phase.
|
|
2099
|
+
* Returns true if dialog is ready, false if it was closed before becoming ready.
|
|
2100
|
+
*/
|
|
2101
|
+
waitForDialogReady(dialog, iframe, cleanup, initMessage) {
|
|
2102
|
+
const dialogOrigin = this.getDialogOrigin();
|
|
2103
|
+
return new Promise((resolve) => {
|
|
2104
|
+
let settled = false;
|
|
2105
|
+
const teardown = () => {
|
|
2106
|
+
if (settled) return;
|
|
2107
|
+
settled = true;
|
|
2108
|
+
clearTimeout(readyTimeout);
|
|
2109
|
+
window.removeEventListener("message", handleMessage);
|
|
2110
|
+
dialog.removeEventListener("close", handleClose);
|
|
2111
|
+
};
|
|
2112
|
+
const handleMessage = (event) => {
|
|
2113
|
+
if (event.origin !== dialogOrigin) return;
|
|
2114
|
+
if (event.data?.type === "PASSKEY_READY") {
|
|
2115
|
+
teardown();
|
|
2116
|
+
iframe.contentWindow?.postMessage({
|
|
2117
|
+
type: "PASSKEY_INIT",
|
|
2118
|
+
...initMessage
|
|
2119
|
+
}, dialogOrigin);
|
|
2120
|
+
resolve(true);
|
|
2121
|
+
} else if (event.data?.type === "PASSKEY_CLOSE") {
|
|
2122
|
+
teardown();
|
|
2123
|
+
cleanup();
|
|
2124
|
+
resolve(false);
|
|
2125
|
+
}
|
|
2126
|
+
};
|
|
2127
|
+
const handleClose = () => {
|
|
2128
|
+
teardown();
|
|
2129
|
+
resolve(false);
|
|
2130
|
+
};
|
|
2131
|
+
const readyTimeout = setTimeout(() => {
|
|
2132
|
+
teardown();
|
|
2133
|
+
cleanup();
|
|
2134
|
+
resolve(false);
|
|
2135
|
+
}, 1e4);
|
|
2136
|
+
window.addEventListener("message", handleMessage);
|
|
2137
|
+
dialog.addEventListener("close", handleClose);
|
|
2138
|
+
});
|
|
2139
|
+
}
|
|
1341
2140
|
/**
|
|
1342
2141
|
* Create a modal dialog with an iframe inside.
|
|
1343
2142
|
*/
|
|
@@ -1374,7 +2173,9 @@ var OneAuthClient = class {
|
|
|
1374
2173
|
border-radius: 14px;
|
|
1375
2174
|
border: none;
|
|
1376
2175
|
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.12), 0 2px 8px rgba(0, 0, 0, 0.08);
|
|
1377
|
-
transition:
|
|
2176
|
+
transition: height 0.15s ease-out;
|
|
2177
|
+
max-height: calc(100vh - 100px);
|
|
2178
|
+
max-height: calc(100dvh - 100px);
|
|
1378
2179
|
}
|
|
1379
2180
|
|
|
1380
2181
|
@media (min-width: 769px) {
|
|
@@ -1436,14 +2237,10 @@ var OneAuthClient = class {
|
|
|
1436
2237
|
const iframe = document.createElement("iframe");
|
|
1437
2238
|
iframe.setAttribute(
|
|
1438
2239
|
"allow",
|
|
1439
|
-
"publickey-credentials-get *; publickey-credentials-create *; clipboard-write"
|
|
2240
|
+
"publickey-credentials-get *; publickey-credentials-create *; clipboard-write; identity-credentials-get"
|
|
1440
2241
|
);
|
|
1441
2242
|
iframe.setAttribute("aria-label", "Passkey Authentication");
|
|
1442
2243
|
iframe.setAttribute("tabindex", "0");
|
|
1443
|
-
iframe.setAttribute(
|
|
1444
|
-
"sandbox",
|
|
1445
|
-
"allow-forms allow-scripts allow-same-origin allow-popups allow-popups-to-escape-sandbox"
|
|
1446
|
-
);
|
|
1447
2244
|
iframe.setAttribute("src", url);
|
|
1448
2245
|
iframe.setAttribute("title", "Passkey");
|
|
1449
2246
|
iframe.style.border = "none";
|
|
@@ -1453,10 +2250,8 @@ var OneAuthClient = class {
|
|
|
1453
2250
|
const handleMessage = (event) => {
|
|
1454
2251
|
if (event.origin !== hostUrl.origin) return;
|
|
1455
2252
|
if (event.data?.type === "PASSKEY_RESIZE") {
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
iframe.style.width = `${event.data.width}px`;
|
|
1459
|
-
}
|
|
2253
|
+
const maxHeight = window.innerHeight - 100;
|
|
2254
|
+
iframe.style.height = `${Math.min(event.data.height, maxHeight)}px`;
|
|
1460
2255
|
} else if (event.data?.type === "PASSKEY_DISCONNECT") {
|
|
1461
2256
|
localStorage.removeItem("1auth-user");
|
|
1462
2257
|
}
|
|
@@ -1474,7 +2269,10 @@ var OneAuthClient = class {
|
|
|
1474
2269
|
}
|
|
1475
2270
|
});
|
|
1476
2271
|
dialog.showModal();
|
|
2272
|
+
let cleanedUp = false;
|
|
1477
2273
|
const cleanup = () => {
|
|
2274
|
+
if (cleanedUp) return;
|
|
2275
|
+
cleanedUp = true;
|
|
1478
2276
|
window.removeEventListener("message", handleMessage);
|
|
1479
2277
|
document.removeEventListener("keydown", handleEscape);
|
|
1480
2278
|
dialog.close();
|
|
@@ -1482,12 +2280,24 @@ var OneAuthClient = class {
|
|
|
1482
2280
|
};
|
|
1483
2281
|
return { dialog, iframe, cleanup };
|
|
1484
2282
|
}
|
|
1485
|
-
waitForModalAuthResponse(_dialog,
|
|
2283
|
+
waitForModalAuthResponse(_dialog, iframe, cleanup) {
|
|
1486
2284
|
const dialogOrigin = this.getDialogOrigin();
|
|
1487
2285
|
return new Promise((resolve) => {
|
|
2286
|
+
let dialogReady = false;
|
|
1488
2287
|
const handleMessage = (event) => {
|
|
1489
2288
|
if (event.origin !== dialogOrigin) return;
|
|
1490
2289
|
const data = event.data;
|
|
2290
|
+
if (data?.type === "PASSKEY_READY") {
|
|
2291
|
+
dialogReady = true;
|
|
2292
|
+
iframe.contentWindow?.postMessage({
|
|
2293
|
+
type: "PASSKEY_INIT",
|
|
2294
|
+
mode: "iframe"
|
|
2295
|
+
}, dialogOrigin);
|
|
2296
|
+
return;
|
|
2297
|
+
}
|
|
2298
|
+
if (!dialogReady && data?.type === "PASSKEY_CLOSE") {
|
|
2299
|
+
return;
|
|
2300
|
+
}
|
|
1491
2301
|
if (data?.type === "PASSKEY_LOGIN_RESULT") {
|
|
1492
2302
|
window.removeEventListener("message", handleMessage);
|
|
1493
2303
|
cleanup();
|
|
@@ -1495,6 +2305,7 @@ var OneAuthClient = class {
|
|
|
1495
2305
|
resolve({
|
|
1496
2306
|
success: true,
|
|
1497
2307
|
username: data.data?.username,
|
|
2308
|
+
address: data.data?.address,
|
|
1498
2309
|
user: data.data?.user
|
|
1499
2310
|
});
|
|
1500
2311
|
} else {
|
|
@@ -1509,7 +2320,8 @@ var OneAuthClient = class {
|
|
|
1509
2320
|
if (data.success) {
|
|
1510
2321
|
resolve({
|
|
1511
2322
|
success: true,
|
|
1512
|
-
username: data.data?.username
|
|
2323
|
+
username: data.data?.username,
|
|
2324
|
+
address: data.data?.address
|
|
1513
2325
|
});
|
|
1514
2326
|
} else {
|
|
1515
2327
|
resolve({
|
|
@@ -1517,6 +2329,11 @@ var OneAuthClient = class {
|
|
|
1517
2329
|
error: data.error
|
|
1518
2330
|
});
|
|
1519
2331
|
}
|
|
2332
|
+
} else if (data?.type === "PASSKEY_RETRY_POPUP") {
|
|
2333
|
+
window.removeEventListener("message", handleMessage);
|
|
2334
|
+
cleanup();
|
|
2335
|
+
const popupUrl = data.data?.url?.replace("mode=iframe", "mode=popup") || `${this.getDialogUrl()}/dialog/auth?mode=popup${this.config.clientId ? `&clientId=${this.config.clientId}` : ""}`;
|
|
2336
|
+
this.waitForPopupAuthResponse(popupUrl).then(resolve);
|
|
1520
2337
|
} else if (data?.type === "PASSKEY_CLOSE") {
|
|
1521
2338
|
window.removeEventListener("message", handleMessage);
|
|
1522
2339
|
cleanup();
|
|
@@ -1532,6 +2349,79 @@ var OneAuthClient = class {
|
|
|
1532
2349
|
window.addEventListener("message", handleMessage);
|
|
1533
2350
|
});
|
|
1534
2351
|
}
|
|
2352
|
+
/**
|
|
2353
|
+
* Open a popup for auth and wait for the result.
|
|
2354
|
+
* Used when iframe mode fails (e.g., due to password manager interference).
|
|
2355
|
+
*/
|
|
2356
|
+
waitForPopupAuthResponse(url) {
|
|
2357
|
+
const dialogOrigin = this.getDialogOrigin();
|
|
2358
|
+
const popup = this.openPopup(url);
|
|
2359
|
+
return new Promise((resolve) => {
|
|
2360
|
+
const pollTimer = setInterval(() => {
|
|
2361
|
+
if (popup?.closed) {
|
|
2362
|
+
clearInterval(pollTimer);
|
|
2363
|
+
window.removeEventListener("message", handleMessage);
|
|
2364
|
+
resolve({
|
|
2365
|
+
success: false,
|
|
2366
|
+
error: {
|
|
2367
|
+
code: "USER_CANCELLED",
|
|
2368
|
+
message: "Authentication was cancelled"
|
|
2369
|
+
}
|
|
2370
|
+
});
|
|
2371
|
+
}
|
|
2372
|
+
}, 500);
|
|
2373
|
+
const handleMessage = (event) => {
|
|
2374
|
+
if (event.origin !== dialogOrigin) return;
|
|
2375
|
+
const data = event.data;
|
|
2376
|
+
if (data?.type === "PASSKEY_LOGIN_RESULT") {
|
|
2377
|
+
clearInterval(pollTimer);
|
|
2378
|
+
window.removeEventListener("message", handleMessage);
|
|
2379
|
+
popup?.close();
|
|
2380
|
+
if (data.success) {
|
|
2381
|
+
resolve({
|
|
2382
|
+
success: true,
|
|
2383
|
+
username: data.data?.username,
|
|
2384
|
+
address: data.data?.address,
|
|
2385
|
+
user: data.data?.user
|
|
2386
|
+
});
|
|
2387
|
+
} else {
|
|
2388
|
+
resolve({
|
|
2389
|
+
success: false,
|
|
2390
|
+
error: data.error
|
|
2391
|
+
});
|
|
2392
|
+
}
|
|
2393
|
+
} else if (data?.type === "PASSKEY_REGISTER_RESULT") {
|
|
2394
|
+
clearInterval(pollTimer);
|
|
2395
|
+
window.removeEventListener("message", handleMessage);
|
|
2396
|
+
popup?.close();
|
|
2397
|
+
if (data.success) {
|
|
2398
|
+
resolve({
|
|
2399
|
+
success: true,
|
|
2400
|
+
username: data.data?.username,
|
|
2401
|
+
address: data.data?.address
|
|
2402
|
+
});
|
|
2403
|
+
} else {
|
|
2404
|
+
resolve({
|
|
2405
|
+
success: false,
|
|
2406
|
+
error: data.error
|
|
2407
|
+
});
|
|
2408
|
+
}
|
|
2409
|
+
} else if (data?.type === "PASSKEY_CLOSE") {
|
|
2410
|
+
clearInterval(pollTimer);
|
|
2411
|
+
window.removeEventListener("message", handleMessage);
|
|
2412
|
+
popup?.close();
|
|
2413
|
+
resolve({
|
|
2414
|
+
success: false,
|
|
2415
|
+
error: {
|
|
2416
|
+
code: "USER_CANCELLED",
|
|
2417
|
+
message: "Authentication was cancelled"
|
|
2418
|
+
}
|
|
2419
|
+
});
|
|
2420
|
+
}
|
|
2421
|
+
};
|
|
2422
|
+
window.addEventListener("message", handleMessage);
|
|
2423
|
+
});
|
|
2424
|
+
}
|
|
1535
2425
|
waitForAuthenticateResponse(_dialog, _iframe, cleanup) {
|
|
1536
2426
|
const dialogOrigin = this.getDialogOrigin();
|
|
1537
2427
|
return new Promise((resolve) => {
|
|
@@ -1571,6 +2461,84 @@ var OneAuthClient = class {
|
|
|
1571
2461
|
window.addEventListener("message", handleMessage);
|
|
1572
2462
|
});
|
|
1573
2463
|
}
|
|
2464
|
+
waitForConnectResponse(_dialog, _iframe, cleanup) {
|
|
2465
|
+
const dialogOrigin = this.getDialogOrigin();
|
|
2466
|
+
return new Promise((resolve) => {
|
|
2467
|
+
const handleMessage = (event) => {
|
|
2468
|
+
if (event.origin !== dialogOrigin) return;
|
|
2469
|
+
const data = event.data;
|
|
2470
|
+
if (data?.type === "PASSKEY_CONNECT_RESULT") {
|
|
2471
|
+
window.removeEventListener("message", handleMessage);
|
|
2472
|
+
cleanup();
|
|
2473
|
+
if (data.success) {
|
|
2474
|
+
resolve({
|
|
2475
|
+
success: true,
|
|
2476
|
+
username: data.data?.username,
|
|
2477
|
+
address: data.data?.address,
|
|
2478
|
+
autoConnected: data.data?.autoConnected
|
|
2479
|
+
});
|
|
2480
|
+
} else {
|
|
2481
|
+
resolve({
|
|
2482
|
+
success: false,
|
|
2483
|
+
action: data.data?.action,
|
|
2484
|
+
error: data.error
|
|
2485
|
+
});
|
|
2486
|
+
}
|
|
2487
|
+
} else if (data?.type === "PASSKEY_CLOSE") {
|
|
2488
|
+
window.removeEventListener("message", handleMessage);
|
|
2489
|
+
cleanup();
|
|
2490
|
+
resolve({
|
|
2491
|
+
success: false,
|
|
2492
|
+
action: "cancel",
|
|
2493
|
+
error: {
|
|
2494
|
+
code: "USER_CANCELLED",
|
|
2495
|
+
message: "Connection was cancelled"
|
|
2496
|
+
}
|
|
2497
|
+
});
|
|
2498
|
+
}
|
|
2499
|
+
};
|
|
2500
|
+
window.addEventListener("message", handleMessage);
|
|
2501
|
+
});
|
|
2502
|
+
}
|
|
2503
|
+
waitForConsentResponse(_dialog, _iframe, cleanup) {
|
|
2504
|
+
const dialogOrigin = this.getDialogOrigin();
|
|
2505
|
+
return new Promise((resolve) => {
|
|
2506
|
+
const handleMessage = (event) => {
|
|
2507
|
+
if (event.origin !== dialogOrigin) return;
|
|
2508
|
+
const data = event.data;
|
|
2509
|
+
if (data?.type === "PASSKEY_CONSENT_RESULT") {
|
|
2510
|
+
window.removeEventListener("message", handleMessage);
|
|
2511
|
+
cleanup();
|
|
2512
|
+
if (data.success) {
|
|
2513
|
+
resolve({
|
|
2514
|
+
success: true,
|
|
2515
|
+
data: data.data,
|
|
2516
|
+
grantedAt: data.data?.grantedAt
|
|
2517
|
+
});
|
|
2518
|
+
} else {
|
|
2519
|
+
resolve({
|
|
2520
|
+
success: false,
|
|
2521
|
+
error: data.error ?? {
|
|
2522
|
+
code: "USER_REJECTED",
|
|
2523
|
+
message: "User denied the consent request"
|
|
2524
|
+
}
|
|
2525
|
+
});
|
|
2526
|
+
}
|
|
2527
|
+
} else if (data?.type === "PASSKEY_CLOSE") {
|
|
2528
|
+
window.removeEventListener("message", handleMessage);
|
|
2529
|
+
cleanup();
|
|
2530
|
+
resolve({
|
|
2531
|
+
success: false,
|
|
2532
|
+
error: {
|
|
2533
|
+
code: "USER_CANCELLED",
|
|
2534
|
+
message: "User closed the dialog"
|
|
2535
|
+
}
|
|
2536
|
+
});
|
|
2537
|
+
}
|
|
2538
|
+
};
|
|
2539
|
+
window.addEventListener("message", handleMessage);
|
|
2540
|
+
});
|
|
2541
|
+
}
|
|
1574
2542
|
waitForModalSigningResponse(requestId, _dialog, _iframe, cleanup) {
|
|
1575
2543
|
const dialogOrigin = this.getDialogOrigin();
|
|
1576
2544
|
return new Promise((resolve) => {
|
|
@@ -1659,9 +2627,7 @@ var OneAuthClient = class {
|
|
|
1659
2627
|
const response = await fetch(
|
|
1660
2628
|
`${this.config.providerUrl}/api/sign/request/${requestId}`,
|
|
1661
2629
|
{
|
|
1662
|
-
headers: {
|
|
1663
|
-
"x-client-id": this.config.clientId
|
|
1664
|
-
}
|
|
2630
|
+
headers: this.config.clientId ? { "x-client-id": this.config.clientId } : {}
|
|
1665
2631
|
}
|
|
1666
2632
|
);
|
|
1667
2633
|
if (!response.ok) {
|
|
@@ -1765,7 +2731,7 @@ function buildTransactionReview(calls) {
|
|
|
1765
2731
|
|
|
1766
2732
|
// src/provider.ts
|
|
1767
2733
|
var DEFAULT_STORAGE_KEY = "1auth-user";
|
|
1768
|
-
function
|
|
2734
|
+
function createOneAuthProvider(options) {
|
|
1769
2735
|
const { client } = options;
|
|
1770
2736
|
let chainId = options.chainId;
|
|
1771
2737
|
const storageKey = options.storageKey || DEFAULT_STORAGE_KEY;
|
|
@@ -1781,7 +2747,7 @@ function createPasskeyProvider(options) {
|
|
|
1781
2747
|
const raw = localStorage.getItem(storageKey);
|
|
1782
2748
|
if (!raw) return null;
|
|
1783
2749
|
const parsed = JSON.parse(raw);
|
|
1784
|
-
if (!parsed?.
|
|
2750
|
+
if (!parsed?.address) return null;
|
|
1785
2751
|
return parsed;
|
|
1786
2752
|
} catch {
|
|
1787
2753
|
return null;
|
|
@@ -1796,12 +2762,11 @@ function createPasskeyProvider(options) {
|
|
|
1796
2762
|
localStorage.removeItem(storageKey);
|
|
1797
2763
|
};
|
|
1798
2764
|
const resolveAccountAddress = async (username) => {
|
|
2765
|
+
const clientId = client.getClientId();
|
|
1799
2766
|
const response = await fetch(
|
|
1800
2767
|
`${client.getProviderUrl()}/api/users/${encodeURIComponent(username)}/account`,
|
|
1801
2768
|
{
|
|
1802
|
-
headers: {
|
|
1803
|
-
"x-client-id": client.getClientId()
|
|
1804
|
-
}
|
|
2769
|
+
headers: clientId ? { "x-client-id": clientId } : {}
|
|
1805
2770
|
}
|
|
1806
2771
|
);
|
|
1807
2772
|
if (!response.ok) {
|
|
@@ -1816,12 +2781,29 @@ function createPasskeyProvider(options) {
|
|
|
1816
2781
|
if (stored) {
|
|
1817
2782
|
return [stored.address];
|
|
1818
2783
|
}
|
|
1819
|
-
const
|
|
1820
|
-
|
|
1821
|
-
|
|
2784
|
+
const connectResult = await client.connectWithModal();
|
|
2785
|
+
let username;
|
|
2786
|
+
let address;
|
|
2787
|
+
if (connectResult.success) {
|
|
2788
|
+
username = connectResult.username;
|
|
2789
|
+
address = connectResult.address;
|
|
2790
|
+
} else if (connectResult.action === "switch") {
|
|
2791
|
+
const authResult = await client.authWithModal();
|
|
2792
|
+
if (!authResult.success) {
|
|
2793
|
+
throw new Error(authResult.error?.message || "Authentication failed");
|
|
2794
|
+
}
|
|
2795
|
+
username = authResult.username;
|
|
2796
|
+
address = authResult.address;
|
|
2797
|
+
} else {
|
|
2798
|
+
throw new Error(connectResult.error?.message || "Connection cancelled");
|
|
2799
|
+
}
|
|
2800
|
+
if (!address && username) {
|
|
2801
|
+
address = await resolveAccountAddress(username);
|
|
1822
2802
|
}
|
|
1823
|
-
|
|
1824
|
-
|
|
2803
|
+
if (!address) {
|
|
2804
|
+
throw new Error("No account address available");
|
|
2805
|
+
}
|
|
2806
|
+
setStoredUser({ username, address });
|
|
1825
2807
|
emit("accountsChanged", [address]);
|
|
1826
2808
|
emit("connect", { chainId: (0, import_viem4.numberToHex)(chainId) });
|
|
1827
2809
|
return [address];
|
|
@@ -1835,11 +2817,11 @@ function createPasskeyProvider(options) {
|
|
|
1835
2817
|
const stored = getStoredUser();
|
|
1836
2818
|
if (stored) return stored;
|
|
1837
2819
|
const [address] = await connect();
|
|
1838
|
-
|
|
1839
|
-
if (!username || !address) {
|
|
2820
|
+
if (!address) {
|
|
1840
2821
|
throw new Error("Failed to resolve user session");
|
|
1841
2822
|
}
|
|
1842
|
-
|
|
2823
|
+
const user = getStoredUser();
|
|
2824
|
+
return user || { address };
|
|
1843
2825
|
};
|
|
1844
2826
|
const parseChainId = (value) => {
|
|
1845
2827
|
if (typeof value === "number") return value;
|
|
@@ -1878,6 +2860,16 @@ function createPasskeyProvider(options) {
|
|
|
1878
2860
|
};
|
|
1879
2861
|
});
|
|
1880
2862
|
};
|
|
2863
|
+
const normalizeTokenRequests = (requests) => {
|
|
2864
|
+
if (!Array.isArray(requests)) return void 0;
|
|
2865
|
+
return requests.map((r) => {
|
|
2866
|
+
const req = r;
|
|
2867
|
+
return {
|
|
2868
|
+
token: req.token,
|
|
2869
|
+
amount: typeof req.amount === "bigint" ? req.amount : BigInt(String(req.amount || "0"))
|
|
2870
|
+
};
|
|
2871
|
+
});
|
|
2872
|
+
};
|
|
1881
2873
|
const decodeMessage = (value) => {
|
|
1882
2874
|
if (!(0, import_viem4.isHex)(value)) return value;
|
|
1883
2875
|
try {
|
|
@@ -1887,9 +2879,13 @@ function createPasskeyProvider(options) {
|
|
|
1887
2879
|
}
|
|
1888
2880
|
};
|
|
1889
2881
|
const signMessage = async (message) => {
|
|
1890
|
-
const
|
|
2882
|
+
const user = await ensureUser();
|
|
2883
|
+
if (!user.username && !user.address) {
|
|
2884
|
+
throw new Error("Username or address required for signing.");
|
|
2885
|
+
}
|
|
1891
2886
|
const result = await client.signMessage({
|
|
1892
|
-
username,
|
|
2887
|
+
username: user.username,
|
|
2888
|
+
accountAddress: user.address,
|
|
1893
2889
|
message
|
|
1894
2890
|
});
|
|
1895
2891
|
if (!result.success || !result.signature) {
|
|
@@ -1898,10 +2894,14 @@ function createPasskeyProvider(options) {
|
|
|
1898
2894
|
return encodeWebAuthnSignature(result.signature);
|
|
1899
2895
|
};
|
|
1900
2896
|
const signTypedData = async (typedData) => {
|
|
1901
|
-
const
|
|
2897
|
+
const user = await ensureUser();
|
|
2898
|
+
if (!user.username && !user.address) {
|
|
2899
|
+
throw new Error("Username or address required for signing.");
|
|
2900
|
+
}
|
|
1902
2901
|
const data = typeof typedData === "string" ? JSON.parse(typedData) : typedData;
|
|
1903
2902
|
const result = await client.signTypedData({
|
|
1904
|
-
username,
|
|
2903
|
+
username: user.username,
|
|
2904
|
+
accountAddress: user.address,
|
|
1905
2905
|
domain: data.domain,
|
|
1906
2906
|
types: data.types,
|
|
1907
2907
|
primaryType: data.primaryType,
|
|
@@ -1916,32 +2916,40 @@ function createPasskeyProvider(options) {
|
|
|
1916
2916
|
if (!options.signIntent) {
|
|
1917
2917
|
return {
|
|
1918
2918
|
username: payload.username,
|
|
2919
|
+
accountAddress: payload.accountAddress,
|
|
1919
2920
|
targetChain: payload.targetChain,
|
|
1920
|
-
calls: payload.calls
|
|
2921
|
+
calls: payload.calls,
|
|
2922
|
+
tokenRequests: payload.tokenRequests
|
|
1921
2923
|
};
|
|
1922
2924
|
}
|
|
2925
|
+
if (!payload.username) {
|
|
2926
|
+
throw new Error("Username required for signed intents. Set a username first.");
|
|
2927
|
+
}
|
|
1923
2928
|
const signedIntent = await options.signIntent({
|
|
1924
2929
|
username: payload.username,
|
|
1925
2930
|
accountAddress: payload.accountAddress,
|
|
1926
2931
|
targetChain: payload.targetChain,
|
|
1927
|
-
calls: payload.calls
|
|
2932
|
+
calls: payload.calls,
|
|
2933
|
+
tokenRequests: payload.tokenRequests
|
|
1928
2934
|
});
|
|
1929
2935
|
return { signedIntent };
|
|
1930
2936
|
};
|
|
1931
2937
|
const sendIntent = async (payload) => {
|
|
1932
|
-
const closeOn = options.waitForHash ?? true ? "completed" : "preconfirmed";
|
|
2938
|
+
const closeOn = options.closeOn ?? (options.waitForHash ?? true ? "completed" : "preconfirmed");
|
|
1933
2939
|
const intentPayload = await resolveIntentPayload(payload);
|
|
1934
2940
|
const result = await client.sendIntent({
|
|
1935
2941
|
...intentPayload,
|
|
2942
|
+
tokenRequests: payload.tokenRequests,
|
|
2943
|
+
sourceChainId: payload.sourceChainId,
|
|
1936
2944
|
closeOn,
|
|
1937
2945
|
waitForHash: options.waitForHash ?? true,
|
|
1938
2946
|
hashTimeoutMs: options.hashTimeoutMs,
|
|
1939
2947
|
hashIntervalMs: options.hashIntervalMs
|
|
1940
2948
|
});
|
|
1941
|
-
if (!result.success
|
|
2949
|
+
if (!result.success) {
|
|
1942
2950
|
throw new Error(result.error?.message || "Transaction failed");
|
|
1943
2951
|
}
|
|
1944
|
-
return result.
|
|
2952
|
+
return result.intentId;
|
|
1945
2953
|
};
|
|
1946
2954
|
const request = async ({ method, params }) => {
|
|
1947
2955
|
switch (method) {
|
|
@@ -1994,11 +3002,15 @@ function createPasskeyProvider(options) {
|
|
|
1994
3002
|
const user = await ensureUser();
|
|
1995
3003
|
const targetChain = parseChainId(tx.chainId) ?? chainId;
|
|
1996
3004
|
const calls = normalizeCalls([tx]);
|
|
3005
|
+
const tokenRequests = normalizeTokenRequests(tx.tokenRequests);
|
|
3006
|
+
const txSourceChainId = parseChainId(tx.sourceChainId);
|
|
1997
3007
|
return sendIntent({
|
|
1998
3008
|
username: user.username,
|
|
1999
3009
|
accountAddress: user.address,
|
|
2000
3010
|
targetChain,
|
|
2001
|
-
calls
|
|
3011
|
+
calls,
|
|
3012
|
+
tokenRequests,
|
|
3013
|
+
sourceChainId: txSourceChainId
|
|
2002
3014
|
});
|
|
2003
3015
|
}
|
|
2004
3016
|
case "wallet_sendCalls": {
|
|
@@ -2007,22 +3019,131 @@ function createPasskeyProvider(options) {
|
|
|
2007
3019
|
const user = await ensureUser();
|
|
2008
3020
|
const targetChain = parseChainId(payload.chainId) ?? chainId;
|
|
2009
3021
|
const calls = normalizeCalls(payload.calls || []);
|
|
3022
|
+
const tokenRequests = normalizeTokenRequests(payload.tokenRequests);
|
|
3023
|
+
const sourceChainId = parseChainId(payload.sourceChainId);
|
|
2010
3024
|
if (!calls.length) throw new Error("No calls provided");
|
|
2011
3025
|
return sendIntent({
|
|
2012
3026
|
username: user.username,
|
|
2013
3027
|
accountAddress: user.address,
|
|
2014
3028
|
targetChain,
|
|
2015
|
-
calls
|
|
3029
|
+
calls,
|
|
3030
|
+
tokenRequests,
|
|
3031
|
+
sourceChainId
|
|
2016
3032
|
});
|
|
2017
3033
|
}
|
|
2018
3034
|
case "wallet_getCapabilities": {
|
|
3035
|
+
const paramList = Array.isArray(params) ? params : [];
|
|
3036
|
+
const requestedChains = paramList[1];
|
|
2019
3037
|
const chainIds = getSupportedChainIds();
|
|
2020
|
-
const
|
|
2021
|
-
|
|
3038
|
+
const capabilities = {};
|
|
3039
|
+
for (const chainId2 of chainIds) {
|
|
3040
|
+
const hexChainId = `0x${chainId2.toString(16)}`;
|
|
3041
|
+
if (requestedChains && !requestedChains.includes(hexChainId)) {
|
|
3042
|
+
continue;
|
|
3043
|
+
}
|
|
3044
|
+
capabilities[hexChainId] = {
|
|
3045
|
+
atomic: { status: "supported" },
|
|
3046
|
+
paymasterService: { supported: true },
|
|
3047
|
+
auxiliaryFunds: { supported: true }
|
|
3048
|
+
};
|
|
3049
|
+
}
|
|
3050
|
+
return capabilities;
|
|
3051
|
+
}
|
|
3052
|
+
case "wallet_getAssets": {
|
|
3053
|
+
const user = await ensureUser();
|
|
3054
|
+
if (!user.username) {
|
|
3055
|
+
throw new Error("Username required to fetch assets. Set a username first.");
|
|
3056
|
+
}
|
|
3057
|
+
const clientId = client.getClientId();
|
|
3058
|
+
const response = await fetch(
|
|
3059
|
+
`${client.getProviderUrl()}/api/users/${encodeURIComponent(user.username)}/portfolio`,
|
|
3060
|
+
{
|
|
3061
|
+
headers: clientId ? { "x-client-id": clientId } : {}
|
|
3062
|
+
}
|
|
3063
|
+
);
|
|
3064
|
+
if (!response.ok) {
|
|
3065
|
+
const data = await response.json().catch(() => ({}));
|
|
3066
|
+
throw new Error(data.error || "Failed to get assets");
|
|
3067
|
+
}
|
|
3068
|
+
return response.json();
|
|
3069
|
+
}
|
|
3070
|
+
case "wallet_getCallsStatus": {
|
|
3071
|
+
const paramList = Array.isArray(params) ? params : [];
|
|
3072
|
+
const callsId = paramList[0];
|
|
3073
|
+
if (!callsId) {
|
|
3074
|
+
throw new Error("callsId is required");
|
|
3075
|
+
}
|
|
3076
|
+
const statusClientId = client.getClientId();
|
|
3077
|
+
const response = await fetch(
|
|
3078
|
+
`${client.getProviderUrl()}/api/intent/status/${encodeURIComponent(callsId)}`,
|
|
3079
|
+
{
|
|
3080
|
+
headers: statusClientId ? { "x-client-id": statusClientId } : {}
|
|
3081
|
+
}
|
|
2022
3082
|
);
|
|
3083
|
+
if (!response.ok) {
|
|
3084
|
+
const data2 = await response.json().catch(() => ({}));
|
|
3085
|
+
throw new Error(data2.error || "Failed to get calls status");
|
|
3086
|
+
}
|
|
3087
|
+
const data = await response.json();
|
|
3088
|
+
const statusMap = {
|
|
3089
|
+
pending: "PENDING",
|
|
3090
|
+
preconfirmed: "PENDING",
|
|
3091
|
+
completed: "CONFIRMED",
|
|
3092
|
+
failed: "CONFIRMED",
|
|
3093
|
+
expired: "CONFIRMED"
|
|
3094
|
+
};
|
|
3095
|
+
return {
|
|
3096
|
+
status: statusMap[data.status] || "PENDING",
|
|
3097
|
+
receipts: data.transactionHash ? [
|
|
3098
|
+
{
|
|
3099
|
+
logs: [],
|
|
3100
|
+
status: data.status === "completed" ? "0x1" : "0x0",
|
|
3101
|
+
blockHash: data.blockHash,
|
|
3102
|
+
blockNumber: data.blockNumber,
|
|
3103
|
+
transactionHash: data.transactionHash
|
|
3104
|
+
}
|
|
3105
|
+
] : []
|
|
3106
|
+
};
|
|
3107
|
+
}
|
|
3108
|
+
case "wallet_getCallsHistory": {
|
|
3109
|
+
const paramList = Array.isArray(params) ? params : [];
|
|
3110
|
+
const options2 = paramList[0] || {};
|
|
3111
|
+
const queryParams = new URLSearchParams();
|
|
3112
|
+
if (options2.limit) queryParams.set("limit", String(options2.limit));
|
|
3113
|
+
if (options2.offset) queryParams.set("offset", String(options2.offset));
|
|
3114
|
+
if (options2.status) queryParams.set("status", options2.status);
|
|
3115
|
+
if (options2.from) queryParams.set("from", options2.from);
|
|
3116
|
+
if (options2.to) queryParams.set("to", options2.to);
|
|
3117
|
+
const url = `${client.getProviderUrl()}/api/intent/history${queryParams.toString() ? `?${queryParams}` : ""}`;
|
|
3118
|
+
const historyClientId = client.getClientId();
|
|
3119
|
+
const response = await fetch(url, {
|
|
3120
|
+
headers: historyClientId ? { "x-client-id": historyClientId } : {},
|
|
3121
|
+
credentials: "include"
|
|
3122
|
+
});
|
|
3123
|
+
if (!response.ok) {
|
|
3124
|
+
const data2 = await response.json().catch(() => ({}));
|
|
3125
|
+
throw new Error(data2.error || "Failed to get calls history");
|
|
3126
|
+
}
|
|
3127
|
+
const data = await response.json();
|
|
3128
|
+
const statusMap = {
|
|
3129
|
+
pending: "PENDING",
|
|
3130
|
+
preconfirmed: "PENDING",
|
|
3131
|
+
completed: "CONFIRMED",
|
|
3132
|
+
failed: "CONFIRMED",
|
|
3133
|
+
expired: "CONFIRMED"
|
|
3134
|
+
};
|
|
2023
3135
|
return {
|
|
2024
|
-
|
|
2025
|
-
|
|
3136
|
+
calls: data.intents.map(
|
|
3137
|
+
(intent) => ({
|
|
3138
|
+
callsId: intent.intentId,
|
|
3139
|
+
// intentId is the orchestrator's ID
|
|
3140
|
+
status: statusMap[intent.status] || "PENDING",
|
|
3141
|
+
receipts: intent.transactionHash ? [{ transactionHash: intent.transactionHash }] : [],
|
|
3142
|
+
chainId: `0x${intent.targetChain.toString(16)}`
|
|
3143
|
+
})
|
|
3144
|
+
),
|
|
3145
|
+
total: data.total,
|
|
3146
|
+
hasMore: data.hasMore
|
|
2026
3147
|
};
|
|
2027
3148
|
}
|
|
2028
3149
|
default:
|
|
@@ -2045,6 +3166,7 @@ function createPasskeyProvider(options) {
|
|
|
2045
3166
|
disconnect
|
|
2046
3167
|
};
|
|
2047
3168
|
}
|
|
3169
|
+
var createPasskeyProvider = createOneAuthProvider;
|
|
2048
3170
|
|
|
2049
3171
|
// src/account.ts
|
|
2050
3172
|
var import_viem5 = require("viem");
|
|
@@ -2147,6 +3269,9 @@ function createPasskeyWalletClient(config) {
|
|
|
2147
3269
|
if (!result.success) {
|
|
2148
3270
|
throw new Error(result.error?.message || "Signing failed");
|
|
2149
3271
|
}
|
|
3272
|
+
if (!result.signature) {
|
|
3273
|
+
throw new Error("No signature received");
|
|
3274
|
+
}
|
|
2150
3275
|
return encodeWebAuthnSignature(result.signature);
|
|
2151
3276
|
};
|
|
2152
3277
|
const signTransactionImpl = async (transaction) => {
|
|
@@ -2167,6 +3292,9 @@ function createPasskeyWalletClient(config) {
|
|
|
2167
3292
|
if (!result.success) {
|
|
2168
3293
|
throw new Error(result.error?.message || "Signing failed");
|
|
2169
3294
|
}
|
|
3295
|
+
if (!result.signature) {
|
|
3296
|
+
throw new Error("No signature received");
|
|
3297
|
+
}
|
|
2170
3298
|
return encodeWebAuthnSignature(result.signature);
|
|
2171
3299
|
};
|
|
2172
3300
|
const signTypedDataImpl = async (typedData) => {
|
|
@@ -2188,6 +3316,9 @@ function createPasskeyWalletClient(config) {
|
|
|
2188
3316
|
if (!result.success) {
|
|
2189
3317
|
throw new Error(result.error?.message || "Signing failed");
|
|
2190
3318
|
}
|
|
3319
|
+
if (!result.signature) {
|
|
3320
|
+
throw new Error("No signature received");
|
|
3321
|
+
}
|
|
2191
3322
|
return encodeWebAuthnSignature(result.signature);
|
|
2192
3323
|
};
|
|
2193
3324
|
const buildIntentPayload = async (calls, targetChainOverride) => {
|
|
@@ -2256,11 +3387,12 @@ function createPasskeyWalletClient(config) {
|
|
|
2256
3387
|
* Send multiple calls as a single batched transaction
|
|
2257
3388
|
*/
|
|
2258
3389
|
async sendCalls(params) {
|
|
2259
|
-
const { calls, chainId: targetChain } = params;
|
|
3390
|
+
const { calls, chainId: targetChain, tokenRequests } = params;
|
|
2260
3391
|
const closeOn = config.waitForHash ?? true ? "completed" : "preconfirmed";
|
|
2261
3392
|
const intentPayload = await buildIntentPayload(calls, targetChain);
|
|
2262
3393
|
const result = await provider.sendIntent({
|
|
2263
3394
|
...intentPayload,
|
|
3395
|
+
tokenRequests,
|
|
2264
3396
|
closeOn,
|
|
2265
3397
|
waitForHash: config.waitForHash ?? true,
|
|
2266
3398
|
hashTimeoutMs: config.hashTimeoutMs,
|
|
@@ -2704,9 +3836,10 @@ function BatchQueueWidget({ onSignAll }) {
|
|
|
2704
3836
|
|
|
2705
3837
|
// src/verify.ts
|
|
2706
3838
|
var import_viem7 = require("viem");
|
|
2707
|
-
var
|
|
3839
|
+
var ETHEREUM_MESSAGE_PREFIX = "Ethereum Signed Message:\n";
|
|
3840
|
+
var PASSKEY_MESSAGE_PREFIX = ETHEREUM_MESSAGE_PREFIX;
|
|
2708
3841
|
function hashMessage2(message) {
|
|
2709
|
-
const prefixed =
|
|
3842
|
+
const prefixed = ETHEREUM_MESSAGE_PREFIX + message.length.toString() + message;
|
|
2710
3843
|
return (0, import_viem7.keccak256)((0, import_viem7.toBytes)(prefixed));
|
|
2711
3844
|
}
|
|
2712
3845
|
function verifyMessageHash(message, signedHash) {
|
|
@@ -2718,9 +3851,11 @@ function verifyMessageHash(message, signedHash) {
|
|
|
2718
3851
|
0 && (module.exports = {
|
|
2719
3852
|
BatchQueueProvider,
|
|
2720
3853
|
BatchQueueWidget,
|
|
3854
|
+
ETHEREUM_MESSAGE_PREFIX,
|
|
2721
3855
|
OneAuthClient,
|
|
2722
3856
|
PASSKEY_MESSAGE_PREFIX,
|
|
2723
3857
|
PasskeyProviderClient,
|
|
3858
|
+
createOneAuthProvider,
|
|
2724
3859
|
createPasskeyAccount,
|
|
2725
3860
|
createPasskeyProvider,
|
|
2726
3861
|
createPasskeyWalletClient,
|