@rhinestone/1auth 0.1.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/dist/index.js ADDED
@@ -0,0 +1,2701 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ BatchQueueProvider: () => BatchQueueProvider,
34
+ BatchQueueWidget: () => BatchQueueWidget,
35
+ PASSKEY_MESSAGE_PREFIX: () => PASSKEY_MESSAGE_PREFIX,
36
+ PasskeyProviderClient: () => PasskeyProviderClient,
37
+ createPasskeyAccount: () => createPasskeyAccount,
38
+ createPasskeyProvider: () => createPasskeyProvider,
39
+ createPasskeyWalletClient: () => createPasskeyWalletClient,
40
+ encodeWebAuthnSignature: () => encodeWebAuthnSignature,
41
+ getAllSupportedChainsAndTokens: () => getAllSupportedChainsAndTokens,
42
+ getChainById: () => getChainById,
43
+ getChainExplorerUrl: () => getChainExplorerUrl,
44
+ getChainName: () => getChainName2,
45
+ getChainRpcUrl: () => getChainRpcUrl,
46
+ getSupportedChainIds: () => getSupportedChainIds,
47
+ getSupportedChains: () => getSupportedChains,
48
+ getSupportedTokenSymbols: () => getSupportedTokenSymbols,
49
+ getSupportedTokens: () => getSupportedTokens,
50
+ getTokenAddress: () => import_sdk.getTokenAddress,
51
+ getTokenDecimals: () => import_sdk.getTokenDecimals,
52
+ getTokenSymbol: () => getTokenSymbol,
53
+ hashCalls: () => hashCalls,
54
+ hashMessage: () => hashMessage2,
55
+ isTestnet: () => isTestnet,
56
+ isTokenAddressSupported: () => isTokenAddressSupported,
57
+ resolveTokenAddress: () => resolveTokenAddress,
58
+ useBatchQueue: () => useBatchQueue,
59
+ verifyMessageHash: () => verifyMessageHash
60
+ });
61
+ module.exports = __toCommonJS(index_exports);
62
+
63
+ // src/client.ts
64
+ var import_viem2 = require("viem");
65
+
66
+ // src/registry.ts
67
+ var import_viem = require("viem");
68
+ var viemChains = __toESM(require("viem/chains"));
69
+ var import_sdk = require("@rhinestone/sdk");
70
+ var env = typeof process !== "undefined" ? process.env : {};
71
+ var ALL_VIEM_CHAINS = Object.values(viemChains).filter(
72
+ (value) => typeof value === "object" && value !== null && "id" in value && "name" in value
73
+ );
74
+ var VIEM_CHAIN_BY_ID = new Map(
75
+ ALL_VIEM_CHAINS.map((chain) => [chain.id, chain])
76
+ );
77
+ var SUPPORTED_CHAIN_IDS = new Set(
78
+ (0, import_sdk.getAllSupportedChainsAndTokens)().map((entry) => entry.chainId)
79
+ );
80
+ function parseBool(value) {
81
+ if (value === "true" || value === "1") return true;
82
+ if (value === "false" || value === "0") return false;
83
+ return void 0;
84
+ }
85
+ function resolveIncludeTestnets(explicit) {
86
+ if (explicit !== void 0) return explicit;
87
+ const envValue = parseBool(env.NEXT_PUBLIC_ORCHESTRATOR_USE_TESTNETS) ?? parseBool(env.ORCHESTRATOR_USE_TESTNETS);
88
+ return envValue ?? false;
89
+ }
90
+ function applyChainFilters(chainIds, options) {
91
+ const includeTestnets = resolveIncludeTestnets(options?.includeTestnets);
92
+ const allowlist = options?.chainIds;
93
+ let filtered = chainIds;
94
+ if (!includeTestnets) {
95
+ filtered = filtered.filter((chainId) => !isTestnet(chainId));
96
+ }
97
+ if (allowlist && allowlist.length > 0) {
98
+ const allowed = new Set(allowlist);
99
+ filtered = filtered.filter((chainId) => allowed.has(chainId));
100
+ }
101
+ return filtered;
102
+ }
103
+ function getSupportedChainIds(options) {
104
+ return applyChainFilters(Array.from(SUPPORTED_CHAIN_IDS), options);
105
+ }
106
+ function getSupportedChains(options) {
107
+ return getSupportedChainIds(options).map((chainId) => VIEM_CHAIN_BY_ID.get(chainId)).filter((chain) => Boolean(chain));
108
+ }
109
+ function getAllSupportedChainsAndTokens(options) {
110
+ const allowed = new Set(getSupportedChainIds(options));
111
+ return (0, import_sdk.getAllSupportedChainsAndTokens)().filter((entry) => allowed.has(entry.chainId)).map((entry) => ({
112
+ chainId: entry.chainId,
113
+ tokens: entry.tokens
114
+ }));
115
+ }
116
+ function getChainById(chainId) {
117
+ if (!SUPPORTED_CHAIN_IDS.has(chainId)) {
118
+ throw new Error(`Unsupported chain ID: ${chainId}`);
119
+ }
120
+ const chain = VIEM_CHAIN_BY_ID.get(chainId);
121
+ if (!chain) {
122
+ throw new Error(`Unsupported chain ID: ${chainId}`);
123
+ }
124
+ return chain;
125
+ }
126
+ function getChainName(chainId) {
127
+ try {
128
+ return getChainById(chainId).name;
129
+ } catch {
130
+ return `Chain ${chainId}`;
131
+ }
132
+ }
133
+ function getChainExplorerUrl(chainId) {
134
+ try {
135
+ return getChainById(chainId).blockExplorers?.default?.url;
136
+ } catch {
137
+ return void 0;
138
+ }
139
+ }
140
+ function getChainRpcUrl(chainId) {
141
+ try {
142
+ const chain = getChainById(chainId);
143
+ return chain.rpcUrls?.default?.http?.[0] || chain.rpcUrls?.public?.http?.[0];
144
+ } catch {
145
+ return void 0;
146
+ }
147
+ }
148
+ function getSupportedTokens(chainId) {
149
+ return (0, import_sdk.getSupportedTokens)(chainId);
150
+ }
151
+ function getSupportedTokenSymbols(chainId) {
152
+ return getSupportedTokens(chainId).map((token) => token.symbol);
153
+ }
154
+ function resolveTokenAddress(token, chainId) {
155
+ if ((0, import_viem.isAddress)(token)) {
156
+ return token;
157
+ }
158
+ return (0, import_sdk.getTokenAddress)(token.toUpperCase(), chainId);
159
+ }
160
+ function isTestnet(chainId) {
161
+ try {
162
+ return getChainById(chainId).testnet ?? false;
163
+ } catch {
164
+ return false;
165
+ }
166
+ }
167
+ function getTokenSymbol(tokenAddress, chainId) {
168
+ const token = getSupportedTokens(chainId).find(
169
+ (entry) => entry.address.toLowerCase() === tokenAddress.toLowerCase()
170
+ );
171
+ if (!token) {
172
+ throw new Error(`Unsupported token: ${tokenAddress} on chain ${chainId}`);
173
+ }
174
+ return token.symbol;
175
+ }
176
+ function isTokenAddressSupported(tokenAddress, chainId) {
177
+ try {
178
+ return getSupportedTokens(chainId).some(
179
+ (entry) => entry.address.toLowerCase() === tokenAddress.toLowerCase()
180
+ );
181
+ } catch {
182
+ return false;
183
+ }
184
+ }
185
+
186
+ // src/client.ts
187
+ var POPUP_WIDTH = 450;
188
+ var POPUP_HEIGHT = 600;
189
+ var DEFAULT_EMBED_WIDTH = "400px";
190
+ var DEFAULT_EMBED_HEIGHT = "500px";
191
+ var MODAL_WIDTH = 360;
192
+ var PasskeyProviderClient = class {
193
+ constructor(config) {
194
+ this.config = config;
195
+ this.theme = config.theme || {};
196
+ }
197
+ /**
198
+ * Update the theme configuration at runtime
199
+ */
200
+ setTheme(theme) {
201
+ this.theme = theme;
202
+ }
203
+ /**
204
+ * Build theme URL parameters
205
+ */
206
+ getThemeParams(overrideTheme) {
207
+ const theme = { ...this.theme, ...overrideTheme };
208
+ const params = new URLSearchParams();
209
+ if (theme.mode) {
210
+ params.set("theme", theme.mode);
211
+ }
212
+ if (theme.accent) {
213
+ params.set("accent", theme.accent);
214
+ }
215
+ return params.toString();
216
+ }
217
+ /**
218
+ * Get the dialog URL (Vite app URL)
219
+ * Defaults to providerUrl if dialogUrl is not set
220
+ */
221
+ getDialogUrl() {
222
+ return this.config.dialogUrl || this.config.providerUrl;
223
+ }
224
+ /**
225
+ * Get the origin for message validation
226
+ * Uses dialogUrl origin if set, otherwise providerUrl origin
227
+ */
228
+ getDialogOrigin() {
229
+ const dialogUrl = this.getDialogUrl();
230
+ try {
231
+ return new URL(dialogUrl).origin;
232
+ } catch {
233
+ return dialogUrl;
234
+ }
235
+ }
236
+ /**
237
+ * Get the base provider URL
238
+ */
239
+ getProviderUrl() {
240
+ return this.config.providerUrl;
241
+ }
242
+ /**
243
+ * Get the configured client ID
244
+ */
245
+ getClientId() {
246
+ return this.config.clientId;
247
+ }
248
+ async waitForTransactionHash(intentId, options = {}) {
249
+ const timeoutMs = options.timeoutMs ?? 12e4;
250
+ const intervalMs = options.intervalMs ?? 2e3;
251
+ const deadline = Date.now() + timeoutMs;
252
+ while (Date.now() < deadline) {
253
+ try {
254
+ const response = await fetch(
255
+ `${this.config.providerUrl}/api/intent/status/${intentId}`,
256
+ {
257
+ headers: {
258
+ "x-client-id": this.config.clientId
259
+ }
260
+ }
261
+ );
262
+ if (response.ok) {
263
+ const data = await response.json();
264
+ if (data.transactionHash) {
265
+ return data.transactionHash;
266
+ }
267
+ if (data.status === "failed" || data.status === "expired") {
268
+ return void 0;
269
+ }
270
+ }
271
+ } catch {
272
+ }
273
+ await new Promise((resolve) => setTimeout(resolve, intervalMs));
274
+ }
275
+ return void 0;
276
+ }
277
+ /**
278
+ * Show Porto-style "Get started" auth dialog (combines sign in + sign up)
279
+ * This is the recommended method for authentication - shows a modal overlay
280
+ * with both sign in and create account options.
281
+ */
282
+ async authWithModal(options) {
283
+ const dialogUrl = this.getDialogUrl();
284
+ const params = new URLSearchParams({
285
+ clientId: this.config.clientId,
286
+ mode: "iframe"
287
+ });
288
+ if (options?.username) {
289
+ params.set("username", options.username);
290
+ }
291
+ if (options?.oauthEnabled === false) {
292
+ params.set("oauth", "0");
293
+ }
294
+ const themeParams = this.getThemeParams(options?.theme);
295
+ if (themeParams) {
296
+ const themeParsed = new URLSearchParams(themeParams);
297
+ themeParsed.forEach((value, key) => params.set(key, value));
298
+ }
299
+ const url = `${dialogUrl}/dialog/auth?${params.toString()}`;
300
+ const { dialog, iframe, cleanup } = this.createModalDialog(url);
301
+ return this.waitForModalAuthResponse(dialog, iframe, cleanup);
302
+ }
303
+ /**
304
+ * Authenticate a user with an optional challenge to sign.
305
+ *
306
+ * This method combines authentication (sign in / sign up) with optional
307
+ * challenge signing, enabling off-chain login without on-chain transactions.
308
+ *
309
+ * When a challenge is provided:
310
+ * 1. User authenticates (sign in or sign up)
311
+ * 2. The challenge is hashed with a domain separator
312
+ * 3. User signs the hash with their passkey
313
+ * 4. Returns user info + signature for server-side verification
314
+ *
315
+ * The domain separator ("\x19Passkey Signed Message:\n") ensures the signature
316
+ * cannot be reused for transaction signing, preventing phishing attacks.
317
+ *
318
+ * @example
319
+ * ```typescript
320
+ * // Authenticate with a login challenge
321
+ * const result = await client.authenticate({
322
+ * challenge: `Login to MyApp\nTimestamp: ${Date.now()}\nNonce: ${crypto.randomUUID()}`
323
+ * });
324
+ *
325
+ * if (result.success && result.signature) {
326
+ * // Verify signature server-side
327
+ * const isValid = await verifyOnServer(
328
+ * result.username,
329
+ * result.signature,
330
+ * result.signedHash
331
+ * );
332
+ * }
333
+ * ```
334
+ */
335
+ async authenticate(options) {
336
+ const dialogUrl = this.getDialogUrl();
337
+ const params = new URLSearchParams({
338
+ clientId: this.config.clientId,
339
+ mode: "iframe"
340
+ });
341
+ if (options?.challenge) {
342
+ params.set("challenge", options.challenge);
343
+ }
344
+ const themeParams = this.getThemeParams(options?.theme);
345
+ if (themeParams) {
346
+ const themeParsed = new URLSearchParams(themeParams);
347
+ themeParsed.forEach((value, key) => params.set(key, value));
348
+ }
349
+ const url = `${dialogUrl}/dialog/authenticate?${params.toString()}`;
350
+ const { dialog, iframe, cleanup } = this.createModalDialog(url);
351
+ return this.waitForAuthenticateResponse(dialog, iframe, cleanup);
352
+ }
353
+ /**
354
+ * Show signing in a modal overlay (Porto-style iframe dialog)
355
+ */
356
+ async signWithModal(options) {
357
+ const dialogUrl = this.getDialogUrl();
358
+ const themeParams = this.getThemeParams(options?.theme);
359
+ const signingUrl = `${dialogUrl}/dialog/sign?mode=iframe${themeParams ? `&${themeParams}` : ""}`;
360
+ const { dialog, iframe, cleanup } = this.createModalDialog(signingUrl);
361
+ const dialogOrigin = this.getDialogOrigin();
362
+ await new Promise((resolve) => {
363
+ iframe.onload = () => {
364
+ iframe.contentWindow?.postMessage({
365
+ type: "PASSKEY_INIT",
366
+ mode: "iframe",
367
+ challenge: options.challenge,
368
+ username: options.username,
369
+ description: options.description,
370
+ transaction: options.transaction,
371
+ metadata: options.metadata
372
+ }, dialogOrigin);
373
+ resolve();
374
+ };
375
+ });
376
+ return this.waitForSigningResponse(dialog, iframe, cleanup);
377
+ }
378
+ /**
379
+ * Send an intent to the Rhinestone orchestrator
380
+ *
381
+ * This is the high-level method for cross-chain transactions:
382
+ * 1. Prepares the intent (gets quote from orchestrator)
383
+ * 2. Shows the signing modal with real fees
384
+ * 3. Submits the signed intent for execution
385
+ * 4. Returns the transaction hash
386
+ *
387
+ * @example
388
+ * ```typescript
389
+ * const result = await client.sendIntent({
390
+ * username: 'alice',
391
+ * targetChain: 8453, // Base
392
+ * calls: [
393
+ * {
394
+ * to: '0x...',
395
+ * data: '0x...',
396
+ * label: 'Swap ETH for USDC',
397
+ * sublabel: '0.1 ETH → ~250 USDC',
398
+ * },
399
+ * ],
400
+ * });
401
+ *
402
+ * if (result.success) {
403
+ * console.log('Transaction hash:', result.transactionHash);
404
+ * }
405
+ * ```
406
+ */
407
+ async sendIntent(options) {
408
+ const signedIntent = options.signedIntent;
409
+ const username = signedIntent?.username || options.username;
410
+ const targetChain = signedIntent?.targetChain || options.targetChain;
411
+ const calls = signedIntent?.calls || options.calls;
412
+ if (!username && !signedIntent?.accountAddress) {
413
+ return {
414
+ success: false,
415
+ intentId: "",
416
+ status: "failed",
417
+ error: {
418
+ code: "INVALID_OPTIONS",
419
+ message: "Either username, accountAddress, or signedIntent with user identifier is required"
420
+ }
421
+ };
422
+ }
423
+ if (!targetChain || !calls?.length) {
424
+ return {
425
+ success: false,
426
+ intentId: "",
427
+ status: "failed",
428
+ error: {
429
+ code: "INVALID_OPTIONS",
430
+ message: "targetChain and calls are required (either directly or via signedIntent)"
431
+ }
432
+ };
433
+ }
434
+ let prepareResponse;
435
+ 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
+ const response = await fetch(`${this.config.providerUrl}/api/intent/prepare`, {
445
+ method: "POST",
446
+ headers: {
447
+ "Content-Type": "application/json"
448
+ },
449
+ body: JSON.stringify(requestBody)
450
+ });
451
+ if (!response.ok) {
452
+ const errorData = await response.json().catch(() => ({}));
453
+ const errorMessage = errorData.error || "Failed to prepare intent";
454
+ if (errorMessage.includes("User not found")) {
455
+ localStorage.removeItem("1auth-user");
456
+ }
457
+ return {
458
+ success: false,
459
+ intentId: "",
460
+ status: "failed",
461
+ error: {
462
+ code: errorMessage.includes("User not found") ? "USER_NOT_FOUND" : "PREPARE_FAILED",
463
+ message: errorMessage
464
+ }
465
+ };
466
+ }
467
+ prepareResponse = await response.json();
468
+ } catch (error) {
469
+ return {
470
+ success: false,
471
+ intentId: "",
472
+ status: "failed",
473
+ error: {
474
+ code: "NETWORK_ERROR",
475
+ message: error instanceof Error ? error.message : "Network error"
476
+ }
477
+ };
478
+ }
479
+ const dialogUrl = this.getDialogUrl();
480
+ const signingUrl = `${dialogUrl}/dialog/sign?mode=iframe`;
481
+ const { dialog, iframe, cleanup } = this.createModalDialog(signingUrl);
482
+ const dialogOrigin = this.getDialogOrigin();
483
+ await new Promise((resolve) => {
484
+ const handleReady = (event) => {
485
+ if (event.origin !== dialogOrigin) return;
486
+ if (event.data?.type === "PASSKEY_READY") {
487
+ window.removeEventListener("message", handleReady);
488
+ iframe.contentWindow?.postMessage({
489
+ type: "PASSKEY_INIT",
490
+ mode: "iframe",
491
+ calls,
492
+ chainId: targetChain,
493
+ transaction: prepareResponse.transaction,
494
+ challenge: prepareResponse.challenge,
495
+ username,
496
+ accountAddress: prepareResponse.accountAddress,
497
+ intentId: prepareResponse.intentId
498
+ }, dialogOrigin);
499
+ resolve();
500
+ }
501
+ };
502
+ window.addEventListener("message", handleReady);
503
+ });
504
+ const signingResult = await this.waitForSigningResponse(dialog, iframe, cleanup);
505
+ if (!signingResult.success) {
506
+ return {
507
+ success: false,
508
+ intentId: prepareResponse.intentId,
509
+ status: "failed",
510
+ error: signingResult.error
511
+ };
512
+ }
513
+ let executeResponse;
514
+ try {
515
+ const response = await fetch(`${this.config.providerUrl}/api/intent/execute`, {
516
+ method: "POST",
517
+ headers: {
518
+ "Content-Type": "application/json"
519
+ },
520
+ body: JSON.stringify({
521
+ intentId: prepareResponse.intentId,
522
+ signature: signingResult.signature,
523
+ passkey: signingResult.passkey
524
+ // Include passkey info for signature encoding
525
+ })
526
+ });
527
+ if (!response.ok) {
528
+ const errorData = await response.json().catch(() => ({}));
529
+ this.sendTransactionStatus(iframe, "failed");
530
+ await this.waitForDialogClose(dialog, cleanup);
531
+ return {
532
+ success: false,
533
+ intentId: prepareResponse.intentId,
534
+ status: "failed",
535
+ error: {
536
+ code: "EXECUTE_FAILED",
537
+ message: errorData.error || "Failed to execute intent"
538
+ }
539
+ };
540
+ }
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
+ }
555
+ const closeOn = options.closeOn || "preconfirmed";
556
+ const acceptPreconfirmations = closeOn !== "completed";
557
+ let finalStatus = executeResponse.status;
558
+ let finalTxHash = executeResponse.transactionHash;
559
+ if (finalStatus === "pending") {
560
+ this.sendTransactionStatus(iframe, "processing");
561
+ try {
562
+ const waitResponse = await fetch(
563
+ `${this.config.providerUrl}/api/intent/wait/${prepareResponse.intentId}?preconfirm=${acceptPreconfirmations}`,
564
+ {
565
+ headers: {
566
+ "x-client-id": this.config.clientId
567
+ }
568
+ }
569
+ );
570
+ if (waitResponse.ok) {
571
+ const waitResult = await waitResponse.json();
572
+ finalStatus = waitResult.status === "preconfirmed" || waitResult.status === "completed" ? "completed" : waitResult.status;
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);
579
+ }
580
+ }
581
+ const displayStatus = finalStatus === "completed" ? "confirmed" : finalStatus;
582
+ this.sendTransactionStatus(iframe, displayStatus, finalTxHash);
583
+ await this.waitForDialogClose(dialog, cleanup);
584
+ if (options.waitForHash && !finalTxHash) {
585
+ const hash = await this.waitForTransactionHash(prepareResponse.intentId, {
586
+ timeoutMs: options.hashTimeoutMs,
587
+ intervalMs: options.hashIntervalMs
588
+ });
589
+ if (hash) {
590
+ finalTxHash = hash;
591
+ finalStatus = "completed";
592
+ } else {
593
+ finalStatus = "failed";
594
+ return {
595
+ success: false,
596
+ intentId: prepareResponse.intentId,
597
+ status: finalStatus,
598
+ transactionHash: finalTxHash,
599
+ operationId: executeResponse.operationId,
600
+ error: {
601
+ code: "HASH_TIMEOUT",
602
+ message: "Timed out waiting for transaction hash"
603
+ }
604
+ };
605
+ }
606
+ }
607
+ return {
608
+ success: finalStatus === "completed",
609
+ intentId: prepareResponse.intentId,
610
+ status: finalStatus,
611
+ transactionHash: finalTxHash,
612
+ operationId: executeResponse.operationId,
613
+ error: executeResponse.error
614
+ };
615
+ }
616
+ /**
617
+ * Send transaction status to the dialog iframe
618
+ */
619
+ sendTransactionStatus(iframe, status, transactionHash) {
620
+ const dialogOrigin = this.getDialogOrigin();
621
+ iframe.contentWindow?.postMessage(
622
+ {
623
+ type: "TRANSACTION_STATUS",
624
+ status,
625
+ transactionHash
626
+ },
627
+ dialogOrigin
628
+ );
629
+ }
630
+ /**
631
+ * Wait for the signing result without closing the modal
632
+ */
633
+ waitForIntentSigningResponse(requestId, dialog, _iframe, cleanup) {
634
+ const dialogOrigin = this.getDialogOrigin();
635
+ return new Promise((resolve) => {
636
+ const handleMessage = (event) => {
637
+ if (event.origin !== dialogOrigin) return;
638
+ const message = event.data;
639
+ const payload = message?.data;
640
+ if (message?.type === "PASSKEY_SIGNING_RESULT" && payload?.requestId === requestId) {
641
+ window.removeEventListener("message", handleMessage);
642
+ if (message.success && payload.signature) {
643
+ resolve({
644
+ success: true,
645
+ requestId,
646
+ signature: payload.signature,
647
+ passkey: payload.passkey
648
+ // Include passkey info for signature encoding
649
+ });
650
+ } else {
651
+ resolve({
652
+ success: false,
653
+ error: message.error || {
654
+ code: "SIGNING_FAILED",
655
+ message: "Signing failed"
656
+ }
657
+ });
658
+ }
659
+ } else if (message?.type === "PASSKEY_CLOSE") {
660
+ window.removeEventListener("message", handleMessage);
661
+ cleanup();
662
+ resolve({
663
+ success: false,
664
+ error: {
665
+ code: "USER_REJECTED",
666
+ message: "User closed the dialog"
667
+ }
668
+ });
669
+ }
670
+ };
671
+ window.addEventListener("message", handleMessage);
672
+ dialog.showModal();
673
+ });
674
+ }
675
+ /**
676
+ * Wait for signing result (simplified - no requestId matching)
677
+ */
678
+ waitForSigningResponse(dialog, _iframe, cleanup) {
679
+ const dialogOrigin = this.getDialogOrigin();
680
+ console.log("[SDK] waitForSigningResponse, expecting origin:", dialogOrigin);
681
+ return new Promise((resolve) => {
682
+ const handleMessage = (event) => {
683
+ console.log("[SDK] Received message:", event.origin, event.data?.type);
684
+ if (event.origin !== dialogOrigin) {
685
+ console.log("[SDK] Origin mismatch, ignoring. Expected:", dialogOrigin, "Got:", event.origin);
686
+ return;
687
+ }
688
+ const message = event.data;
689
+ const payload = message?.data;
690
+ if (message?.type === "PASSKEY_SIGNING_RESULT") {
691
+ window.removeEventListener("message", handleMessage);
692
+ if (message.success && payload?.signature) {
693
+ resolve({
694
+ success: true,
695
+ signature: payload.signature,
696
+ passkey: payload.passkey,
697
+ signedHash: payload.signedHash
698
+ });
699
+ } else {
700
+ resolve({
701
+ success: false,
702
+ error: message.error || {
703
+ code: "SIGNING_FAILED",
704
+ message: "Signing failed"
705
+ }
706
+ });
707
+ }
708
+ } else if (message?.type === "PASSKEY_CLOSE") {
709
+ window.removeEventListener("message", handleMessage);
710
+ cleanup();
711
+ resolve({
712
+ success: false,
713
+ error: {
714
+ code: "USER_REJECTED",
715
+ message: "User closed the dialog"
716
+ }
717
+ });
718
+ }
719
+ };
720
+ window.addEventListener("message", handleMessage);
721
+ });
722
+ }
723
+ /**
724
+ * Wait for the dialog to be closed
725
+ */
726
+ waitForDialogClose(dialog, cleanup) {
727
+ const dialogOrigin = this.getDialogOrigin();
728
+ return new Promise((resolve) => {
729
+ const handleMessage = (event) => {
730
+ if (event.origin !== dialogOrigin) return;
731
+ if (event.data?.type === "PASSKEY_CLOSE") {
732
+ window.removeEventListener("message", handleMessage);
733
+ cleanup();
734
+ resolve();
735
+ }
736
+ };
737
+ const handleClose = () => {
738
+ window.removeEventListener("message", handleMessage);
739
+ dialog.removeEventListener("close", handleClose);
740
+ cleanup();
741
+ resolve();
742
+ };
743
+ window.addEventListener("message", handleMessage);
744
+ dialog.addEventListener("close", handleClose);
745
+ });
746
+ }
747
+ /**
748
+ * Poll for intent status
749
+ *
750
+ * Use this to check on the status of a submitted intent
751
+ * that hasn't completed yet.
752
+ */
753
+ async getIntentStatus(intentId) {
754
+ try {
755
+ const response = await fetch(
756
+ `${this.config.providerUrl}/api/intent/status/${intentId}`,
757
+ {
758
+ headers: {
759
+ "x-client-id": this.config.clientId
760
+ }
761
+ }
762
+ );
763
+ if (!response.ok) {
764
+ const errorData = await response.json().catch(() => ({}));
765
+ return {
766
+ success: false,
767
+ intentId,
768
+ status: "failed",
769
+ error: {
770
+ code: "STATUS_FAILED",
771
+ message: errorData.error || "Failed to get intent status"
772
+ }
773
+ };
774
+ }
775
+ const data = await response.json();
776
+ return {
777
+ success: data.status === "completed",
778
+ intentId,
779
+ status: data.status,
780
+ transactionHash: data.transactionHash,
781
+ operationId: data.operationId
782
+ };
783
+ } catch (error) {
784
+ return {
785
+ success: false,
786
+ intentId,
787
+ status: "failed",
788
+ error: {
789
+ code: "NETWORK_ERROR",
790
+ message: error instanceof Error ? error.message : "Network error"
791
+ }
792
+ };
793
+ }
794
+ }
795
+ /**
796
+ * Send a swap intent through the Rhinestone orchestrator
797
+ *
798
+ * This is a high-level abstraction for token swaps (including cross-chain):
799
+ * 1. Resolves token symbols to addresses
800
+ * 2. Builds the swap intent with tokenRequests (output-first model)
801
+ * 3. The orchestrator's solver network finds the best route
802
+ * 4. Executes via the standard intent flow
803
+ *
804
+ * NOTE: The `amount` parameter specifies the OUTPUT amount (what the user wants to receive),
805
+ * not the input amount. The orchestrator will calculate the required input from sourceAssets.
806
+ *
807
+ * @example
808
+ * ```typescript
809
+ * // Buy 100 USDC using ETH on Base
810
+ * const result = await client.sendSwap({
811
+ * username: 'alice',
812
+ * targetChain: 8453,
813
+ * fromToken: 'ETH',
814
+ * toToken: 'USDC',
815
+ * amount: '100', // Receive 100 USDC
816
+ * });
817
+ *
818
+ * // Cross-chain: Buy 50 USDC on Base, paying with ETH from any chain
819
+ * const result = await client.sendSwap({
820
+ * username: 'alice',
821
+ * targetChain: 8453, // Base
822
+ * fromToken: 'ETH',
823
+ * toToken: 'USDC',
824
+ * amount: '50', // Receive 50 USDC
825
+ * });
826
+ * ```
827
+ */
828
+ async sendSwap(options) {
829
+ try {
830
+ getChainById(options.targetChain);
831
+ } catch {
832
+ return {
833
+ success: false,
834
+ intentId: "",
835
+ status: "failed",
836
+ error: {
837
+ code: "INVALID_CHAIN",
838
+ message: `Unsupported chain: ${options.targetChain}`
839
+ }
840
+ };
841
+ }
842
+ const resolveToken = (token, label) => {
843
+ try {
844
+ const address = resolveTokenAddress(token, options.targetChain);
845
+ if (!isTokenAddressSupported(address, options.targetChain)) {
846
+ return {
847
+ error: `Unsupported ${label}: ${token} on chain ${options.targetChain}`
848
+ };
849
+ }
850
+ return { address };
851
+ } catch (error) {
852
+ return {
853
+ error: error instanceof Error ? error.message : `Unsupported ${label}: ${token} on chain ${options.targetChain}`
854
+ };
855
+ }
856
+ };
857
+ const fromTokenResult = resolveToken(options.fromToken, "fromToken");
858
+ if (!fromTokenResult.address) {
859
+ return {
860
+ success: false,
861
+ intentId: "",
862
+ status: "failed",
863
+ error: {
864
+ code: "INVALID_TOKEN",
865
+ message: fromTokenResult.error || `Unknown fromToken: ${options.fromToken}`
866
+ }
867
+ };
868
+ }
869
+ const toTokenResult = resolveToken(options.toToken, "toToken");
870
+ if (!toTokenResult.address) {
871
+ return {
872
+ success: false,
873
+ intentId: "",
874
+ status: "failed",
875
+ error: {
876
+ code: "INVALID_TOKEN",
877
+ message: toTokenResult.error || `Unknown toToken: ${options.toToken}`
878
+ }
879
+ };
880
+ }
881
+ const fromTokenAddress = fromTokenResult.address;
882
+ const toTokenAddress = toTokenResult.address;
883
+ console.log("[SDK sendSwap] Token resolution:", {
884
+ fromToken: options.fromToken,
885
+ fromTokenAddress,
886
+ toToken: options.toToken,
887
+ toTokenAddress,
888
+ targetChain: options.targetChain
889
+ });
890
+ const formatTokenLabel = (token, fallback) => {
891
+ if (!token.startsWith("0x")) {
892
+ return token.toUpperCase();
893
+ }
894
+ try {
895
+ return getTokenSymbol(token, options.targetChain);
896
+ } catch {
897
+ return fallback;
898
+ }
899
+ };
900
+ const fromSymbol = formatTokenLabel(
901
+ options.fromToken,
902
+ `${options.fromToken.slice(0, 6)}...${options.fromToken.slice(-4)}`
903
+ );
904
+ const toSymbol = formatTokenLabel(
905
+ options.toToken,
906
+ `${options.toToken.slice(0, 6)}...${options.toToken.slice(-4)}`
907
+ );
908
+ const isFromNativeEth = fromTokenAddress === "0x0000000000000000000000000000000000000000";
909
+ const isToNativeEth = toTokenAddress === "0x0000000000000000000000000000000000000000";
910
+ const KNOWN_DECIMALS = {
911
+ ETH: 18,
912
+ WETH: 18,
913
+ USDC: 6,
914
+ USDT: 6,
915
+ USDT0: 6
916
+ };
917
+ const getDecimals = (symbol, chainId) => {
918
+ const upperSymbol = symbol.toUpperCase();
919
+ try {
920
+ const decimals = (0, import_sdk.getTokenDecimals)(upperSymbol, chainId);
921
+ console.log(`[SDK] getTokenDecimals(${upperSymbol}, ${chainId}) = ${decimals}`);
922
+ return decimals;
923
+ } catch (e) {
924
+ console.warn(`[SDK] getTokenDecimals failed for ${upperSymbol}, using fallback`, e);
925
+ return KNOWN_DECIMALS[upperSymbol] ?? 18;
926
+ }
927
+ };
928
+ const fromDecimals = getDecimals(options.fromToken, options.targetChain);
929
+ const toDecimals = getDecimals(options.toToken, options.targetChain);
930
+ const isBridge = options.fromToken.toUpperCase() === options.toToken.toUpperCase();
931
+ let tokenRequests;
932
+ if (!isToNativeEth) {
933
+ tokenRequests = [{
934
+ token: toTokenAddress,
935
+ amount: (0, import_viem2.parseUnits)(options.amount, toDecimals).toString()
936
+ }];
937
+ }
938
+ console.log("[SDK sendSwap] Building intent:", {
939
+ isBridge,
940
+ isFromNativeEth,
941
+ isToNativeEth,
942
+ fromDecimals,
943
+ toDecimals,
944
+ tokenRequests
945
+ });
946
+ const result = await this.sendIntent({
947
+ username: options.username,
948
+ targetChain: options.targetChain,
949
+ calls: [
950
+ {
951
+ // Minimal call - just signals to orchestrator we want the tokenRequests delivered
952
+ to: toTokenAddress,
953
+ value: "0"
954
+ }
955
+ ],
956
+ // Request specific output tokens - this is what actually matters for swaps
957
+ tokenRequests,
958
+ // Constrain orchestrator to use only the fromToken as input
959
+ // This ensures the swap uses the correct source token
960
+ // Pass the symbol (not address) so orchestrator can resolve per-chain
961
+ sourceAssets: options.sourceAssets || [options.fromToken.toUpperCase()],
962
+ closeOn: options.closeOn || "preconfirmed",
963
+ waitForHash: options.waitForHash,
964
+ hashTimeoutMs: options.hashTimeoutMs,
965
+ hashIntervalMs: options.hashIntervalMs
966
+ });
967
+ return {
968
+ ...result,
969
+ quote: result.success ? {
970
+ fromToken: fromTokenAddress,
971
+ toToken: toTokenAddress,
972
+ amountIn: options.amount,
973
+ amountOut: "",
974
+ // Filled by orchestrator quote
975
+ rate: ""
976
+ } : void 0
977
+ };
978
+ }
979
+ /**
980
+ * Sign an arbitrary message with the user's passkey
981
+ *
982
+ * This is for off-chain message signing (e.g., authentication challenges,
983
+ * terms acceptance, login signatures), NOT for transaction signing.
984
+ * The message is displayed to the user and signed with their passkey.
985
+ *
986
+ * @example
987
+ * ```typescript
988
+ * // Sign a login challenge
989
+ * const result = await client.signMessage({
990
+ * username: 'alice',
991
+ * message: `Sign in to MyApp\nTimestamp: ${Date.now()}\nNonce: ${crypto.randomUUID()}`,
992
+ * description: 'Verify your identity to continue',
993
+ * });
994
+ *
995
+ * if (result.success) {
996
+ * // Send signature to your backend for verification
997
+ * await fetch('/api/verify', {
998
+ * method: 'POST',
999
+ * body: JSON.stringify({
1000
+ * signature: result.signature,
1001
+ * message: result.signedMessage,
1002
+ * }),
1003
+ * });
1004
+ * }
1005
+ * ```
1006
+ */
1007
+ async signMessage(options) {
1008
+ const dialogUrl = this.getDialogUrl();
1009
+ const themeParams = this.getThemeParams(options?.theme);
1010
+ const signingUrl = `${dialogUrl}/dialog/sign?mode=iframe${themeParams ? `&${themeParams}` : ""}`;
1011
+ const { dialog, iframe, cleanup } = this.createModalDialog(signingUrl);
1012
+ const dialogOrigin = this.getDialogOrigin();
1013
+ await new Promise((resolve) => {
1014
+ const handleReady = (event) => {
1015
+ if (event.origin !== dialogOrigin) return;
1016
+ if (event.data?.type === "PASSKEY_READY") {
1017
+ window.removeEventListener("message", handleReady);
1018
+ iframe.contentWindow?.postMessage({
1019
+ type: "PASSKEY_INIT",
1020
+ mode: "iframe",
1021
+ message: options.message,
1022
+ challenge: options.challenge || options.message,
1023
+ username: options.username,
1024
+ description: options.description,
1025
+ metadata: options.metadata
1026
+ }, dialogOrigin);
1027
+ resolve();
1028
+ }
1029
+ };
1030
+ window.addEventListener("message", handleReady);
1031
+ });
1032
+ const signingResult = await this.waitForSigningResponse(dialog, iframe, cleanup);
1033
+ cleanup();
1034
+ if (signingResult.success) {
1035
+ return {
1036
+ success: true,
1037
+ signature: signingResult.signature,
1038
+ signedMessage: options.message,
1039
+ signedHash: signingResult.signedHash,
1040
+ passkey: signingResult.passkey
1041
+ };
1042
+ }
1043
+ return {
1044
+ success: false,
1045
+ error: signingResult.error
1046
+ };
1047
+ }
1048
+ /**
1049
+ * Sign EIP-712 typed data with the user's passkey
1050
+ *
1051
+ * This method allows signing structured data following the EIP-712 standard.
1052
+ * The typed data is displayed to the user in a human-readable format before signing.
1053
+ *
1054
+ * @example
1055
+ * ```typescript
1056
+ * // Sign an ERC-2612 Permit
1057
+ * const result = await client.signTypedData({
1058
+ * username: 'alice',
1059
+ * domain: {
1060
+ * name: 'Dai Stablecoin',
1061
+ * version: '1',
1062
+ * chainId: 1,
1063
+ * verifyingContract: '0x6B175474E89094C44Da98b954EecdeCB5BE3830F',
1064
+ * },
1065
+ * types: {
1066
+ * Permit: [
1067
+ * { name: 'owner', type: 'address' },
1068
+ * { name: 'spender', type: 'address' },
1069
+ * { name: 'value', type: 'uint256' },
1070
+ * { name: 'nonce', type: 'uint256' },
1071
+ * { name: 'deadline', type: 'uint256' },
1072
+ * ],
1073
+ * },
1074
+ * primaryType: 'Permit',
1075
+ * message: {
1076
+ * owner: '0xabc...',
1077
+ * spender: '0xdef...',
1078
+ * value: 1000000000000000000n,
1079
+ * nonce: 0n,
1080
+ * deadline: 1735689600n,
1081
+ * },
1082
+ * });
1083
+ *
1084
+ * if (result.success) {
1085
+ * console.log('Signed hash:', result.signedHash);
1086
+ * }
1087
+ * ```
1088
+ */
1089
+ async signTypedData(options) {
1090
+ const signedHash = (0, import_viem2.hashTypedData)({
1091
+ domain: options.domain,
1092
+ types: options.types,
1093
+ primaryType: options.primaryType,
1094
+ message: options.message
1095
+ });
1096
+ const dialogUrl = this.getDialogUrl();
1097
+ const themeParams = this.getThemeParams(options?.theme);
1098
+ const signingUrl = `${dialogUrl}/dialog/sign?mode=iframe${themeParams ? `&${themeParams}` : ""}`;
1099
+ const { dialog, iframe, cleanup } = this.createModalDialog(signingUrl);
1100
+ const dialogOrigin = this.getDialogOrigin();
1101
+ await new Promise((resolve) => {
1102
+ const handleReady = (event) => {
1103
+ if (event.origin !== dialogOrigin) return;
1104
+ if (event.data?.type === "PASSKEY_READY") {
1105
+ window.removeEventListener("message", handleReady);
1106
+ iframe.contentWindow?.postMessage({
1107
+ type: "PASSKEY_INIT",
1108
+ mode: "iframe",
1109
+ signingMode: "typedData",
1110
+ typedData: {
1111
+ domain: options.domain,
1112
+ types: options.types,
1113
+ primaryType: options.primaryType,
1114
+ message: options.message
1115
+ },
1116
+ challenge: signedHash,
1117
+ username: options.username,
1118
+ description: options.description
1119
+ }, dialogOrigin);
1120
+ resolve();
1121
+ }
1122
+ };
1123
+ window.addEventListener("message", handleReady);
1124
+ });
1125
+ const signingResult = await this.waitForSigningResponse(dialog, iframe, cleanup);
1126
+ cleanup();
1127
+ if (signingResult.success) {
1128
+ return {
1129
+ success: true,
1130
+ signature: signingResult.signature,
1131
+ signedHash,
1132
+ passkey: signingResult.passkey
1133
+ };
1134
+ }
1135
+ return {
1136
+ success: false,
1137
+ error: signingResult.error
1138
+ };
1139
+ }
1140
+ async signWithPopup(options) {
1141
+ const request = await this.createSigningRequest(options, "popup");
1142
+ const dialogUrl = this.getDialogUrl();
1143
+ const signingUrl = `${dialogUrl}/dialog/sign/${request.requestId}?mode=popup`;
1144
+ const popup = this.openPopup(signingUrl);
1145
+ if (!popup) {
1146
+ return {
1147
+ success: false,
1148
+ error: {
1149
+ code: "POPUP_BLOCKED",
1150
+ message: "Popup was blocked by the browser. Please allow popups for this site."
1151
+ }
1152
+ };
1153
+ }
1154
+ return this.waitForPopupResponse(request.requestId, popup);
1155
+ }
1156
+ async signWithRedirect(options, redirectUrl) {
1157
+ const finalRedirectUrl = redirectUrl || this.config.redirectUrl;
1158
+ if (!finalRedirectUrl) {
1159
+ throw new Error(
1160
+ "redirectUrl is required for redirect flow. Pass it to signWithRedirect() or set it in the constructor."
1161
+ );
1162
+ }
1163
+ const request = await this.createSigningRequest(
1164
+ options,
1165
+ "redirect",
1166
+ finalRedirectUrl
1167
+ );
1168
+ const dialogUrl = this.getDialogUrl();
1169
+ const signingUrl = `${dialogUrl}/dialog/sign/${request.requestId}?mode=redirect&redirectUrl=${encodeURIComponent(finalRedirectUrl)}`;
1170
+ window.location.href = signingUrl;
1171
+ }
1172
+ async signWithEmbed(options, embedOptions) {
1173
+ const request = await this.createSigningRequest(options, "embed");
1174
+ const iframe = this.createEmbed(request.requestId, embedOptions);
1175
+ return this.waitForEmbedResponse(request.requestId, iframe, embedOptions);
1176
+ }
1177
+ createEmbed(requestId, options) {
1178
+ const dialogUrl = this.getDialogUrl();
1179
+ const iframe = document.createElement("iframe");
1180
+ iframe.src = `${dialogUrl}/dialog/sign/${requestId}?mode=iframe`;
1181
+ iframe.style.width = options.width || DEFAULT_EMBED_WIDTH;
1182
+ iframe.style.height = options.height || DEFAULT_EMBED_HEIGHT;
1183
+ iframe.style.border = "none";
1184
+ iframe.style.borderRadius = "12px";
1185
+ iframe.style.boxShadow = "0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)";
1186
+ iframe.id = `passkey-embed-${requestId}`;
1187
+ iframe.allow = "publickey-credentials-get *; publickey-credentials-create *";
1188
+ iframe.onload = () => {
1189
+ options.onReady?.();
1190
+ };
1191
+ options.container.appendChild(iframe);
1192
+ return iframe;
1193
+ }
1194
+ waitForEmbedResponse(requestId, iframe, embedOptions) {
1195
+ const dialogOrigin = this.getDialogOrigin();
1196
+ return new Promise((resolve) => {
1197
+ const cleanup = () => {
1198
+ window.removeEventListener("message", handleMessage);
1199
+ iframe.remove();
1200
+ embedOptions.onClose?.();
1201
+ };
1202
+ const handleMessage = (event) => {
1203
+ if (event.origin !== dialogOrigin) {
1204
+ return;
1205
+ }
1206
+ const message = event.data;
1207
+ const payload = message?.data;
1208
+ if (message?.type === "PASSKEY_SIGNING_RESULT" && payload?.requestId === requestId) {
1209
+ cleanup();
1210
+ if (message.success && payload.signature) {
1211
+ resolve({
1212
+ success: true,
1213
+ requestId,
1214
+ signature: payload.signature
1215
+ });
1216
+ } else {
1217
+ resolve({
1218
+ success: false,
1219
+ requestId,
1220
+ error: message.error
1221
+ });
1222
+ }
1223
+ }
1224
+ };
1225
+ window.addEventListener("message", handleMessage);
1226
+ });
1227
+ }
1228
+ removeEmbed(requestId) {
1229
+ const iframe = document.getElementById(`passkey-embed-${requestId}`);
1230
+ if (iframe) {
1231
+ iframe.remove();
1232
+ }
1233
+ }
1234
+ async handleRedirectCallback() {
1235
+ const params = new URLSearchParams(window.location.search);
1236
+ const requestId = params.get("request_id");
1237
+ const status = params.get("status");
1238
+ const error = params.get("error");
1239
+ const errorMessage = params.get("error_message");
1240
+ if (error) {
1241
+ return {
1242
+ success: false,
1243
+ requestId: requestId || void 0,
1244
+ error: {
1245
+ code: error,
1246
+ message: errorMessage || "Unknown error"
1247
+ }
1248
+ };
1249
+ }
1250
+ if (!requestId) {
1251
+ return {
1252
+ success: false,
1253
+ error: {
1254
+ code: "INVALID_REQUEST",
1255
+ message: "No request_id found in callback URL"
1256
+ }
1257
+ };
1258
+ }
1259
+ if (status !== "completed") {
1260
+ return {
1261
+ success: false,
1262
+ requestId,
1263
+ error: {
1264
+ code: "UNKNOWN",
1265
+ message: `Unexpected status: ${status}`
1266
+ }
1267
+ };
1268
+ }
1269
+ return this.fetchSigningResult(requestId);
1270
+ }
1271
+ /**
1272
+ * Fetch passkeys for a user from the auth provider
1273
+ */
1274
+ async getPasskeys(username) {
1275
+ const response = await fetch(
1276
+ `${this.config.providerUrl}/api/users/${encodeURIComponent(username)}/passkeys`,
1277
+ {
1278
+ headers: {
1279
+ "x-client-id": this.config.clientId
1280
+ }
1281
+ }
1282
+ );
1283
+ if (!response.ok) {
1284
+ const errorData = await response.json().catch(() => ({}));
1285
+ throw new Error(errorData.error || errorData.message || "Failed to fetch passkeys");
1286
+ }
1287
+ const data = await response.json();
1288
+ return data.passkeys;
1289
+ }
1290
+ async createSigningRequest(options, mode, redirectUrl) {
1291
+ const response = await fetch(
1292
+ `${this.config.providerUrl}/api/sign/request`,
1293
+ {
1294
+ method: "POST",
1295
+ headers: {
1296
+ "Content-Type": "application/json"
1297
+ },
1298
+ body: JSON.stringify({
1299
+ clientId: this.config.clientId,
1300
+ username: options.username,
1301
+ challenge: options.challenge,
1302
+ description: options.description,
1303
+ metadata: options.metadata,
1304
+ transaction: options.transaction,
1305
+ mode,
1306
+ redirectUrl
1307
+ })
1308
+ }
1309
+ );
1310
+ if (!response.ok) {
1311
+ const errorData = await response.json().catch(() => ({}));
1312
+ throw new Error(errorData.error || errorData.message || "Failed to create signing request");
1313
+ }
1314
+ return response.json();
1315
+ }
1316
+ openPopup(url) {
1317
+ const left = window.screenX + (window.outerWidth - POPUP_WIDTH) / 2;
1318
+ const top = window.screenY + 50;
1319
+ return window.open(
1320
+ url,
1321
+ "passkey-signing",
1322
+ `width=${POPUP_WIDTH},height=${POPUP_HEIGHT},left=${left},top=${top},popup=true`
1323
+ );
1324
+ }
1325
+ /**
1326
+ * Create a modal dialog with an iframe inside (Porto-style overlay)
1327
+ */
1328
+ createModalDialog(url) {
1329
+ const dialogUrl = this.getDialogUrl();
1330
+ const hostUrl = new URL(dialogUrl);
1331
+ const dialog = document.createElement("dialog");
1332
+ dialog.dataset.passkey = "";
1333
+ document.body.appendChild(dialog);
1334
+ const style = document.createElement("style");
1335
+ style.textContent = `
1336
+ dialog[data-passkey] {
1337
+ padding: 0;
1338
+ border: none;
1339
+ background: transparent;
1340
+ max-width: none;
1341
+ max-height: none;
1342
+ margin: 0;
1343
+ position: fixed;
1344
+ top: 50px;
1345
+ left: 50%;
1346
+ transform: translateX(-50%);
1347
+ outline: none;
1348
+ }
1349
+
1350
+ dialog[data-passkey]::backdrop {
1351
+ background-color: rgba(0, 0, 0, 0.4);
1352
+ backdrop-filter: blur(8px);
1353
+ -webkit-backdrop-filter: blur(8px);
1354
+ }
1355
+
1356
+ dialog[data-passkey] iframe {
1357
+ background-color: transparent;
1358
+ border-radius: 14px;
1359
+ border: none;
1360
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.12), 0 2px 8px rgba(0, 0, 0, 0.08);
1361
+ transition: width 0.2s ease-out, height 0.15s ease-out;
1362
+ }
1363
+
1364
+ @media (min-width: 769px) {
1365
+ dialog[data-passkey] iframe {
1366
+ animation: passkey_zoomIn 0.2s cubic-bezier(0.32, 0.72, 0, 1);
1367
+ }
1368
+ }
1369
+
1370
+ @media (max-width: 768px) {
1371
+ dialog[data-passkey] {
1372
+ width: 100vw !important;
1373
+ height: auto !important;
1374
+ max-height: 90vh !important;
1375
+ max-height: 90dvh !important;
1376
+ top: auto !important;
1377
+ bottom: 0 !important;
1378
+ left: 0 !important;
1379
+ right: 0 !important;
1380
+ transform: none !important;
1381
+ margin: 0 !important;
1382
+ }
1383
+
1384
+ dialog[data-passkey] iframe {
1385
+ animation: passkey_slideFromBottom 0.3s cubic-bezier(0.32, 0.72, 0, 1);
1386
+ border-bottom-left-radius: 0 !important;
1387
+ border-bottom-right-radius: 0 !important;
1388
+ width: 100% !important;
1389
+ max-height: 90vh !important;
1390
+ max-height: 90dvh !important;
1391
+ box-shadow: 0 -4px 32px rgba(0, 0, 0, 0.15) !important;
1392
+ }
1393
+ }
1394
+
1395
+ @keyframes passkey_zoomIn {
1396
+ from {
1397
+ opacity: 0;
1398
+ transform: scale(0.96) translateY(8px);
1399
+ }
1400
+ to {
1401
+ opacity: 1;
1402
+ transform: scale(1) translateY(0);
1403
+ }
1404
+ }
1405
+
1406
+ @keyframes passkey_slideFromBottom {
1407
+ from { transform: translate3d(0, 100%, 0); }
1408
+ to { transform: translate3d(0, 0, 0); }
1409
+ }
1410
+
1411
+ @keyframes passkey_shake {
1412
+ 0%, 100% { transform: translateX(0); }
1413
+ 20% { transform: translateX(-4px); }
1414
+ 40% { transform: translateX(4px); }
1415
+ 60% { transform: translateX(-4px); }
1416
+ 80% { transform: translateX(4px); }
1417
+ }
1418
+ `;
1419
+ dialog.appendChild(style);
1420
+ const iframe = document.createElement("iframe");
1421
+ iframe.setAttribute(
1422
+ "allow",
1423
+ "publickey-credentials-get *; publickey-credentials-create *; clipboard-write"
1424
+ );
1425
+ iframe.setAttribute("aria-label", "Passkey Authentication");
1426
+ iframe.setAttribute("tabindex", "0");
1427
+ iframe.setAttribute(
1428
+ "sandbox",
1429
+ "allow-forms allow-scripts allow-same-origin allow-popups allow-popups-to-escape-sandbox"
1430
+ );
1431
+ iframe.setAttribute("src", url);
1432
+ iframe.setAttribute("title", "Passkey");
1433
+ iframe.style.border = "none";
1434
+ iframe.style.height = "400px";
1435
+ iframe.style.width = `${MODAL_WIDTH}px`;
1436
+ dialog.appendChild(iframe);
1437
+ const handleMessage = (event) => {
1438
+ if (event.origin !== hostUrl.origin) return;
1439
+ if (event.data?.type === "PASSKEY_RESIZE") {
1440
+ iframe.style.height = `${event.data.height}px`;
1441
+ if (event.data.width) {
1442
+ iframe.style.width = `${event.data.width}px`;
1443
+ }
1444
+ } else if (event.data?.type === "PASSKEY_DISCONNECT") {
1445
+ localStorage.removeItem("1auth-user");
1446
+ }
1447
+ };
1448
+ window.addEventListener("message", handleMessage);
1449
+ const handleEscape = (event) => {
1450
+ if (event.key === "Escape") {
1451
+ cleanup();
1452
+ }
1453
+ };
1454
+ document.addEventListener("keydown", handleEscape);
1455
+ dialog.addEventListener("click", (event) => {
1456
+ if (event.target === dialog) {
1457
+ cleanup();
1458
+ }
1459
+ });
1460
+ dialog.showModal();
1461
+ const cleanup = () => {
1462
+ window.removeEventListener("message", handleMessage);
1463
+ document.removeEventListener("keydown", handleEscape);
1464
+ dialog.close();
1465
+ dialog.remove();
1466
+ };
1467
+ return { dialog, iframe, cleanup };
1468
+ }
1469
+ waitForModalAuthResponse(_dialog, _iframe, cleanup) {
1470
+ const dialogOrigin = this.getDialogOrigin();
1471
+ return new Promise((resolve) => {
1472
+ const handleMessage = (event) => {
1473
+ if (event.origin !== dialogOrigin) return;
1474
+ const data = event.data;
1475
+ if (data?.type === "PASSKEY_LOGIN_RESULT") {
1476
+ window.removeEventListener("message", handleMessage);
1477
+ cleanup();
1478
+ if (data.success) {
1479
+ resolve({
1480
+ success: true,
1481
+ username: data.data?.username,
1482
+ user: data.data?.user
1483
+ });
1484
+ } else {
1485
+ resolve({
1486
+ success: false,
1487
+ error: data.error
1488
+ });
1489
+ }
1490
+ } else if (data?.type === "PASSKEY_REGISTER_RESULT") {
1491
+ window.removeEventListener("message", handleMessage);
1492
+ cleanup();
1493
+ if (data.success) {
1494
+ resolve({
1495
+ success: true,
1496
+ username: data.data?.username
1497
+ });
1498
+ } else {
1499
+ resolve({
1500
+ success: false,
1501
+ error: data.error
1502
+ });
1503
+ }
1504
+ } else if (data?.type === "PASSKEY_CLOSE") {
1505
+ window.removeEventListener("message", handleMessage);
1506
+ cleanup();
1507
+ resolve({
1508
+ success: false,
1509
+ error: {
1510
+ code: "USER_CANCELLED",
1511
+ message: "Authentication was cancelled"
1512
+ }
1513
+ });
1514
+ }
1515
+ };
1516
+ window.addEventListener("message", handleMessage);
1517
+ });
1518
+ }
1519
+ waitForAuthenticateResponse(_dialog, _iframe, cleanup) {
1520
+ const dialogOrigin = this.getDialogOrigin();
1521
+ return new Promise((resolve) => {
1522
+ const handleMessage = (event) => {
1523
+ if (event.origin !== dialogOrigin) return;
1524
+ const data = event.data;
1525
+ if (data?.type === "PASSKEY_AUTHENTICATE_RESULT") {
1526
+ window.removeEventListener("message", handleMessage);
1527
+ cleanup();
1528
+ if (data.success) {
1529
+ resolve({
1530
+ success: true,
1531
+ username: data.data?.username,
1532
+ user: data.data?.user,
1533
+ accountAddress: data.data?.accountAddress,
1534
+ signature: data.data?.signature,
1535
+ signedHash: data.data?.signedHash
1536
+ });
1537
+ } else {
1538
+ resolve({
1539
+ success: false,
1540
+ error: data.error
1541
+ });
1542
+ }
1543
+ } else if (data?.type === "PASSKEY_CLOSE") {
1544
+ window.removeEventListener("message", handleMessage);
1545
+ cleanup();
1546
+ resolve({
1547
+ success: false,
1548
+ error: {
1549
+ code: "USER_CANCELLED",
1550
+ message: "Authentication was cancelled"
1551
+ }
1552
+ });
1553
+ }
1554
+ };
1555
+ window.addEventListener("message", handleMessage);
1556
+ });
1557
+ }
1558
+ waitForModalSigningResponse(requestId, _dialog, _iframe, cleanup) {
1559
+ const dialogOrigin = this.getDialogOrigin();
1560
+ return new Promise((resolve) => {
1561
+ const handleMessage = (event) => {
1562
+ if (event.origin !== dialogOrigin) return;
1563
+ const message = event.data;
1564
+ const payload = message?.data;
1565
+ if (message?.type === "PASSKEY_SIGNING_RESULT" && payload?.requestId === requestId) {
1566
+ window.removeEventListener("message", handleMessage);
1567
+ cleanup();
1568
+ if (message.success && payload.signature) {
1569
+ resolve({
1570
+ success: true,
1571
+ requestId,
1572
+ signature: payload.signature
1573
+ });
1574
+ } else {
1575
+ resolve({
1576
+ success: false,
1577
+ requestId,
1578
+ error: message.error
1579
+ });
1580
+ }
1581
+ } else if (message?.type === "PASSKEY_CLOSE") {
1582
+ window.removeEventListener("message", handleMessage);
1583
+ cleanup();
1584
+ resolve({
1585
+ success: false,
1586
+ requestId,
1587
+ error: {
1588
+ code: "USER_REJECTED",
1589
+ message: "Signing was cancelled"
1590
+ }
1591
+ });
1592
+ }
1593
+ };
1594
+ window.addEventListener("message", handleMessage);
1595
+ });
1596
+ }
1597
+ waitForPopupResponse(requestId, popup) {
1598
+ const dialogOrigin = this.getDialogOrigin();
1599
+ return new Promise((resolve) => {
1600
+ const checkClosed = setInterval(() => {
1601
+ if (popup.closed) {
1602
+ clearInterval(checkClosed);
1603
+ window.removeEventListener("message", handleMessage);
1604
+ resolve({
1605
+ success: false,
1606
+ requestId,
1607
+ error: {
1608
+ code: "USER_REJECTED",
1609
+ message: "Popup was closed without completing"
1610
+ }
1611
+ });
1612
+ }
1613
+ }, 500);
1614
+ const handleMessage = (event) => {
1615
+ if (event.origin !== dialogOrigin) {
1616
+ return;
1617
+ }
1618
+ const message = event.data;
1619
+ const payload = message?.data;
1620
+ if (message?.type === "PASSKEY_SIGNING_RESULT" && payload?.requestId === requestId) {
1621
+ clearInterval(checkClosed);
1622
+ window.removeEventListener("message", handleMessage);
1623
+ popup.close();
1624
+ if (message.success && payload.signature) {
1625
+ resolve({
1626
+ success: true,
1627
+ requestId,
1628
+ signature: payload.signature
1629
+ });
1630
+ } else {
1631
+ resolve({
1632
+ success: false,
1633
+ requestId,
1634
+ error: message.error
1635
+ });
1636
+ }
1637
+ }
1638
+ };
1639
+ window.addEventListener("message", handleMessage);
1640
+ });
1641
+ }
1642
+ async fetchSigningResult(requestId) {
1643
+ const response = await fetch(
1644
+ `${this.config.providerUrl}/api/sign/request/${requestId}`,
1645
+ {
1646
+ headers: {
1647
+ "x-client-id": this.config.clientId
1648
+ }
1649
+ }
1650
+ );
1651
+ if (!response.ok) {
1652
+ return {
1653
+ success: false,
1654
+ requestId,
1655
+ error: {
1656
+ code: "NETWORK_ERROR",
1657
+ message: "Failed to fetch signing result"
1658
+ }
1659
+ };
1660
+ }
1661
+ const data = await response.json();
1662
+ if (data.status === "COMPLETED" && data.signature) {
1663
+ return {
1664
+ success: true,
1665
+ requestId,
1666
+ signature: data.signature
1667
+ };
1668
+ }
1669
+ const errorCode = data.error?.code || "UNKNOWN";
1670
+ return {
1671
+ success: false,
1672
+ requestId,
1673
+ error: {
1674
+ code: errorCode,
1675
+ message: data.error?.message || `Request status: ${data.status}`
1676
+ }
1677
+ };
1678
+ }
1679
+ };
1680
+
1681
+ // src/provider.ts
1682
+ var import_viem4 = require("viem");
1683
+
1684
+ // src/walletClient/utils.ts
1685
+ var import_viem3 = require("viem");
1686
+ var P256_N = BigInt(
1687
+ "0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551"
1688
+ );
1689
+ var P256_N_DIV_2 = P256_N / 2n;
1690
+ var WEBAUTHN_AUTH_TYPE = {
1691
+ type: "tuple",
1692
+ components: [
1693
+ { type: "bytes", name: "authenticatorData" },
1694
+ { type: "string", name: "clientDataJSON" },
1695
+ { type: "uint256", name: "challengeIndex" },
1696
+ { type: "uint256", name: "typeIndex" },
1697
+ { type: "uint256", name: "r" },
1698
+ { type: "uint256", name: "s" }
1699
+ ]
1700
+ };
1701
+ function encodeWebAuthnSignature(sig) {
1702
+ let s = BigInt(sig.s);
1703
+ if (s > P256_N_DIV_2) {
1704
+ s = P256_N - s;
1705
+ }
1706
+ return (0, import_viem3.encodeAbiParameters)([WEBAUTHN_AUTH_TYPE], [
1707
+ {
1708
+ authenticatorData: sig.authenticatorData,
1709
+ clientDataJSON: sig.clientDataJSON,
1710
+ challengeIndex: BigInt(sig.challengeIndex),
1711
+ typeIndex: BigInt(sig.typeIndex),
1712
+ r: BigInt(sig.r),
1713
+ s
1714
+ }
1715
+ ]);
1716
+ }
1717
+ function hashCalls(calls) {
1718
+ const encoded = (0, import_viem3.encodeAbiParameters)(
1719
+ [
1720
+ {
1721
+ type: "tuple[]",
1722
+ components: [
1723
+ { type: "address", name: "to" },
1724
+ { type: "bytes", name: "data" },
1725
+ { type: "uint256", name: "value" }
1726
+ ]
1727
+ }
1728
+ ],
1729
+ [
1730
+ calls.map((c) => ({
1731
+ to: c.to,
1732
+ data: c.data || "0x",
1733
+ value: c.value || 0n
1734
+ }))
1735
+ ]
1736
+ );
1737
+ return (0, import_viem3.keccak256)(encoded);
1738
+ }
1739
+ function buildTransactionReview(calls) {
1740
+ return {
1741
+ actions: calls.map((call, i) => ({
1742
+ type: "custom",
1743
+ label: call.label || `Contract Call ${i + 1}`,
1744
+ sublabel: call.sublabel || `To: ${call.to.slice(0, 10)}...${call.to.slice(-8)}`,
1745
+ amount: call.value ? `${call.value} wei` : void 0
1746
+ }))
1747
+ };
1748
+ }
1749
+
1750
+ // src/provider.ts
1751
+ var DEFAULT_STORAGE_KEY = "1auth-user";
1752
+ function createPasskeyProvider(options) {
1753
+ const { client } = options;
1754
+ let chainId = options.chainId;
1755
+ const storageKey = options.storageKey || DEFAULT_STORAGE_KEY;
1756
+ const listeners = /* @__PURE__ */ new Map();
1757
+ const emit = (event, ...args) => {
1758
+ const set = listeners.get(event);
1759
+ if (!set) return;
1760
+ for (const listener of set) listener(...args);
1761
+ };
1762
+ const getStoredUser = () => {
1763
+ if (typeof window === "undefined") return null;
1764
+ try {
1765
+ const raw = localStorage.getItem(storageKey);
1766
+ if (!raw) return null;
1767
+ const parsed = JSON.parse(raw);
1768
+ if (!parsed?.username || !parsed?.address) return null;
1769
+ return parsed;
1770
+ } catch {
1771
+ return null;
1772
+ }
1773
+ };
1774
+ const setStoredUser = (user) => {
1775
+ if (typeof window === "undefined") return;
1776
+ localStorage.setItem(storageKey, JSON.stringify(user));
1777
+ };
1778
+ const clearStoredUser = () => {
1779
+ if (typeof window === "undefined") return;
1780
+ localStorage.removeItem(storageKey);
1781
+ };
1782
+ const resolveAccountAddress = async (username) => {
1783
+ const response = await fetch(
1784
+ `${client.getProviderUrl()}/api/users/${encodeURIComponent(username)}/account`,
1785
+ {
1786
+ headers: {
1787
+ "x-client-id": client.getClientId()
1788
+ }
1789
+ }
1790
+ );
1791
+ if (!response.ok) {
1792
+ const data2 = await response.json().catch(() => ({}));
1793
+ throw new Error(data2.error || "Failed to resolve account address");
1794
+ }
1795
+ const data = await response.json();
1796
+ return data.address;
1797
+ };
1798
+ const connect = async () => {
1799
+ const stored = getStoredUser();
1800
+ if (stored) {
1801
+ return [stored.address];
1802
+ }
1803
+ const result = await client.authWithModal();
1804
+ if (!result.success || !result.username) {
1805
+ throw new Error(result.error?.message || "Authentication failed");
1806
+ }
1807
+ const address = await resolveAccountAddress(result.username);
1808
+ setStoredUser({ username: result.username, address });
1809
+ emit("accountsChanged", [address]);
1810
+ emit("connect", { chainId: (0, import_viem4.numberToHex)(chainId) });
1811
+ return [address];
1812
+ };
1813
+ const disconnect = async () => {
1814
+ clearStoredUser();
1815
+ emit("accountsChanged", []);
1816
+ emit("disconnect");
1817
+ };
1818
+ const ensureUser = async () => {
1819
+ const stored = getStoredUser();
1820
+ if (stored) return stored;
1821
+ const [address] = await connect();
1822
+ const username = getStoredUser()?.username;
1823
+ if (!username || !address) {
1824
+ throw new Error("Failed to resolve user session");
1825
+ }
1826
+ return { username, address };
1827
+ };
1828
+ const parseChainId = (value) => {
1829
+ if (typeof value === "number") return value;
1830
+ if (typeof value === "string") {
1831
+ if (value.startsWith("0x")) return Number.parseInt(value, 16);
1832
+ const parsed = Number(value);
1833
+ return Number.isFinite(parsed) ? parsed : void 0;
1834
+ }
1835
+ return void 0;
1836
+ };
1837
+ const normalizeValue = (value) => {
1838
+ if (value === void 0 || value === null) return void 0;
1839
+ if (typeof value === "bigint") return value.toString();
1840
+ if (typeof value === "number") return Math.trunc(value).toString();
1841
+ if (typeof value === "string") {
1842
+ if (value.startsWith("0x")) {
1843
+ try {
1844
+ return BigInt(value).toString();
1845
+ } catch {
1846
+ return "0";
1847
+ }
1848
+ }
1849
+ return value;
1850
+ }
1851
+ return void 0;
1852
+ };
1853
+ const normalizeCalls = (calls) => {
1854
+ return calls.map((call) => {
1855
+ const c = call;
1856
+ return {
1857
+ to: c.to,
1858
+ data: c.data || "0x",
1859
+ value: normalizeValue(c.value) || "0",
1860
+ label: c.label,
1861
+ sublabel: c.sublabel
1862
+ };
1863
+ });
1864
+ };
1865
+ const decodeMessage = (value) => {
1866
+ if (!(0, import_viem4.isHex)(value)) return value;
1867
+ try {
1868
+ return (0, import_viem4.hexToString)(value);
1869
+ } catch {
1870
+ return value;
1871
+ }
1872
+ };
1873
+ const signMessage = async (message) => {
1874
+ const { username } = await ensureUser();
1875
+ const result = await client.signMessage({
1876
+ username,
1877
+ message
1878
+ });
1879
+ if (!result.success || !result.signature) {
1880
+ throw new Error(result.error?.message || "Signing failed");
1881
+ }
1882
+ return encodeWebAuthnSignature(result.signature);
1883
+ };
1884
+ const signTypedData = async (typedData) => {
1885
+ const { username } = await ensureUser();
1886
+ const data = typeof typedData === "string" ? JSON.parse(typedData) : typedData;
1887
+ const result = await client.signTypedData({
1888
+ username,
1889
+ domain: data.domain,
1890
+ types: data.types,
1891
+ primaryType: data.primaryType,
1892
+ message: data.message
1893
+ });
1894
+ if (!result.success || !result.signature) {
1895
+ throw new Error(result.error?.message || "Signing failed");
1896
+ }
1897
+ return encodeWebAuthnSignature(result.signature);
1898
+ };
1899
+ const sendIntent = async (payload) => {
1900
+ const closeOn = options.waitForHash ?? true ? "completed" : "preconfirmed";
1901
+ const result = await client.sendIntent({
1902
+ ...payload,
1903
+ closeOn,
1904
+ waitForHash: options.waitForHash ?? true,
1905
+ hashTimeoutMs: options.hashTimeoutMs,
1906
+ hashIntervalMs: options.hashIntervalMs
1907
+ });
1908
+ if (!result.success || !result.transactionHash) {
1909
+ throw new Error(result.error?.message || "Transaction failed");
1910
+ }
1911
+ return result.transactionHash;
1912
+ };
1913
+ const request = async ({ method, params }) => {
1914
+ switch (method) {
1915
+ case "eth_chainId":
1916
+ return (0, import_viem4.numberToHex)(chainId);
1917
+ case "eth_accounts": {
1918
+ const stored = getStoredUser();
1919
+ return stored ? [stored.address] : [];
1920
+ }
1921
+ case "eth_requestAccounts":
1922
+ return connect();
1923
+ case "wallet_connect":
1924
+ return connect();
1925
+ case "wallet_disconnect":
1926
+ await disconnect();
1927
+ return true;
1928
+ case "wallet_switchEthereumChain": {
1929
+ const [param] = params || [];
1930
+ const next = parseChainId(param?.chainId ?? param);
1931
+ if (!next) {
1932
+ throw new Error("Invalid chainId");
1933
+ }
1934
+ chainId = next;
1935
+ emit("chainChanged", (0, import_viem4.numberToHex)(chainId));
1936
+ return null;
1937
+ }
1938
+ case "personal_sign": {
1939
+ const paramList = Array.isArray(params) ? params : [];
1940
+ const first = paramList[0];
1941
+ const second = paramList[1];
1942
+ const message = typeof first === "string" && first.startsWith("0x") && second ? typeof second === "string" && !second.startsWith("0x") ? second : decodeMessage(first) : typeof first === "string" ? decodeMessage(first) : typeof second === "string" ? decodeMessage(second) : "";
1943
+ if (!message) throw new Error("Invalid personal_sign payload");
1944
+ return signMessage(message);
1945
+ }
1946
+ case "eth_sign": {
1947
+ const paramList = Array.isArray(params) ? params : [];
1948
+ const message = typeof paramList[1] === "string" ? paramList[1] : "";
1949
+ if (!message) throw new Error("Invalid eth_sign payload");
1950
+ return signMessage(decodeMessage(message));
1951
+ }
1952
+ case "eth_signTypedData":
1953
+ case "eth_signTypedData_v4": {
1954
+ const paramList = Array.isArray(params) ? params : [];
1955
+ const typedData = paramList[1] ?? paramList[0];
1956
+ return signTypedData(typedData);
1957
+ }
1958
+ case "eth_sendTransaction": {
1959
+ const paramList = Array.isArray(params) ? params : [];
1960
+ const tx = paramList[0] || {};
1961
+ const user = await ensureUser();
1962
+ const targetChain = parseChainId(tx.chainId) ?? chainId;
1963
+ const calls = normalizeCalls([tx]);
1964
+ return sendIntent({
1965
+ username: user.username,
1966
+ targetChain,
1967
+ calls
1968
+ });
1969
+ }
1970
+ case "wallet_sendCalls": {
1971
+ const paramList = Array.isArray(params) ? params : [];
1972
+ const payload = paramList[0] || {};
1973
+ const user = await ensureUser();
1974
+ const targetChain = parseChainId(payload.chainId) ?? chainId;
1975
+ const calls = normalizeCalls(payload.calls || []);
1976
+ if (!calls.length) throw new Error("No calls provided");
1977
+ return sendIntent({
1978
+ username: user.username,
1979
+ targetChain,
1980
+ calls
1981
+ });
1982
+ }
1983
+ case "wallet_getCapabilities": {
1984
+ const chainIds = getSupportedChainIds();
1985
+ const tokensByChain = Object.fromEntries(
1986
+ chainIds.map((id) => [id, getSupportedTokens(id)])
1987
+ );
1988
+ return {
1989
+ chains: chainIds,
1990
+ tokens: tokensByChain
1991
+ };
1992
+ }
1993
+ default:
1994
+ throw new Error(`Unsupported method: ${method}`);
1995
+ }
1996
+ };
1997
+ return {
1998
+ request,
1999
+ on(event, listener) {
2000
+ const set = listeners.get(event) ?? /* @__PURE__ */ new Set();
2001
+ set.add(listener);
2002
+ listeners.set(event, set);
2003
+ },
2004
+ removeListener(event, listener) {
2005
+ const set = listeners.get(event);
2006
+ if (!set) return;
2007
+ set.delete(listener);
2008
+ if (set.size === 0) listeners.delete(event);
2009
+ },
2010
+ disconnect
2011
+ };
2012
+ }
2013
+
2014
+ // src/account.ts
2015
+ var import_viem5 = require("viem");
2016
+ var import_accounts = require("viem/accounts");
2017
+ function createPasskeyAccount(client, params) {
2018
+ const { address, username } = params;
2019
+ const normalizeMessage = (message) => {
2020
+ if (typeof message === "string") return message;
2021
+ const raw = message.raw;
2022
+ if ((0, import_viem5.isHex)(raw)) {
2023
+ try {
2024
+ return (0, import_viem5.hexToString)(raw);
2025
+ } catch {
2026
+ return raw;
2027
+ }
2028
+ }
2029
+ return (0, import_viem5.bytesToString)(raw);
2030
+ };
2031
+ const account = (0, import_accounts.toAccount)({
2032
+ address,
2033
+ signMessage: async ({ message }) => {
2034
+ const result = await client.signMessage({
2035
+ username,
2036
+ message: normalizeMessage(message)
2037
+ });
2038
+ if (!result.success || !result.signature) {
2039
+ throw new Error(result.error?.message || "Signing failed");
2040
+ }
2041
+ return encodeWebAuthnSignature(result.signature);
2042
+ },
2043
+ signTransaction: async () => {
2044
+ throw new Error("signTransaction not supported; use sendIntent");
2045
+ },
2046
+ signTypedData: async (typedData) => {
2047
+ if (!typedData.domain || !typedData.types || !typedData.primaryType) {
2048
+ throw new Error("Invalid typed data");
2049
+ }
2050
+ const domainInput = typedData.domain;
2051
+ if (!domainInput.name || !domainInput.version) {
2052
+ throw new Error("Typed data domain must include name and version");
2053
+ }
2054
+ const domain = {
2055
+ name: domainInput.name,
2056
+ version: domainInput.version,
2057
+ chainId: typeof domainInput.chainId === "bigint" ? Number(domainInput.chainId) : domainInput.chainId,
2058
+ verifyingContract: domainInput.verifyingContract,
2059
+ salt: domainInput.salt
2060
+ };
2061
+ const rawTypes = typedData.types;
2062
+ const normalizedTypes = Object.fromEntries(
2063
+ Object.entries(rawTypes).map(([key, fields]) => [
2064
+ key,
2065
+ fields.map((field) => ({ name: field.name, type: field.type }))
2066
+ ])
2067
+ );
2068
+ const result = await client.signTypedData({
2069
+ username,
2070
+ domain,
2071
+ types: normalizedTypes,
2072
+ primaryType: typedData.primaryType,
2073
+ message: typedData.message
2074
+ });
2075
+ if (!result.success || !result.signature) {
2076
+ throw new Error(result.error?.message || "Signing failed");
2077
+ }
2078
+ return encodeWebAuthnSignature(result.signature);
2079
+ }
2080
+ });
2081
+ return {
2082
+ ...account,
2083
+ username
2084
+ };
2085
+ }
2086
+
2087
+ // src/walletClient/index.ts
2088
+ var import_viem6 = require("viem");
2089
+ var import_accounts2 = require("viem/accounts");
2090
+ function createPasskeyWalletClient(config) {
2091
+ const provider = new PasskeyProviderClient({
2092
+ providerUrl: config.providerUrl,
2093
+ clientId: config.clientId,
2094
+ dialogUrl: config.dialogUrl
2095
+ });
2096
+ const signMessageImpl = async (message) => {
2097
+ const hash = (0, import_viem6.hashMessage)(message);
2098
+ const result = await provider.signWithModal({
2099
+ challenge: hash,
2100
+ username: config.username,
2101
+ description: "Sign message",
2102
+ transaction: {
2103
+ actions: [
2104
+ {
2105
+ type: "custom",
2106
+ label: "Sign Message",
2107
+ sublabel: typeof message === "string" ? message.slice(0, 50) + (message.length > 50 ? "..." : "") : "Raw message"
2108
+ }
2109
+ ]
2110
+ }
2111
+ });
2112
+ if (!result.success) {
2113
+ throw new Error(result.error?.message || "Signing failed");
2114
+ }
2115
+ return encodeWebAuthnSignature(result.signature);
2116
+ };
2117
+ const signTransactionImpl = async (transaction) => {
2118
+ const calls = [
2119
+ {
2120
+ to: transaction.to,
2121
+ data: transaction.data,
2122
+ value: transaction.value
2123
+ }
2124
+ ];
2125
+ const hash = hashCalls(calls);
2126
+ const result = await provider.signWithModal({
2127
+ challenge: hash,
2128
+ username: config.username,
2129
+ description: "Sign transaction",
2130
+ transaction: buildTransactionReview(calls)
2131
+ });
2132
+ if (!result.success) {
2133
+ throw new Error(result.error?.message || "Signing failed");
2134
+ }
2135
+ return encodeWebAuthnSignature(result.signature);
2136
+ };
2137
+ const signTypedDataImpl = async (typedData) => {
2138
+ const hash = (0, import_viem6.hashTypedData)(typedData);
2139
+ const result = await provider.signWithModal({
2140
+ challenge: hash,
2141
+ username: config.username,
2142
+ description: "Sign typed data",
2143
+ transaction: {
2144
+ actions: [
2145
+ {
2146
+ type: "custom",
2147
+ label: "Sign Data",
2148
+ sublabel: typedData.primaryType || "Typed Data"
2149
+ }
2150
+ ]
2151
+ }
2152
+ });
2153
+ if (!result.success) {
2154
+ throw new Error(result.error?.message || "Signing failed");
2155
+ }
2156
+ return encodeWebAuthnSignature(result.signature);
2157
+ };
2158
+ const account = (0, import_accounts2.toAccount)({
2159
+ address: config.accountAddress,
2160
+ signMessage: ({ message }) => signMessageImpl(message),
2161
+ signTransaction: signTransactionImpl,
2162
+ signTypedData: signTypedDataImpl
2163
+ });
2164
+ const client = (0, import_viem6.createWalletClient)({
2165
+ account,
2166
+ chain: config.chain,
2167
+ transport: config.transport
2168
+ });
2169
+ const extendedClient = Object.assign(client, {
2170
+ /**
2171
+ * Send a single transaction via intent flow
2172
+ */
2173
+ async sendTransaction(transaction) {
2174
+ const calls = [
2175
+ {
2176
+ to: transaction.to,
2177
+ data: transaction.data,
2178
+ value: transaction.value
2179
+ }
2180
+ ];
2181
+ const closeOn = config.waitForHash ?? true ? "completed" : "preconfirmed";
2182
+ const result = await provider.sendIntent({
2183
+ username: config.username,
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
+ })),
2192
+ closeOn,
2193
+ waitForHash: config.waitForHash ?? true,
2194
+ hashTimeoutMs: config.hashTimeoutMs,
2195
+ hashIntervalMs: config.hashIntervalMs
2196
+ });
2197
+ if (!result.success || !result.transactionHash) {
2198
+ throw new Error(result.error?.message || "Transaction failed");
2199
+ }
2200
+ return result.transactionHash;
2201
+ },
2202
+ /**
2203
+ * Send multiple calls as a single batched transaction
2204
+ */
2205
+ async sendCalls(params) {
2206
+ const { calls } = params;
2207
+ const closeOn = config.waitForHash ?? true ? "completed" : "preconfirmed";
2208
+ const result = await provider.sendIntent({
2209
+ username: config.username,
2210
+ targetChain: config.chain.id,
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
+ })),
2218
+ closeOn,
2219
+ waitForHash: config.waitForHash ?? true,
2220
+ hashTimeoutMs: config.hashTimeoutMs,
2221
+ hashIntervalMs: config.hashIntervalMs
2222
+ });
2223
+ if (!result.success || !result.transactionHash) {
2224
+ throw new Error(result.error?.message || "Transaction failed");
2225
+ }
2226
+ return result.transactionHash;
2227
+ }
2228
+ });
2229
+ return extendedClient;
2230
+ }
2231
+
2232
+ // src/batch/BatchQueueContext.tsx
2233
+ var React = __toESM(require("react"));
2234
+ var import_jsx_runtime = require("react/jsx-runtime");
2235
+ function getChainName2(chainId) {
2236
+ return getChainName(chainId);
2237
+ }
2238
+ var BatchQueueContext = React.createContext(null);
2239
+ function useBatchQueue() {
2240
+ const context = React.useContext(BatchQueueContext);
2241
+ if (!context) {
2242
+ throw new Error("useBatchQueue must be used within a BatchQueueProvider");
2243
+ }
2244
+ return context;
2245
+ }
2246
+ function generateId() {
2247
+ return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
2248
+ }
2249
+ function getStorageKey(username) {
2250
+ return username ? `1auth_batch_queue_${username}` : "1auth_batch_queue_anonymous";
2251
+ }
2252
+ function BatchQueueProvider({
2253
+ client,
2254
+ username,
2255
+ children
2256
+ }) {
2257
+ const [queue, setQueue] = React.useState([]);
2258
+ const [isExpanded, setExpanded] = React.useState(false);
2259
+ const [isSigning, setIsSigning] = React.useState(false);
2260
+ const [animationTrigger, setAnimationTrigger] = React.useState(0);
2261
+ const batchChainId = queue.length > 0 ? queue[0].targetChain : null;
2262
+ React.useEffect(() => {
2263
+ const storageKey = getStorageKey(username);
2264
+ try {
2265
+ const stored = localStorage.getItem(storageKey);
2266
+ if (stored) {
2267
+ const parsed = JSON.parse(stored);
2268
+ if (Array.isArray(parsed) && parsed.every(
2269
+ (item) => typeof item.id === "string" && typeof item.call === "object" && typeof item.targetChain === "number"
2270
+ )) {
2271
+ setQueue(parsed);
2272
+ }
2273
+ }
2274
+ } catch {
2275
+ }
2276
+ }, [username]);
2277
+ React.useEffect(() => {
2278
+ const storageKey = getStorageKey(username);
2279
+ try {
2280
+ if (queue.length > 0) {
2281
+ localStorage.setItem(storageKey, JSON.stringify(queue));
2282
+ } else {
2283
+ localStorage.removeItem(storageKey);
2284
+ }
2285
+ } catch {
2286
+ }
2287
+ }, [queue, username]);
2288
+ const addToBatch = React.useCallback((call, targetChain) => {
2289
+ if (batchChainId !== null && batchChainId !== targetChain) {
2290
+ return {
2291
+ success: false,
2292
+ error: `Batch is set to ${getChainName2(batchChainId)}. Sign current batch first or clear it.`
2293
+ };
2294
+ }
2295
+ const batchedCall = {
2296
+ id: generateId(),
2297
+ call,
2298
+ targetChain,
2299
+ addedAt: Date.now()
2300
+ };
2301
+ setQueue((prev) => [...prev, batchedCall]);
2302
+ setAnimationTrigger((prev) => prev + 1);
2303
+ return { success: true };
2304
+ }, [batchChainId]);
2305
+ const removeFromBatch = React.useCallback((id) => {
2306
+ setQueue((prev) => prev.filter((item) => item.id !== id));
2307
+ }, []);
2308
+ const clearBatch = React.useCallback(() => {
2309
+ setQueue([]);
2310
+ setExpanded(false);
2311
+ }, []);
2312
+ const signAll = React.useCallback(async (username2) => {
2313
+ if (queue.length === 0) {
2314
+ return {
2315
+ success: false,
2316
+ intentId: "",
2317
+ status: "failed",
2318
+ error: {
2319
+ code: "EMPTY_BATCH",
2320
+ message: "No calls in batch to sign"
2321
+ }
2322
+ };
2323
+ }
2324
+ const targetChain = queue[0].targetChain;
2325
+ const calls = queue.map((item) => item.call);
2326
+ setIsSigning(true);
2327
+ try {
2328
+ const result = await client.sendIntent({
2329
+ username: username2,
2330
+ targetChain,
2331
+ calls
2332
+ });
2333
+ if (result.success) {
2334
+ clearBatch();
2335
+ }
2336
+ return result;
2337
+ } finally {
2338
+ setIsSigning(false);
2339
+ }
2340
+ }, [queue, client, clearBatch]);
2341
+ const value = {
2342
+ queue,
2343
+ batchChainId,
2344
+ addToBatch,
2345
+ removeFromBatch,
2346
+ clearBatch,
2347
+ signAll,
2348
+ isExpanded,
2349
+ setExpanded,
2350
+ isSigning,
2351
+ animationTrigger
2352
+ };
2353
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BatchQueueContext.Provider, { value, children });
2354
+ }
2355
+
2356
+ // src/batch/BatchQueueWidget.tsx
2357
+ var React2 = __toESM(require("react"));
2358
+ var import_jsx_runtime2 = require("react/jsx-runtime");
2359
+ var ANIMATION_STYLES = `
2360
+ @keyframes batch-bounce-in {
2361
+ 0% { transform: scale(0.8); opacity: 0; }
2362
+ 50% { transform: scale(1.05); }
2363
+ 100% { transform: scale(1); opacity: 1; }
2364
+ }
2365
+ @keyframes batch-item-bounce {
2366
+ 0%, 100% { transform: translateX(0); }
2367
+ 25% { transform: translateX(-2px); }
2368
+ 75% { transform: translateX(2px); }
2369
+ }
2370
+ `;
2371
+ function injectStyles() {
2372
+ if (typeof document === "undefined") return;
2373
+ const styleId = "batch-queue-widget-styles";
2374
+ if (document.getElementById(styleId)) return;
2375
+ const style = document.createElement("style");
2376
+ style.id = styleId;
2377
+ style.textContent = ANIMATION_STYLES;
2378
+ document.head.appendChild(style);
2379
+ }
2380
+ var widgetStyles = {
2381
+ position: "fixed",
2382
+ bottom: "16px",
2383
+ right: "16px",
2384
+ zIndex: 50,
2385
+ backgroundColor: "#18181b",
2386
+ color: "#ffffff",
2387
+ borderRadius: "12px",
2388
+ boxShadow: "0 4px 12px rgba(0,0,0,0.3)",
2389
+ minWidth: "240px",
2390
+ maxWidth: "360px",
2391
+ fontFamily: "system-ui, -apple-system, sans-serif",
2392
+ fontSize: "14px",
2393
+ overflow: "hidden"
2394
+ };
2395
+ var headerStyles = {
2396
+ display: "flex",
2397
+ alignItems: "center",
2398
+ justifyContent: "space-between",
2399
+ padding: "12px 16px",
2400
+ cursor: "pointer",
2401
+ userSelect: "none"
2402
+ };
2403
+ var headerLeftStyles = {
2404
+ display: "flex",
2405
+ alignItems: "center",
2406
+ gap: "8px"
2407
+ };
2408
+ var counterBadgeStyles = {
2409
+ backgroundColor: "#ffffff",
2410
+ color: "#18181b",
2411
+ borderRadius: "9999px",
2412
+ padding: "2px 8px",
2413
+ fontSize: "12px",
2414
+ fontWeight: 600
2415
+ };
2416
+ var chainBadgeStyles = {
2417
+ backgroundColor: "rgba(255,255,255,0.15)",
2418
+ color: "#a1a1aa",
2419
+ borderRadius: "4px",
2420
+ padding: "2px 6px",
2421
+ fontSize: "11px"
2422
+ };
2423
+ var signAllButtonStyles = {
2424
+ backgroundColor: "#ffffff",
2425
+ color: "#18181b",
2426
+ border: "none",
2427
+ borderRadius: "6px",
2428
+ padding: "6px 12px",
2429
+ fontSize: "13px",
2430
+ fontWeight: 500,
2431
+ cursor: "pointer",
2432
+ transition: "background-color 0.15s"
2433
+ };
2434
+ var signAllButtonHoverStyles = {
2435
+ backgroundColor: "#e4e4e7"
2436
+ };
2437
+ var signAllButtonDisabledStyles = {
2438
+ opacity: 0.5,
2439
+ cursor: "not-allowed"
2440
+ };
2441
+ var listContainerStyles = {
2442
+ maxHeight: "300px",
2443
+ overflowY: "auto",
2444
+ borderTop: "1px solid #27272a"
2445
+ };
2446
+ var callItemStyles = {
2447
+ display: "flex",
2448
+ alignItems: "flex-start",
2449
+ padding: "10px 16px",
2450
+ borderBottom: "1px solid #27272a",
2451
+ position: "relative"
2452
+ };
2453
+ var callItemLastStyles = {
2454
+ borderBottom: "none"
2455
+ };
2456
+ var callContentStyles = {
2457
+ flex: 1,
2458
+ minWidth: 0
2459
+ };
2460
+ var callLabelStyles = {
2461
+ fontWeight: 500,
2462
+ marginBottom: "2px"
2463
+ };
2464
+ var callSublabelStyles = {
2465
+ color: "#a1a1aa",
2466
+ fontSize: "12px",
2467
+ overflow: "hidden",
2468
+ textOverflow: "ellipsis",
2469
+ whiteSpace: "nowrap"
2470
+ };
2471
+ var removeButtonStyles = {
2472
+ position: "absolute",
2473
+ left: "8px",
2474
+ top: "50%",
2475
+ transform: "translateY(-50%)",
2476
+ backgroundColor: "#dc2626",
2477
+ color: "#ffffff",
2478
+ border: "none",
2479
+ borderRadius: "4px",
2480
+ width: "20px",
2481
+ height: "20px",
2482
+ display: "flex",
2483
+ alignItems: "center",
2484
+ justifyContent: "center",
2485
+ cursor: "pointer",
2486
+ fontSize: "14px",
2487
+ opacity: 0,
2488
+ transition: "opacity 0.15s"
2489
+ };
2490
+ var removeButtonVisibleStyles = {
2491
+ opacity: 1
2492
+ };
2493
+ var clearButtonStyles = {
2494
+ display: "block",
2495
+ width: "100%",
2496
+ padding: "10px 16px",
2497
+ backgroundColor: "transparent",
2498
+ color: "#a1a1aa",
2499
+ border: "none",
2500
+ borderTop: "1px solid #27272a",
2501
+ fontSize: "12px",
2502
+ cursor: "pointer",
2503
+ textAlign: "center",
2504
+ transition: "color 0.15s"
2505
+ };
2506
+ var clearButtonHoverStyles = {
2507
+ color: "#ffffff"
2508
+ };
2509
+ function ChevronIcon({ down }) {
2510
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
2511
+ "svg",
2512
+ {
2513
+ width: "16",
2514
+ height: "16",
2515
+ viewBox: "0 0 24 24",
2516
+ fill: "none",
2517
+ stroke: "currentColor",
2518
+ strokeWidth: "2",
2519
+ strokeLinecap: "round",
2520
+ strokeLinejoin: "round",
2521
+ style: {
2522
+ transition: "transform 0.2s",
2523
+ transform: down ? "rotate(0deg)" : "rotate(-90deg)"
2524
+ },
2525
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "m6 9 6 6 6-6" })
2526
+ }
2527
+ );
2528
+ }
2529
+ function BatchQueueWidget({ onSignAll }) {
2530
+ const {
2531
+ queue,
2532
+ batchChainId,
2533
+ removeFromBatch,
2534
+ clearBatch,
2535
+ isExpanded,
2536
+ setExpanded,
2537
+ isSigning,
2538
+ animationTrigger
2539
+ } = useBatchQueue();
2540
+ const [hoveredItemId, setHoveredItemId] = React2.useState(null);
2541
+ const [isSignAllHovered, setIsSignAllHovered] = React2.useState(false);
2542
+ const [isClearHovered, setIsClearHovered] = React2.useState(false);
2543
+ const [shouldAnimate, setShouldAnimate] = React2.useState(false);
2544
+ React2.useEffect(() => {
2545
+ injectStyles();
2546
+ }, []);
2547
+ React2.useEffect(() => {
2548
+ if (animationTrigger > 0) {
2549
+ setShouldAnimate(true);
2550
+ const timer = setTimeout(() => setShouldAnimate(false), 300);
2551
+ return () => clearTimeout(timer);
2552
+ }
2553
+ }, [animationTrigger]);
2554
+ if (queue.length === 0) {
2555
+ return null;
2556
+ }
2557
+ const handleHeaderClick = () => {
2558
+ setExpanded(!isExpanded);
2559
+ };
2560
+ const handleSignAllClick = (e) => {
2561
+ e.stopPropagation();
2562
+ if (!isSigning) {
2563
+ onSignAll();
2564
+ }
2565
+ };
2566
+ const animatedWidgetStyles = {
2567
+ ...widgetStyles,
2568
+ animation: shouldAnimate ? "batch-bounce-in 0.3s ease-out" : void 0
2569
+ };
2570
+ const currentSignAllStyles = {
2571
+ ...signAllButtonStyles,
2572
+ ...isSignAllHovered && !isSigning ? signAllButtonHoverStyles : {},
2573
+ ...isSigning ? signAllButtonDisabledStyles : {}
2574
+ };
2575
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: animatedWidgetStyles, children: [
2576
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: headerStyles, onClick: handleHeaderClick, children: [
2577
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: headerLeftStyles, children: [
2578
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ChevronIcon, { down: isExpanded }),
2579
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: counterBadgeStyles, children: queue.length }),
2580
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { children: [
2581
+ "call",
2582
+ queue.length !== 1 ? "s" : "",
2583
+ " queued"
2584
+ ] }),
2585
+ batchChainId && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: chainBadgeStyles, children: getChainName2(batchChainId) })
2586
+ ] }),
2587
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
2588
+ "button",
2589
+ {
2590
+ style: currentSignAllStyles,
2591
+ onClick: handleSignAllClick,
2592
+ onMouseEnter: () => setIsSignAllHovered(true),
2593
+ onMouseLeave: () => setIsSignAllHovered(false),
2594
+ disabled: isSigning,
2595
+ children: isSigning ? "Signing..." : "Sign All"
2596
+ }
2597
+ )
2598
+ ] }),
2599
+ isExpanded && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
2600
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: listContainerStyles, children: queue.map((item, index) => {
2601
+ const isHovered = hoveredItemId === item.id;
2602
+ const isLast = index === queue.length - 1;
2603
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
2604
+ "div",
2605
+ {
2606
+ style: {
2607
+ ...callItemStyles,
2608
+ ...isLast ? callItemLastStyles : {},
2609
+ paddingLeft: isHovered ? "36px" : "16px",
2610
+ transition: "padding-left 0.15s"
2611
+ },
2612
+ onMouseEnter: () => setHoveredItemId(item.id),
2613
+ onMouseLeave: () => setHoveredItemId(null),
2614
+ children: [
2615
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
2616
+ "button",
2617
+ {
2618
+ style: {
2619
+ ...removeButtonStyles,
2620
+ ...isHovered ? removeButtonVisibleStyles : {}
2621
+ },
2622
+ onClick: () => removeFromBatch(item.id),
2623
+ title: "Remove from batch",
2624
+ children: "\xD7"
2625
+ }
2626
+ ),
2627
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: callContentStyles, children: [
2628
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: callLabelStyles, children: item.call.label || "Contract Call" }),
2629
+ item.call.sublabel && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: callSublabelStyles, children: item.call.sublabel }),
2630
+ !item.call.sublabel && item.call.to && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: callSublabelStyles, children: [
2631
+ "To: ",
2632
+ item.call.to.slice(0, 6),
2633
+ "...",
2634
+ item.call.to.slice(-4)
2635
+ ] })
2636
+ ] })
2637
+ ]
2638
+ },
2639
+ item.id
2640
+ );
2641
+ }) }),
2642
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
2643
+ "button",
2644
+ {
2645
+ style: {
2646
+ ...clearButtonStyles,
2647
+ ...isClearHovered ? clearButtonHoverStyles : {}
2648
+ },
2649
+ onClick: clearBatch,
2650
+ onMouseEnter: () => setIsClearHovered(true),
2651
+ onMouseLeave: () => setIsClearHovered(false),
2652
+ children: "Clear batch"
2653
+ }
2654
+ )
2655
+ ] })
2656
+ ] });
2657
+ }
2658
+
2659
+ // src/verify.ts
2660
+ var import_viem7 = require("viem");
2661
+ var PASSKEY_MESSAGE_PREFIX = "Passkey Signed Message:\n";
2662
+ function hashMessage2(message) {
2663
+ const prefixed = PASSKEY_MESSAGE_PREFIX + message.length.toString() + message;
2664
+ return (0, import_viem7.keccak256)((0, import_viem7.toBytes)(prefixed));
2665
+ }
2666
+ function verifyMessageHash(message, signedHash) {
2667
+ if (!signedHash) return false;
2668
+ const expectedHash = hashMessage2(message);
2669
+ return expectedHash.toLowerCase() === signedHash.toLowerCase();
2670
+ }
2671
+ // Annotate the CommonJS export names for ESM import in node:
2672
+ 0 && (module.exports = {
2673
+ BatchQueueProvider,
2674
+ BatchQueueWidget,
2675
+ PASSKEY_MESSAGE_PREFIX,
2676
+ PasskeyProviderClient,
2677
+ createPasskeyAccount,
2678
+ createPasskeyProvider,
2679
+ createPasskeyWalletClient,
2680
+ encodeWebAuthnSignature,
2681
+ getAllSupportedChainsAndTokens,
2682
+ getChainById,
2683
+ getChainExplorerUrl,
2684
+ getChainName,
2685
+ getChainRpcUrl,
2686
+ getSupportedChainIds,
2687
+ getSupportedChains,
2688
+ getSupportedTokenSymbols,
2689
+ getSupportedTokens,
2690
+ getTokenAddress,
2691
+ getTokenDecimals,
2692
+ getTokenSymbol,
2693
+ hashCalls,
2694
+ hashMessage,
2695
+ isTestnet,
2696
+ isTokenAddressSupported,
2697
+ resolveTokenAddress,
2698
+ useBatchQueue,
2699
+ verifyMessageHash
2700
+ });
2701
+ //# sourceMappingURL=index.js.map