@rhinestone/1auth 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +130 -0
- package/dist/{chunk-UXYKIMGZ.mjs → chunk-TACK3LJN.mjs} +180 -21
- package/dist/chunk-TACK3LJN.mjs.map +1 -0
- package/dist/{client-C1inywuT.d.mts → client-DyYGKWj3.d.mts} +321 -33
- package/dist/{client-C1inywuT.d.ts → client-DyYGKWj3.d.ts} +321 -33
- package/dist/index.d.mts +29 -16
- package/dist/index.d.ts +29 -16
- package/dist/index.js +1041 -249
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +861 -229
- package/dist/index.mjs.map +1 -1
- package/dist/provider-CNTZPPFz.d.ts +33 -0
- package/dist/provider-Ctr7HQHR.d.mts +33 -0
- package/dist/react.d.mts +4 -4
- package/dist/react.d.ts +4 -4
- package/dist/react.js.map +1 -1
- package/dist/react.mjs.map +1 -1
- package/dist/server.d.mts +10 -44
- package/dist/server.d.ts +10 -44
- package/dist/server.js +11 -8
- package/dist/server.js.map +1 -1
- package/dist/server.mjs +11 -8
- package/dist/server.mjs.map +1 -1
- package/dist/wagmi.d.mts +5 -4
- package/dist/wagmi.d.ts +5 -4
- package/dist/wagmi.js +173 -23
- package/dist/wagmi.js.map +1 -1
- package/dist/wagmi.mjs +3 -2
- package/dist/wagmi.mjs.map +1 -1
- package/package.json +24 -11
- package/dist/chunk-UXYKIMGZ.mjs.map +0 -1
- package/dist/provider-Dgh51NRc.d.mts +0 -24
- package/dist/provider-q7M728Mn.d.ts +0 -24
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
buildTransactionReview,
|
|
3
|
+
createOneAuthProvider,
|
|
3
4
|
createPasskeyProvider,
|
|
4
5
|
encodeWebAuthnSignature,
|
|
5
6
|
getAllSupportedChainsAndTokens,
|
|
@@ -18,7 +19,7 @@ import {
|
|
|
18
19
|
isTestnet,
|
|
19
20
|
isTokenAddressSupported,
|
|
20
21
|
resolveTokenAddress
|
|
21
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-TACK3LJN.mjs";
|
|
22
23
|
|
|
23
24
|
// src/client.ts
|
|
24
25
|
import { parseUnits, hashTypedData } from "viem";
|
|
@@ -27,10 +28,13 @@ var POPUP_HEIGHT = 600;
|
|
|
27
28
|
var DEFAULT_EMBED_WIDTH = "400px";
|
|
28
29
|
var DEFAULT_EMBED_HEIGHT = "500px";
|
|
29
30
|
var MODAL_WIDTH = 360;
|
|
30
|
-
var
|
|
31
|
+
var DEFAULT_PROVIDER_URL = "https://passkey.1auth.box";
|
|
32
|
+
var OneAuthClient = class {
|
|
31
33
|
constructor(config) {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
+
const providerUrl = config.providerUrl || DEFAULT_PROVIDER_URL;
|
|
35
|
+
const dialogUrl = config.dialogUrl || providerUrl;
|
|
36
|
+
this.config = { ...config, providerUrl, dialogUrl };
|
|
37
|
+
this.theme = this.config.theme || {};
|
|
34
38
|
}
|
|
35
39
|
/**
|
|
36
40
|
* Update the theme configuration at runtime
|
|
@@ -92,9 +96,7 @@ var PasskeyProviderClient = class {
|
|
|
92
96
|
const response = await fetch(
|
|
93
97
|
`${this.config.providerUrl}/api/intent/status/${intentId}`,
|
|
94
98
|
{
|
|
95
|
-
headers: {
|
|
96
|
-
"x-client-id": this.config.clientId
|
|
97
|
-
}
|
|
99
|
+
headers: this.config.clientId ? { "x-client-id": this.config.clientId } : {}
|
|
98
100
|
}
|
|
99
101
|
);
|
|
100
102
|
if (response.ok) {
|
|
@@ -113,16 +115,16 @@ var PasskeyProviderClient = class {
|
|
|
113
115
|
return void 0;
|
|
114
116
|
}
|
|
115
117
|
/**
|
|
116
|
-
*
|
|
117
|
-
* This is the recommended method for authentication - shows a modal overlay
|
|
118
|
-
* with both sign in and create account options.
|
|
118
|
+
* Open the auth dialog (sign in + sign up).
|
|
119
119
|
*/
|
|
120
120
|
async authWithModal(options) {
|
|
121
121
|
const dialogUrl = this.getDialogUrl();
|
|
122
122
|
const params = new URLSearchParams({
|
|
123
|
-
clientId: this.config.clientId,
|
|
124
123
|
mode: "iframe"
|
|
125
124
|
});
|
|
125
|
+
if (this.config.clientId) {
|
|
126
|
+
params.set("clientId", this.config.clientId);
|
|
127
|
+
}
|
|
126
128
|
if (options?.username) {
|
|
127
129
|
params.set("username", options.username);
|
|
128
130
|
}
|
|
@@ -138,6 +140,55 @@ var PasskeyProviderClient = class {
|
|
|
138
140
|
const { dialog, iframe, cleanup } = this.createModalDialog(url);
|
|
139
141
|
return this.waitForModalAuthResponse(dialog, iframe, cleanup);
|
|
140
142
|
}
|
|
143
|
+
/**
|
|
144
|
+
* Open the connect dialog (lightweight connection without passkey auth).
|
|
145
|
+
*
|
|
146
|
+
* This method shows a simple connection confirmation dialog that doesn't
|
|
147
|
+
* require a passkey signature. Users can optionally enable "auto-connect"
|
|
148
|
+
* to skip this dialog in the future.
|
|
149
|
+
*
|
|
150
|
+
* If the user has never connected before, this will return action: "switch"
|
|
151
|
+
* to indicate that the full auth modal should be opened instead.
|
|
152
|
+
*
|
|
153
|
+
* @example
|
|
154
|
+
* ```typescript
|
|
155
|
+
* const result = await client.connectWithModal();
|
|
156
|
+
*
|
|
157
|
+
* if (result.success) {
|
|
158
|
+
* console.log('Connected as:', result.username);
|
|
159
|
+
* } else if (result.action === 'switch') {
|
|
160
|
+
* // User needs to sign in first
|
|
161
|
+
* const authResult = await client.authWithModal();
|
|
162
|
+
* }
|
|
163
|
+
* ```
|
|
164
|
+
*/
|
|
165
|
+
async connectWithModal(options) {
|
|
166
|
+
const dialogUrl = this.getDialogUrl();
|
|
167
|
+
const params = new URLSearchParams({
|
|
168
|
+
mode: "iframe"
|
|
169
|
+
});
|
|
170
|
+
if (this.config.clientId) {
|
|
171
|
+
params.set("clientId", this.config.clientId);
|
|
172
|
+
}
|
|
173
|
+
const themeParams = this.getThemeParams(options?.theme);
|
|
174
|
+
if (themeParams) {
|
|
175
|
+
const themeParsed = new URLSearchParams(themeParams);
|
|
176
|
+
themeParsed.forEach((value, key) => params.set(key, value));
|
|
177
|
+
}
|
|
178
|
+
const url = `${dialogUrl}/dialog/connect?${params.toString()}`;
|
|
179
|
+
const { dialog, iframe, cleanup } = this.createModalDialog(url);
|
|
180
|
+
const ready = await this.waitForDialogReady(dialog, iframe, cleanup, {
|
|
181
|
+
mode: "iframe"
|
|
182
|
+
});
|
|
183
|
+
if (!ready) {
|
|
184
|
+
return {
|
|
185
|
+
success: false,
|
|
186
|
+
action: "cancel",
|
|
187
|
+
error: { code: "USER_CANCELLED", message: "Connection was cancelled" }
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
return this.waitForConnectResponse(dialog, iframe, cleanup);
|
|
191
|
+
}
|
|
141
192
|
/**
|
|
142
193
|
* Authenticate a user with an optional challenge to sign.
|
|
143
194
|
*
|
|
@@ -173,9 +224,11 @@ var PasskeyProviderClient = class {
|
|
|
173
224
|
async authenticate(options) {
|
|
174
225
|
const dialogUrl = this.getDialogUrl();
|
|
175
226
|
const params = new URLSearchParams({
|
|
176
|
-
clientId: this.config.clientId,
|
|
177
227
|
mode: "iframe"
|
|
178
228
|
});
|
|
229
|
+
if (this.config.clientId) {
|
|
230
|
+
params.set("clientId", this.config.clientId);
|
|
231
|
+
}
|
|
179
232
|
if (options?.challenge) {
|
|
180
233
|
params.set("challenge", options.challenge);
|
|
181
234
|
}
|
|
@@ -189,28 +242,30 @@ var PasskeyProviderClient = class {
|
|
|
189
242
|
return this.waitForAuthenticateResponse(dialog, iframe, cleanup);
|
|
190
243
|
}
|
|
191
244
|
/**
|
|
192
|
-
* Show signing in a modal overlay (
|
|
245
|
+
* Show signing in a modal overlay (iframe dialog)
|
|
193
246
|
*/
|
|
194
247
|
async signWithModal(options) {
|
|
195
248
|
const dialogUrl = this.getDialogUrl();
|
|
196
249
|
const themeParams = this.getThemeParams(options?.theme);
|
|
197
250
|
const signingUrl = `${dialogUrl}/dialog/sign?mode=iframe${themeParams ? `&${themeParams}` : ""}`;
|
|
198
251
|
const { dialog, iframe, cleanup } = this.createModalDialog(signingUrl);
|
|
199
|
-
const
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
username: options.username,
|
|
207
|
-
description: options.description,
|
|
208
|
-
transaction: options.transaction,
|
|
209
|
-
metadata: options.metadata
|
|
210
|
-
}, dialogOrigin);
|
|
211
|
-
resolve();
|
|
212
|
-
};
|
|
252
|
+
const ready = await this.waitForDialogReady(dialog, iframe, cleanup, {
|
|
253
|
+
mode: "iframe",
|
|
254
|
+
challenge: options.challenge,
|
|
255
|
+
username: options.username,
|
|
256
|
+
description: options.description,
|
|
257
|
+
transaction: options.transaction,
|
|
258
|
+
metadata: options.metadata
|
|
213
259
|
});
|
|
260
|
+
if (!ready) {
|
|
261
|
+
return {
|
|
262
|
+
success: false,
|
|
263
|
+
error: {
|
|
264
|
+
code: "USER_REJECTED",
|
|
265
|
+
message: "User closed the dialog"
|
|
266
|
+
}
|
|
267
|
+
};
|
|
268
|
+
}
|
|
214
269
|
return this.waitForSigningResponse(dialog, iframe, cleanup);
|
|
215
270
|
}
|
|
216
271
|
/**
|
|
@@ -243,10 +298,24 @@ var PasskeyProviderClient = class {
|
|
|
243
298
|
* ```
|
|
244
299
|
*/
|
|
245
300
|
async sendIntent(options) {
|
|
246
|
-
const signedIntent = options.signedIntent
|
|
301
|
+
const signedIntent = options.signedIntent ? {
|
|
302
|
+
...options.signedIntent,
|
|
303
|
+
merchantId: options.signedIntent.merchantId || options.signedIntent.developerId
|
|
304
|
+
} : void 0;
|
|
247
305
|
const username = signedIntent?.username || options.username;
|
|
248
306
|
const targetChain = signedIntent?.targetChain || options.targetChain;
|
|
249
307
|
const calls = signedIntent?.calls || options.calls;
|
|
308
|
+
if (signedIntent && !signedIntent.merchantId) {
|
|
309
|
+
return {
|
|
310
|
+
success: false,
|
|
311
|
+
intentId: "",
|
|
312
|
+
status: "failed",
|
|
313
|
+
error: {
|
|
314
|
+
code: "INVALID_OPTIONS",
|
|
315
|
+
message: "Signed intent requires developerId (clientId)"
|
|
316
|
+
}
|
|
317
|
+
};
|
|
318
|
+
}
|
|
250
319
|
if (!username && !signedIntent?.accountAddress) {
|
|
251
320
|
return {
|
|
252
321
|
success: false,
|
|
@@ -269,16 +338,21 @@ var PasskeyProviderClient = class {
|
|
|
269
338
|
}
|
|
270
339
|
};
|
|
271
340
|
}
|
|
341
|
+
const serializedTokenRequests = options.tokenRequests?.map((r) => ({
|
|
342
|
+
token: r.token,
|
|
343
|
+
amount: r.amount.toString()
|
|
344
|
+
}));
|
|
272
345
|
let prepareResponse;
|
|
346
|
+
const requestBody = signedIntent || {
|
|
347
|
+
username: options.username,
|
|
348
|
+
targetChain: options.targetChain,
|
|
349
|
+
calls: options.calls,
|
|
350
|
+
tokenRequests: serializedTokenRequests,
|
|
351
|
+
sourceAssets: options.sourceAssets,
|
|
352
|
+
sourceChainId: options.sourceChainId,
|
|
353
|
+
...this.config.clientId && { clientId: this.config.clientId }
|
|
354
|
+
};
|
|
273
355
|
try {
|
|
274
|
-
const requestBody = signedIntent || {
|
|
275
|
-
username: options.username,
|
|
276
|
-
targetChain: options.targetChain,
|
|
277
|
-
calls: options.calls,
|
|
278
|
-
tokenRequests: options.tokenRequests,
|
|
279
|
-
sourceAssets: options.sourceAssets,
|
|
280
|
-
clientId: this.config.clientId
|
|
281
|
-
};
|
|
282
356
|
const response = await fetch(`${this.config.providerUrl}/api/intent/prepare`, {
|
|
283
357
|
method: "POST",
|
|
284
358
|
headers: {
|
|
@@ -315,112 +389,190 @@ var PasskeyProviderClient = class {
|
|
|
315
389
|
};
|
|
316
390
|
}
|
|
317
391
|
const dialogUrl = this.getDialogUrl();
|
|
318
|
-
const
|
|
392
|
+
const themeParams = this.getThemeParams();
|
|
393
|
+
const signingUrl = `${dialogUrl}/dialog/sign?mode=iframe${themeParams ? `&${themeParams}` : ""}`;
|
|
319
394
|
const { dialog, iframe, cleanup } = this.createModalDialog(signingUrl);
|
|
320
395
|
const dialogOrigin = this.getDialogOrigin();
|
|
321
|
-
await
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
accountAddress: prepareResponse.accountAddress,
|
|
335
|
-
intentId: prepareResponse.intentId
|
|
336
|
-
}, dialogOrigin);
|
|
337
|
-
resolve();
|
|
338
|
-
}
|
|
339
|
-
};
|
|
340
|
-
window.addEventListener("message", handleReady);
|
|
396
|
+
const ready = await this.waitForDialogReady(dialog, iframe, cleanup, {
|
|
397
|
+
mode: "iframe",
|
|
398
|
+
calls,
|
|
399
|
+
chainId: targetChain,
|
|
400
|
+
transaction: prepareResponse.transaction,
|
|
401
|
+
challenge: prepareResponse.challenge,
|
|
402
|
+
username,
|
|
403
|
+
accountAddress: prepareResponse.accountAddress,
|
|
404
|
+
originMessages: prepareResponse.originMessages,
|
|
405
|
+
tokenRequests: serializedTokenRequests,
|
|
406
|
+
expiresAt: prepareResponse.expiresAt,
|
|
407
|
+
userId: prepareResponse.userId,
|
|
408
|
+
intentOp: prepareResponse.intentOp
|
|
341
409
|
});
|
|
342
|
-
|
|
410
|
+
if (!ready) {
|
|
411
|
+
return {
|
|
412
|
+
success: false,
|
|
413
|
+
intentId: "",
|
|
414
|
+
status: "failed",
|
|
415
|
+
error: { code: "USER_CANCELLED", message: "User closed the dialog" }
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
const signingResult = await this.waitForSigningWithRefresh(
|
|
419
|
+
dialog,
|
|
420
|
+
iframe,
|
|
421
|
+
cleanup,
|
|
422
|
+
dialogOrigin,
|
|
423
|
+
// Refresh callback - called when dialog requests a quote refresh
|
|
424
|
+
async () => {
|
|
425
|
+
console.log("[SDK] Dialog requested quote refresh, re-preparing intent");
|
|
426
|
+
try {
|
|
427
|
+
const refreshResponse = await fetch(`${this.config.providerUrl}/api/intent/prepare`, {
|
|
428
|
+
method: "POST",
|
|
429
|
+
headers: { "Content-Type": "application/json" },
|
|
430
|
+
body: JSON.stringify(requestBody),
|
|
431
|
+
credentials: "include"
|
|
432
|
+
});
|
|
433
|
+
if (!refreshResponse.ok) {
|
|
434
|
+
console.error("[SDK] Quote refresh failed:", await refreshResponse.text());
|
|
435
|
+
return null;
|
|
436
|
+
}
|
|
437
|
+
const refreshedData = await refreshResponse.json();
|
|
438
|
+
prepareResponse = refreshedData;
|
|
439
|
+
return {
|
|
440
|
+
intentOp: refreshedData.intentOp,
|
|
441
|
+
expiresAt: refreshedData.expiresAt,
|
|
442
|
+
challenge: refreshedData.challenge,
|
|
443
|
+
originMessages: refreshedData.originMessages,
|
|
444
|
+
transaction: refreshedData.transaction
|
|
445
|
+
};
|
|
446
|
+
} catch (error) {
|
|
447
|
+
console.error("[SDK] Quote refresh error:", error);
|
|
448
|
+
return null;
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
);
|
|
343
452
|
if (!signingResult.success) {
|
|
344
453
|
return {
|
|
345
454
|
success: false,
|
|
346
|
-
intentId:
|
|
455
|
+
intentId: "",
|
|
456
|
+
// No intentId yet - signing was cancelled before execute
|
|
347
457
|
status: "failed",
|
|
348
458
|
error: signingResult.error
|
|
349
459
|
};
|
|
350
460
|
}
|
|
461
|
+
const dialogExecutedIntent = "intentId" in signingResult && signingResult.intentId;
|
|
351
462
|
let executeResponse;
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
463
|
+
if (dialogExecutedIntent) {
|
|
464
|
+
executeResponse = {
|
|
465
|
+
success: true,
|
|
466
|
+
intentId: signingResult.intentId,
|
|
467
|
+
status: "pending"
|
|
468
|
+
};
|
|
469
|
+
} else {
|
|
470
|
+
try {
|
|
471
|
+
const response = await fetch(`${this.config.providerUrl}/api/intent/execute`, {
|
|
472
|
+
method: "POST",
|
|
473
|
+
headers: {
|
|
474
|
+
"Content-Type": "application/json"
|
|
475
|
+
},
|
|
476
|
+
body: JSON.stringify({
|
|
477
|
+
// Data from prepare response (no intentId yet - created on execute)
|
|
478
|
+
intentOp: prepareResponse.intentOp,
|
|
479
|
+
userId: prepareResponse.userId,
|
|
480
|
+
targetChain: prepareResponse.targetChain,
|
|
481
|
+
calls: prepareResponse.calls,
|
|
482
|
+
expiresAt: prepareResponse.expiresAt,
|
|
483
|
+
// Signature from dialog
|
|
484
|
+
signature: signingResult.signature,
|
|
485
|
+
passkey: signingResult.passkey
|
|
486
|
+
// Include passkey info for signature encoding
|
|
487
|
+
})
|
|
488
|
+
});
|
|
489
|
+
if (!response.ok) {
|
|
490
|
+
const errorData = await response.json().catch(() => ({}));
|
|
491
|
+
this.sendTransactionStatus(iframe, "failed");
|
|
492
|
+
await this.waitForDialogClose(dialog, cleanup);
|
|
493
|
+
return {
|
|
494
|
+
success: false,
|
|
495
|
+
intentId: "",
|
|
496
|
+
// No intentId - execute failed before creation
|
|
497
|
+
status: "failed",
|
|
498
|
+
error: {
|
|
499
|
+
code: "EXECUTE_FAILED",
|
|
500
|
+
message: errorData.error || "Failed to execute intent"
|
|
501
|
+
}
|
|
502
|
+
};
|
|
503
|
+
}
|
|
504
|
+
executeResponse = await response.json();
|
|
505
|
+
} catch (error) {
|
|
367
506
|
this.sendTransactionStatus(iframe, "failed");
|
|
368
507
|
await this.waitForDialogClose(dialog, cleanup);
|
|
369
508
|
return {
|
|
370
509
|
success: false,
|
|
371
|
-
intentId:
|
|
510
|
+
intentId: "",
|
|
511
|
+
// No intentId - network error before creation
|
|
372
512
|
status: "failed",
|
|
373
513
|
error: {
|
|
374
|
-
code: "
|
|
375
|
-
message:
|
|
514
|
+
code: "NETWORK_ERROR",
|
|
515
|
+
message: error instanceof Error ? error.message : "Network error"
|
|
376
516
|
}
|
|
377
517
|
};
|
|
378
518
|
}
|
|
379
|
-
executeResponse = await response.json();
|
|
380
|
-
} catch (error) {
|
|
381
|
-
this.sendTransactionStatus(iframe, "failed");
|
|
382
|
-
await this.waitForDialogClose(dialog, cleanup);
|
|
383
|
-
return {
|
|
384
|
-
success: false,
|
|
385
|
-
intentId: prepareResponse.intentId,
|
|
386
|
-
status: "failed",
|
|
387
|
-
error: {
|
|
388
|
-
code: "NETWORK_ERROR",
|
|
389
|
-
message: error instanceof Error ? error.message : "Network error"
|
|
390
|
-
}
|
|
391
|
-
};
|
|
392
519
|
}
|
|
393
|
-
const closeOn = options.closeOn || "preconfirmed";
|
|
394
|
-
const acceptPreconfirmations = closeOn !== "completed";
|
|
395
520
|
let finalStatus = executeResponse.status;
|
|
396
521
|
let finalTxHash = executeResponse.transactionHash;
|
|
397
522
|
if (finalStatus === "pending") {
|
|
398
|
-
this.sendTransactionStatus(iframe, "
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
523
|
+
this.sendTransactionStatus(iframe, "pending");
|
|
524
|
+
const maxAttempts = 120;
|
|
525
|
+
const pollIntervalMs = 1500;
|
|
526
|
+
let lastStatus = "pending";
|
|
527
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
528
|
+
try {
|
|
529
|
+
const statusResponse = await fetch(
|
|
530
|
+
`${this.config.providerUrl}/api/intent/status/${executeResponse.intentId}`,
|
|
531
|
+
{
|
|
532
|
+
method: "GET",
|
|
533
|
+
headers: this.config.clientId ? { "x-client-id": this.config.clientId } : {}
|
|
534
|
+
}
|
|
535
|
+
);
|
|
536
|
+
if (statusResponse.ok) {
|
|
537
|
+
const statusResult = await statusResponse.json();
|
|
538
|
+
finalStatus = statusResult.status;
|
|
539
|
+
finalTxHash = statusResult.transactionHash;
|
|
540
|
+
if (finalStatus !== lastStatus) {
|
|
541
|
+
this.sendTransactionStatus(iframe, finalStatus, finalTxHash);
|
|
542
|
+
lastStatus = finalStatus;
|
|
543
|
+
}
|
|
544
|
+
const closeOn2 = options.closeOn || "preconfirmed";
|
|
545
|
+
const successStatuses2 = {
|
|
546
|
+
claimed: ["claimed", "preconfirmed", "filled", "completed"],
|
|
547
|
+
preconfirmed: ["preconfirmed", "filled", "completed"],
|
|
548
|
+
filled: ["filled", "completed"],
|
|
549
|
+
completed: ["completed"]
|
|
550
|
+
};
|
|
551
|
+
const isTerminal = finalStatus === "failed" || finalStatus === "expired";
|
|
552
|
+
const isSuccess = successStatuses2[closeOn2]?.includes(finalStatus) ?? false;
|
|
553
|
+
if (isTerminal || isSuccess) {
|
|
554
|
+
break;
|
|
405
555
|
}
|
|
406
556
|
}
|
|
407
|
-
)
|
|
408
|
-
|
|
409
|
-
const waitResult = await waitResponse.json();
|
|
410
|
-
finalStatus = waitResult.status === "preconfirmed" || waitResult.status === "completed" ? "completed" : waitResult.status;
|
|
411
|
-
finalTxHash = waitResult.transactionHash;
|
|
412
|
-
} else {
|
|
413
|
-
console.error("Wait endpoint failed:", await waitResponse.text());
|
|
557
|
+
} catch (pollError) {
|
|
558
|
+
console.error("Failed to poll intent status:", pollError);
|
|
414
559
|
}
|
|
415
|
-
|
|
416
|
-
console.error("Failed to wait for intent:", waitError);
|
|
560
|
+
await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
|
|
417
561
|
}
|
|
418
562
|
}
|
|
419
|
-
const
|
|
563
|
+
const closeOn = options.closeOn || "preconfirmed";
|
|
564
|
+
const successStatuses = {
|
|
565
|
+
claimed: ["claimed", "preconfirmed", "filled", "completed"],
|
|
566
|
+
preconfirmed: ["preconfirmed", "filled", "completed"],
|
|
567
|
+
filled: ["filled", "completed"],
|
|
568
|
+
completed: ["completed"]
|
|
569
|
+
};
|
|
570
|
+
const isSuccessStatus = successStatuses[closeOn]?.includes(finalStatus) ?? false;
|
|
571
|
+
const displayStatus = isSuccessStatus ? "confirmed" : finalStatus;
|
|
420
572
|
this.sendTransactionStatus(iframe, displayStatus, finalTxHash);
|
|
421
573
|
await this.waitForDialogClose(dialog, cleanup);
|
|
422
574
|
if (options.waitForHash && !finalTxHash) {
|
|
423
|
-
const hash = await this.waitForTransactionHash(
|
|
575
|
+
const hash = await this.waitForTransactionHash(executeResponse.intentId, {
|
|
424
576
|
timeoutMs: options.hashTimeoutMs,
|
|
425
577
|
intervalMs: options.hashIntervalMs
|
|
426
578
|
});
|
|
@@ -431,7 +583,7 @@ var PasskeyProviderClient = class {
|
|
|
431
583
|
finalStatus = "failed";
|
|
432
584
|
return {
|
|
433
585
|
success: false,
|
|
434
|
-
intentId:
|
|
586
|
+
intentId: executeResponse.intentId,
|
|
435
587
|
status: finalStatus,
|
|
436
588
|
transactionHash: finalTxHash,
|
|
437
589
|
operationId: executeResponse.operationId,
|
|
@@ -443,14 +595,204 @@ var PasskeyProviderClient = class {
|
|
|
443
595
|
}
|
|
444
596
|
}
|
|
445
597
|
return {
|
|
446
|
-
success:
|
|
447
|
-
intentId:
|
|
598
|
+
success: isSuccessStatus,
|
|
599
|
+
intentId: executeResponse.intentId,
|
|
448
600
|
status: finalStatus,
|
|
449
601
|
transactionHash: finalTxHash,
|
|
450
602
|
operationId: executeResponse.operationId,
|
|
451
603
|
error: executeResponse.error
|
|
452
604
|
};
|
|
453
605
|
}
|
|
606
|
+
/**
|
|
607
|
+
* Send a batch of intents for multi-chain execution with a single passkey tap.
|
|
608
|
+
*
|
|
609
|
+
* This method prepares multiple intents, shows a paginated review,
|
|
610
|
+
* and signs all intents with a single passkey tap via a shared merkle tree.
|
|
611
|
+
*
|
|
612
|
+
* @example
|
|
613
|
+
* ```typescript
|
|
614
|
+
* const result = await client.sendBatchIntent({
|
|
615
|
+
* username: 'alice',
|
|
616
|
+
* intents: [
|
|
617
|
+
* {
|
|
618
|
+
* targetChain: 8453, // Base
|
|
619
|
+
* calls: [{ to: '0x...', data: '0x...', label: 'Swap on Base' }],
|
|
620
|
+
* },
|
|
621
|
+
* {
|
|
622
|
+
* targetChain: 42161, // Arbitrum
|
|
623
|
+
* calls: [{ to: '0x...', data: '0x...', label: 'Mint on Arbitrum' }],
|
|
624
|
+
* },
|
|
625
|
+
* ],
|
|
626
|
+
* });
|
|
627
|
+
*
|
|
628
|
+
* if (result.success) {
|
|
629
|
+
* console.log(`${result.successCount} intents submitted`);
|
|
630
|
+
* }
|
|
631
|
+
* ```
|
|
632
|
+
*/
|
|
633
|
+
async sendBatchIntent(options) {
|
|
634
|
+
if (!options.username) {
|
|
635
|
+
return {
|
|
636
|
+
success: false,
|
|
637
|
+
results: [],
|
|
638
|
+
successCount: 0,
|
|
639
|
+
failureCount: 0
|
|
640
|
+
};
|
|
641
|
+
}
|
|
642
|
+
if (!options.intents?.length) {
|
|
643
|
+
return {
|
|
644
|
+
success: false,
|
|
645
|
+
results: [],
|
|
646
|
+
successCount: 0,
|
|
647
|
+
failureCount: 0
|
|
648
|
+
};
|
|
649
|
+
}
|
|
650
|
+
const serializedIntents = options.intents.map((intent) => ({
|
|
651
|
+
targetChain: intent.targetChain,
|
|
652
|
+
calls: intent.calls,
|
|
653
|
+
tokenRequests: intent.tokenRequests?.map((r) => ({
|
|
654
|
+
token: r.token,
|
|
655
|
+
amount: r.amount.toString()
|
|
656
|
+
})),
|
|
657
|
+
sourceAssets: intent.sourceAssets,
|
|
658
|
+
sourceChainId: intent.sourceChainId
|
|
659
|
+
}));
|
|
660
|
+
const requestBody = {
|
|
661
|
+
username: options.username,
|
|
662
|
+
intents: serializedIntents,
|
|
663
|
+
...this.config.clientId && { clientId: this.config.clientId }
|
|
664
|
+
};
|
|
665
|
+
let prepareResponse;
|
|
666
|
+
try {
|
|
667
|
+
const response = await fetch(`${this.config.providerUrl}/api/intent/batch-prepare`, {
|
|
668
|
+
method: "POST",
|
|
669
|
+
headers: { "Content-Type": "application/json" },
|
|
670
|
+
body: JSON.stringify(requestBody)
|
|
671
|
+
});
|
|
672
|
+
if (!response.ok) {
|
|
673
|
+
const errorData = await response.json().catch(() => ({}));
|
|
674
|
+
const errorMessage = errorData.error || "Failed to prepare batch intent";
|
|
675
|
+
if (errorMessage.includes("User not found")) {
|
|
676
|
+
localStorage.removeItem("1auth-user");
|
|
677
|
+
}
|
|
678
|
+
return {
|
|
679
|
+
success: false,
|
|
680
|
+
results: [],
|
|
681
|
+
successCount: 0,
|
|
682
|
+
failureCount: 0
|
|
683
|
+
};
|
|
684
|
+
}
|
|
685
|
+
prepareResponse = await response.json();
|
|
686
|
+
} catch {
|
|
687
|
+
return {
|
|
688
|
+
success: false,
|
|
689
|
+
results: [],
|
|
690
|
+
successCount: 0,
|
|
691
|
+
failureCount: 0
|
|
692
|
+
};
|
|
693
|
+
}
|
|
694
|
+
const dialogUrl = this.getDialogUrl();
|
|
695
|
+
const themeParams = this.getThemeParams();
|
|
696
|
+
const signingUrl = `${dialogUrl}/dialog/sign?mode=iframe${themeParams ? `&${themeParams}` : ""}`;
|
|
697
|
+
const { dialog, iframe, cleanup } = this.createModalDialog(signingUrl);
|
|
698
|
+
const dialogOrigin = this.getDialogOrigin();
|
|
699
|
+
const ready = await this.waitForDialogReady(dialog, iframe, cleanup, {
|
|
700
|
+
mode: "iframe",
|
|
701
|
+
batchMode: true,
|
|
702
|
+
batchIntents: prepareResponse.intents,
|
|
703
|
+
challenge: prepareResponse.challenge,
|
|
704
|
+
username: options.username,
|
|
705
|
+
accountAddress: prepareResponse.accountAddress,
|
|
706
|
+
userId: prepareResponse.userId,
|
|
707
|
+
expiresAt: prepareResponse.expiresAt
|
|
708
|
+
});
|
|
709
|
+
if (!ready) {
|
|
710
|
+
return {
|
|
711
|
+
success: false,
|
|
712
|
+
results: [],
|
|
713
|
+
successCount: 0,
|
|
714
|
+
failureCount: 0
|
|
715
|
+
};
|
|
716
|
+
}
|
|
717
|
+
const batchResult = await new Promise((resolve) => {
|
|
718
|
+
const handleMessage = async (event) => {
|
|
719
|
+
if (event.origin !== dialogOrigin) return;
|
|
720
|
+
const message = event.data;
|
|
721
|
+
if (message?.type === "PASSKEY_REFRESH_QUOTE") {
|
|
722
|
+
console.log("[SDK] Batch dialog requested quote refresh, re-preparing all intents");
|
|
723
|
+
try {
|
|
724
|
+
const refreshResponse = await fetch(`${this.config.providerUrl}/api/intent/batch-prepare`, {
|
|
725
|
+
method: "POST",
|
|
726
|
+
headers: { "Content-Type": "application/json" },
|
|
727
|
+
body: JSON.stringify(requestBody)
|
|
728
|
+
});
|
|
729
|
+
if (refreshResponse.ok) {
|
|
730
|
+
const refreshed = await refreshResponse.json();
|
|
731
|
+
prepareResponse = refreshed;
|
|
732
|
+
iframe.contentWindow?.postMessage({
|
|
733
|
+
type: "PASSKEY_REFRESH_COMPLETE",
|
|
734
|
+
batchIntents: refreshed.intents,
|
|
735
|
+
challenge: refreshed.challenge,
|
|
736
|
+
expiresAt: refreshed.expiresAt
|
|
737
|
+
}, dialogOrigin);
|
|
738
|
+
} else {
|
|
739
|
+
iframe.contentWindow?.postMessage({
|
|
740
|
+
type: "PASSKEY_REFRESH_ERROR",
|
|
741
|
+
error: "Failed to refresh batch quotes"
|
|
742
|
+
}, dialogOrigin);
|
|
743
|
+
}
|
|
744
|
+
} catch {
|
|
745
|
+
iframe.contentWindow?.postMessage({
|
|
746
|
+
type: "PASSKEY_REFRESH_ERROR",
|
|
747
|
+
error: "Failed to refresh batch quotes"
|
|
748
|
+
}, dialogOrigin);
|
|
749
|
+
}
|
|
750
|
+
return;
|
|
751
|
+
}
|
|
752
|
+
if (message?.type === "PASSKEY_SIGNING_RESULT") {
|
|
753
|
+
window.removeEventListener("message", handleMessage);
|
|
754
|
+
if (message.success && message.data?.batchResults) {
|
|
755
|
+
const rawResults = message.data.batchResults;
|
|
756
|
+
const results = rawResults.map((r) => ({
|
|
757
|
+
index: r.index,
|
|
758
|
+
success: r.success ?? r.status !== "FAILED",
|
|
759
|
+
intentId: r.intentId || r.operationId || "",
|
|
760
|
+
status: r.status === "FAILED" ? "failed" : "pending",
|
|
761
|
+
error: r.error ? { code: "EXECUTE_FAILED", message: r.error } : void 0
|
|
762
|
+
}));
|
|
763
|
+
const successCount = results.filter((r) => r.success).length;
|
|
764
|
+
await this.waitForDialogClose(dialog, cleanup);
|
|
765
|
+
resolve({
|
|
766
|
+
success: successCount === results.length,
|
|
767
|
+
results,
|
|
768
|
+
successCount,
|
|
769
|
+
failureCount: results.length - successCount
|
|
770
|
+
});
|
|
771
|
+
} else {
|
|
772
|
+
cleanup();
|
|
773
|
+
resolve({
|
|
774
|
+
success: false,
|
|
775
|
+
results: [],
|
|
776
|
+
successCount: 0,
|
|
777
|
+
failureCount: 0
|
|
778
|
+
});
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
if (message?.type === "PASSKEY_CLOSE") {
|
|
782
|
+
window.removeEventListener("message", handleMessage);
|
|
783
|
+
cleanup();
|
|
784
|
+
resolve({
|
|
785
|
+
success: false,
|
|
786
|
+
results: [],
|
|
787
|
+
successCount: 0,
|
|
788
|
+
failureCount: 0
|
|
789
|
+
});
|
|
790
|
+
}
|
|
791
|
+
};
|
|
792
|
+
window.addEventListener("message", handleMessage);
|
|
793
|
+
});
|
|
794
|
+
return batchResult;
|
|
795
|
+
}
|
|
454
796
|
/**
|
|
455
797
|
* Send transaction status to the dialog iframe
|
|
456
798
|
*/
|
|
@@ -527,7 +869,77 @@ var PasskeyProviderClient = class {
|
|
|
527
869
|
const payload = message?.data;
|
|
528
870
|
if (message?.type === "PASSKEY_SIGNING_RESULT") {
|
|
529
871
|
window.removeEventListener("message", handleMessage);
|
|
530
|
-
if (message.success && payload?.
|
|
872
|
+
if (message.success && payload?.intentId) {
|
|
873
|
+
resolve({
|
|
874
|
+
success: true,
|
|
875
|
+
intentId: payload.intentId
|
|
876
|
+
});
|
|
877
|
+
} else if (message.success && payload?.signature) {
|
|
878
|
+
resolve({
|
|
879
|
+
success: true,
|
|
880
|
+
signature: payload.signature,
|
|
881
|
+
passkey: payload.passkey,
|
|
882
|
+
signedHash: payload.signedHash
|
|
883
|
+
});
|
|
884
|
+
} else {
|
|
885
|
+
resolve({
|
|
886
|
+
success: false,
|
|
887
|
+
error: message.error || {
|
|
888
|
+
code: "SIGNING_FAILED",
|
|
889
|
+
message: "Signing failed"
|
|
890
|
+
}
|
|
891
|
+
});
|
|
892
|
+
}
|
|
893
|
+
} else if (message?.type === "PASSKEY_CLOSE") {
|
|
894
|
+
window.removeEventListener("message", handleMessage);
|
|
895
|
+
cleanup();
|
|
896
|
+
resolve({
|
|
897
|
+
success: false,
|
|
898
|
+
error: {
|
|
899
|
+
code: "USER_REJECTED",
|
|
900
|
+
message: "User closed the dialog"
|
|
901
|
+
}
|
|
902
|
+
});
|
|
903
|
+
}
|
|
904
|
+
};
|
|
905
|
+
window.addEventListener("message", handleMessage);
|
|
906
|
+
});
|
|
907
|
+
}
|
|
908
|
+
/**
|
|
909
|
+
* Wait for signing result with auto-refresh support
|
|
910
|
+
* This method handles both signing results and quote refresh requests from the dialog
|
|
911
|
+
*/
|
|
912
|
+
waitForSigningWithRefresh(dialog, iframe, cleanup, dialogOrigin, onRefresh) {
|
|
913
|
+
console.log("[SDK] waitForSigningWithRefresh, expecting origin:", dialogOrigin);
|
|
914
|
+
return new Promise((resolve) => {
|
|
915
|
+
const handleMessage = async (event) => {
|
|
916
|
+
if (event.origin !== dialogOrigin) return;
|
|
917
|
+
const message = event.data;
|
|
918
|
+
if (message?.type === "PASSKEY_REFRESH_QUOTE") {
|
|
919
|
+
console.log("[SDK] Received quote refresh request from dialog");
|
|
920
|
+
const refreshedData = await onRefresh();
|
|
921
|
+
if (refreshedData) {
|
|
922
|
+
iframe.contentWindow?.postMessage({
|
|
923
|
+
type: "PASSKEY_REFRESH_COMPLETE",
|
|
924
|
+
...refreshedData
|
|
925
|
+
}, dialogOrigin);
|
|
926
|
+
} else {
|
|
927
|
+
iframe.contentWindow?.postMessage({
|
|
928
|
+
type: "PASSKEY_REFRESH_ERROR",
|
|
929
|
+
error: "Failed to refresh quote"
|
|
930
|
+
}, dialogOrigin);
|
|
931
|
+
}
|
|
932
|
+
return;
|
|
933
|
+
}
|
|
934
|
+
const payload = message?.data;
|
|
935
|
+
if (message?.type === "PASSKEY_SIGNING_RESULT") {
|
|
936
|
+
window.removeEventListener("message", handleMessage);
|
|
937
|
+
if (message.success && payload?.intentId) {
|
|
938
|
+
resolve({
|
|
939
|
+
success: true,
|
|
940
|
+
intentId: payload.intentId
|
|
941
|
+
});
|
|
942
|
+
} else if (message.success && payload?.signature) {
|
|
531
943
|
resolve({
|
|
532
944
|
success: true,
|
|
533
945
|
signature: payload.signature,
|
|
@@ -593,9 +1005,7 @@ var PasskeyProviderClient = class {
|
|
|
593
1005
|
const response = await fetch(
|
|
594
1006
|
`${this.config.providerUrl}/api/intent/status/${intentId}`,
|
|
595
1007
|
{
|
|
596
|
-
headers: {
|
|
597
|
-
"x-client-id": this.config.clientId
|
|
598
|
-
}
|
|
1008
|
+
headers: this.config.clientId ? { "x-client-id": this.config.clientId } : {}
|
|
599
1009
|
}
|
|
600
1010
|
);
|
|
601
1011
|
if (!response.ok) {
|
|
@@ -630,6 +1040,43 @@ var PasskeyProviderClient = class {
|
|
|
630
1040
|
};
|
|
631
1041
|
}
|
|
632
1042
|
}
|
|
1043
|
+
/**
|
|
1044
|
+
* Get the history of intents for the authenticated user.
|
|
1045
|
+
*
|
|
1046
|
+
* Requires an active session (user must be logged in).
|
|
1047
|
+
*
|
|
1048
|
+
* @example
|
|
1049
|
+
* ```typescript
|
|
1050
|
+
* // Get recent intents
|
|
1051
|
+
* const history = await client.getIntentHistory({ limit: 10 });
|
|
1052
|
+
*
|
|
1053
|
+
* // Filter by status
|
|
1054
|
+
* const pending = await client.getIntentHistory({ status: 'pending' });
|
|
1055
|
+
*
|
|
1056
|
+
* // Filter by date range
|
|
1057
|
+
* const lastWeek = await client.getIntentHistory({
|
|
1058
|
+
* from: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString(),
|
|
1059
|
+
* });
|
|
1060
|
+
* ```
|
|
1061
|
+
*/
|
|
1062
|
+
async getIntentHistory(options) {
|
|
1063
|
+
const queryParams = new URLSearchParams();
|
|
1064
|
+
if (options?.limit) queryParams.set("limit", String(options.limit));
|
|
1065
|
+
if (options?.offset) queryParams.set("offset", String(options.offset));
|
|
1066
|
+
if (options?.status) queryParams.set("status", options.status);
|
|
1067
|
+
if (options?.from) queryParams.set("from", options.from);
|
|
1068
|
+
if (options?.to) queryParams.set("to", options.to);
|
|
1069
|
+
const url = `${this.config.providerUrl}/api/intent/history${queryParams.toString() ? `?${queryParams}` : ""}`;
|
|
1070
|
+
const response = await fetch(url, {
|
|
1071
|
+
headers: this.config.clientId ? { "x-client-id": this.config.clientId } : {},
|
|
1072
|
+
credentials: "include"
|
|
1073
|
+
});
|
|
1074
|
+
if (!response.ok) {
|
|
1075
|
+
const errorData = await response.json().catch(() => ({}));
|
|
1076
|
+
throw new Error(errorData.error || "Failed to get intent history");
|
|
1077
|
+
}
|
|
1078
|
+
return response.json();
|
|
1079
|
+
}
|
|
633
1080
|
/**
|
|
634
1081
|
* Send a swap intent through the Rhinestone orchestrator
|
|
635
1082
|
*
|
|
@@ -692,17 +1139,21 @@ var PasskeyProviderClient = class {
|
|
|
692
1139
|
};
|
|
693
1140
|
}
|
|
694
1141
|
};
|
|
695
|
-
|
|
696
|
-
if (
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
1142
|
+
let fromTokenAddress;
|
|
1143
|
+
if (options.fromToken) {
|
|
1144
|
+
const fromTokenResult = resolveToken(options.fromToken, "fromToken");
|
|
1145
|
+
if (!fromTokenResult.address) {
|
|
1146
|
+
return {
|
|
1147
|
+
success: false,
|
|
1148
|
+
intentId: "",
|
|
1149
|
+
status: "failed",
|
|
1150
|
+
error: {
|
|
1151
|
+
code: "INVALID_TOKEN",
|
|
1152
|
+
message: fromTokenResult.error || `Unknown fromToken: ${options.fromToken}`
|
|
1153
|
+
}
|
|
1154
|
+
};
|
|
1155
|
+
}
|
|
1156
|
+
fromTokenAddress = fromTokenResult.address;
|
|
706
1157
|
}
|
|
707
1158
|
const toTokenResult = resolveToken(options.toToken, "toToken");
|
|
708
1159
|
if (!toTokenResult.address) {
|
|
@@ -716,18 +1167,17 @@ var PasskeyProviderClient = class {
|
|
|
716
1167
|
}
|
|
717
1168
|
};
|
|
718
1169
|
}
|
|
719
|
-
const fromTokenAddress = fromTokenResult.address;
|
|
720
1170
|
const toTokenAddress = toTokenResult.address;
|
|
721
1171
|
console.log("[SDK sendSwap] Token resolution:", {
|
|
722
|
-
fromToken: options.fromToken,
|
|
723
|
-
fromTokenAddress,
|
|
1172
|
+
fromToken: options.fromToken ?? "Any",
|
|
1173
|
+
fromTokenAddress: fromTokenAddress ?? "orchestrator picks",
|
|
724
1174
|
toToken: options.toToken,
|
|
725
1175
|
toTokenAddress,
|
|
726
1176
|
targetChain: options.targetChain
|
|
727
1177
|
});
|
|
728
1178
|
const formatTokenLabel = (token, fallback) => {
|
|
729
1179
|
if (!token.startsWith("0x")) {
|
|
730
|
-
return token
|
|
1180
|
+
return token;
|
|
731
1181
|
}
|
|
732
1182
|
try {
|
|
733
1183
|
return getTokenSymbol(token, options.targetChain);
|
|
@@ -735,15 +1185,11 @@ var PasskeyProviderClient = class {
|
|
|
735
1185
|
return fallback;
|
|
736
1186
|
}
|
|
737
1187
|
};
|
|
738
|
-
const fromSymbol = formatTokenLabel(
|
|
739
|
-
options.fromToken,
|
|
740
|
-
`${options.fromToken.slice(0, 6)}...${options.fromToken.slice(-4)}`
|
|
741
|
-
);
|
|
742
1188
|
const toSymbol = formatTokenLabel(
|
|
743
1189
|
options.toToken,
|
|
744
1190
|
`${options.toToken.slice(0, 6)}...${options.toToken.slice(-4)}`
|
|
745
1191
|
);
|
|
746
|
-
const isFromNativeEth = fromTokenAddress === "0x0000000000000000000000000000000000000000";
|
|
1192
|
+
const isFromNativeEth = fromTokenAddress ? fromTokenAddress === "0x0000000000000000000000000000000000000000" : false;
|
|
747
1193
|
const isToNativeEth = toTokenAddress === "0x0000000000000000000000000000000000000000";
|
|
748
1194
|
const KNOWN_DECIMALS = {
|
|
749
1195
|
ETH: 18,
|
|
@@ -753,31 +1199,33 @@ var PasskeyProviderClient = class {
|
|
|
753
1199
|
USDT0: 6
|
|
754
1200
|
};
|
|
755
1201
|
const getDecimals = (symbol, chainId) => {
|
|
756
|
-
const upperSymbol = symbol.toUpperCase();
|
|
757
1202
|
try {
|
|
758
|
-
const
|
|
759
|
-
|
|
1203
|
+
const match = getSupportedTokens(chainId).find(
|
|
1204
|
+
(t) => t.symbol.toUpperCase() === symbol.toUpperCase()
|
|
1205
|
+
);
|
|
1206
|
+
if (match) {
|
|
1207
|
+
console.log(`[SDK] getTokenDecimals(${match.symbol}, ${chainId}) = ${match.decimals}`);
|
|
1208
|
+
return match.decimals;
|
|
1209
|
+
}
|
|
1210
|
+
const decimals = getTokenDecimals(symbol, chainId);
|
|
1211
|
+
console.log(`[SDK] getTokenDecimals(${symbol}, ${chainId}) = ${decimals}`);
|
|
760
1212
|
return decimals;
|
|
761
1213
|
} catch (e) {
|
|
762
|
-
|
|
1214
|
+
const upperSymbol = symbol.toUpperCase();
|
|
1215
|
+
console.warn(`[SDK] getTokenDecimals failed for ${symbol}, using fallback`, e);
|
|
763
1216
|
return KNOWN_DECIMALS[upperSymbol] ?? 18;
|
|
764
1217
|
}
|
|
765
1218
|
};
|
|
766
|
-
const fromDecimals = getDecimals(options.fromToken, options.targetChain);
|
|
767
1219
|
const toDecimals = getDecimals(options.toToken, options.targetChain);
|
|
768
|
-
const isBridge = options.fromToken.toUpperCase() === options.toToken.toUpperCase();
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
amount: parseUnits(options.amount, toDecimals).toString()
|
|
774
|
-
}];
|
|
775
|
-
}
|
|
1220
|
+
const isBridge = options.fromToken ? options.fromToken.toUpperCase() === options.toToken.toUpperCase() : false;
|
|
1221
|
+
const tokenRequests = [{
|
|
1222
|
+
token: toTokenAddress,
|
|
1223
|
+
amount: parseUnits(options.amount, toDecimals)
|
|
1224
|
+
}];
|
|
776
1225
|
console.log("[SDK sendSwap] Building intent:", {
|
|
777
1226
|
isBridge,
|
|
778
1227
|
isFromNativeEth,
|
|
779
1228
|
isToNativeEth,
|
|
780
|
-
fromDecimals,
|
|
781
1229
|
toDecimals,
|
|
782
1230
|
tokenRequests
|
|
783
1231
|
});
|
|
@@ -788,15 +1236,20 @@ var PasskeyProviderClient = class {
|
|
|
788
1236
|
{
|
|
789
1237
|
// Minimal call - just signals to orchestrator we want the tokenRequests delivered
|
|
790
1238
|
to: toTokenAddress,
|
|
791
|
-
value: "0"
|
|
1239
|
+
value: "0",
|
|
1240
|
+
// SDK provides labels so dialog shows "Buy ETH" not "Send ETH / To: 0x000..."
|
|
1241
|
+
label: `Buy ${toSymbol}`,
|
|
1242
|
+
sublabel: `${options.amount} ${toSymbol}`
|
|
792
1243
|
}
|
|
793
1244
|
],
|
|
794
1245
|
// Request specific output tokens - this is what actually matters for swaps
|
|
795
1246
|
tokenRequests,
|
|
796
1247
|
// Constrain orchestrator to use only the fromToken as input
|
|
797
1248
|
// This ensures the swap uses the correct source token
|
|
798
|
-
//
|
|
799
|
-
sourceAssets: options.sourceAssets || [options.fromToken
|
|
1249
|
+
// Use canonical symbol casing from registry (e.g. "MockUSD" not "MOCKUSD")
|
|
1250
|
+
sourceAssets: options.sourceAssets || (options.fromToken ? [options.fromToken] : void 0),
|
|
1251
|
+
// Pass source chain ID so orchestrator knows which chain to look for tokens on
|
|
1252
|
+
sourceChainId: options.sourceChainId,
|
|
800
1253
|
closeOn: options.closeOn || "preconfirmed",
|
|
801
1254
|
waitForHash: options.waitForHash,
|
|
802
1255
|
hashTimeoutMs: options.hashTimeoutMs,
|
|
@@ -805,7 +1258,7 @@ var PasskeyProviderClient = class {
|
|
|
805
1258
|
return {
|
|
806
1259
|
...result,
|
|
807
1260
|
quote: result.success ? {
|
|
808
|
-
fromToken: fromTokenAddress,
|
|
1261
|
+
fromToken: fromTokenAddress ?? options.fromToken,
|
|
809
1262
|
toToken: toTokenAddress,
|
|
810
1263
|
amountIn: options.amount,
|
|
811
1264
|
amountOut: "",
|
|
@@ -847,26 +1300,23 @@ var PasskeyProviderClient = class {
|
|
|
847
1300
|
const themeParams = this.getThemeParams(options?.theme);
|
|
848
1301
|
const signingUrl = `${dialogUrl}/dialog/sign?mode=iframe${themeParams ? `&${themeParams}` : ""}`;
|
|
849
1302
|
const { dialog, iframe, cleanup } = this.createModalDialog(signingUrl);
|
|
850
|
-
const
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
}, dialogOrigin);
|
|
865
|
-
resolve();
|
|
1303
|
+
const ready = await this.waitForDialogReady(dialog, iframe, cleanup, {
|
|
1304
|
+
mode: "iframe",
|
|
1305
|
+
message: options.message,
|
|
1306
|
+
challenge: options.challenge || options.message,
|
|
1307
|
+
username: options.username,
|
|
1308
|
+
description: options.description,
|
|
1309
|
+
metadata: options.metadata
|
|
1310
|
+
});
|
|
1311
|
+
if (!ready) {
|
|
1312
|
+
return {
|
|
1313
|
+
success: false,
|
|
1314
|
+
error: {
|
|
1315
|
+
code: "USER_REJECTED",
|
|
1316
|
+
message: "User closed the dialog"
|
|
866
1317
|
}
|
|
867
1318
|
};
|
|
868
|
-
|
|
869
|
-
});
|
|
1319
|
+
}
|
|
870
1320
|
const signingResult = await this.waitForSigningResponse(dialog, iframe, cleanup);
|
|
871
1321
|
cleanup();
|
|
872
1322
|
if (signingResult.success) {
|
|
@@ -935,31 +1385,28 @@ var PasskeyProviderClient = class {
|
|
|
935
1385
|
const themeParams = this.getThemeParams(options?.theme);
|
|
936
1386
|
const signingUrl = `${dialogUrl}/dialog/sign?mode=iframe${themeParams ? `&${themeParams}` : ""}`;
|
|
937
1387
|
const { dialog, iframe, cleanup } = this.createModalDialog(signingUrl);
|
|
938
|
-
const
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
}, dialogOrigin);
|
|
958
|
-
resolve();
|
|
1388
|
+
const ready = await this.waitForDialogReady(dialog, iframe, cleanup, {
|
|
1389
|
+
mode: "iframe",
|
|
1390
|
+
signingMode: "typedData",
|
|
1391
|
+
typedData: {
|
|
1392
|
+
domain: options.domain,
|
|
1393
|
+
types: options.types,
|
|
1394
|
+
primaryType: options.primaryType,
|
|
1395
|
+
message: options.message
|
|
1396
|
+
},
|
|
1397
|
+
challenge: signedHash,
|
|
1398
|
+
username: options.username,
|
|
1399
|
+
description: options.description
|
|
1400
|
+
});
|
|
1401
|
+
if (!ready) {
|
|
1402
|
+
return {
|
|
1403
|
+
success: false,
|
|
1404
|
+
error: {
|
|
1405
|
+
code: "USER_REJECTED",
|
|
1406
|
+
message: "User closed the dialog"
|
|
959
1407
|
}
|
|
960
1408
|
};
|
|
961
|
-
|
|
962
|
-
});
|
|
1409
|
+
}
|
|
963
1410
|
const signingResult = await this.waitForSigningResponse(dialog, iframe, cleanup);
|
|
964
1411
|
cleanup();
|
|
965
1412
|
if (signingResult.success) {
|
|
@@ -1113,9 +1560,7 @@ var PasskeyProviderClient = class {
|
|
|
1113
1560
|
const response = await fetch(
|
|
1114
1561
|
`${this.config.providerUrl}/api/users/${encodeURIComponent(username)}/passkeys`,
|
|
1115
1562
|
{
|
|
1116
|
-
headers: {
|
|
1117
|
-
"x-client-id": this.config.clientId
|
|
1118
|
-
}
|
|
1563
|
+
headers: this.config.clientId ? { "x-client-id": this.config.clientId } : {}
|
|
1119
1564
|
}
|
|
1120
1565
|
);
|
|
1121
1566
|
if (!response.ok) {
|
|
@@ -1134,7 +1579,7 @@ var PasskeyProviderClient = class {
|
|
|
1134
1579
|
"Content-Type": "application/json"
|
|
1135
1580
|
},
|
|
1136
1581
|
body: JSON.stringify({
|
|
1137
|
-
clientId: this.config.clientId,
|
|
1582
|
+
...this.config.clientId && { clientId: this.config.clientId },
|
|
1138
1583
|
username: options.username,
|
|
1139
1584
|
challenge: options.challenge,
|
|
1140
1585
|
description: options.description,
|
|
@@ -1161,7 +1606,45 @@ var PasskeyProviderClient = class {
|
|
|
1161
1606
|
);
|
|
1162
1607
|
}
|
|
1163
1608
|
/**
|
|
1164
|
-
*
|
|
1609
|
+
* Wait for the dialog iframe to signal ready, then send init data.
|
|
1610
|
+
* Also handles early close (X button, escape, backdrop) during the ready phase.
|
|
1611
|
+
* Returns true if dialog is ready, false if it was closed before becoming ready.
|
|
1612
|
+
*/
|
|
1613
|
+
waitForDialogReady(dialog, iframe, cleanup, initMessage) {
|
|
1614
|
+
const dialogOrigin = this.getDialogOrigin();
|
|
1615
|
+
return new Promise((resolve) => {
|
|
1616
|
+
let settled = false;
|
|
1617
|
+
const teardown = () => {
|
|
1618
|
+
if (settled) return;
|
|
1619
|
+
settled = true;
|
|
1620
|
+
window.removeEventListener("message", handleMessage);
|
|
1621
|
+
dialog.removeEventListener("close", handleClose);
|
|
1622
|
+
};
|
|
1623
|
+
const handleMessage = (event) => {
|
|
1624
|
+
if (event.origin !== dialogOrigin) return;
|
|
1625
|
+
if (event.data?.type === "PASSKEY_READY") {
|
|
1626
|
+
teardown();
|
|
1627
|
+
iframe.contentWindow?.postMessage({
|
|
1628
|
+
type: "PASSKEY_INIT",
|
|
1629
|
+
...initMessage
|
|
1630
|
+
}, dialogOrigin);
|
|
1631
|
+
resolve(true);
|
|
1632
|
+
} else if (event.data?.type === "PASSKEY_CLOSE") {
|
|
1633
|
+
teardown();
|
|
1634
|
+
cleanup();
|
|
1635
|
+
resolve(false);
|
|
1636
|
+
}
|
|
1637
|
+
};
|
|
1638
|
+
const handleClose = () => {
|
|
1639
|
+
teardown();
|
|
1640
|
+
resolve(false);
|
|
1641
|
+
};
|
|
1642
|
+
window.addEventListener("message", handleMessage);
|
|
1643
|
+
dialog.addEventListener("close", handleClose);
|
|
1644
|
+
});
|
|
1645
|
+
}
|
|
1646
|
+
/**
|
|
1647
|
+
* Create a modal dialog with an iframe inside.
|
|
1165
1648
|
*/
|
|
1166
1649
|
createModalDialog(url) {
|
|
1167
1650
|
const dialogUrl = this.getDialogUrl();
|
|
@@ -1304,12 +1787,24 @@ var PasskeyProviderClient = class {
|
|
|
1304
1787
|
};
|
|
1305
1788
|
return { dialog, iframe, cleanup };
|
|
1306
1789
|
}
|
|
1307
|
-
waitForModalAuthResponse(_dialog,
|
|
1790
|
+
waitForModalAuthResponse(_dialog, iframe, cleanup) {
|
|
1308
1791
|
const dialogOrigin = this.getDialogOrigin();
|
|
1309
1792
|
return new Promise((resolve) => {
|
|
1793
|
+
let dialogReady = false;
|
|
1310
1794
|
const handleMessage = (event) => {
|
|
1311
1795
|
if (event.origin !== dialogOrigin) return;
|
|
1312
1796
|
const data = event.data;
|
|
1797
|
+
if (data?.type === "PASSKEY_READY") {
|
|
1798
|
+
dialogReady = true;
|
|
1799
|
+
iframe.contentWindow?.postMessage({
|
|
1800
|
+
type: "PASSKEY_INIT",
|
|
1801
|
+
mode: "iframe"
|
|
1802
|
+
}, dialogOrigin);
|
|
1803
|
+
return;
|
|
1804
|
+
}
|
|
1805
|
+
if (!dialogReady && data?.type === "PASSKEY_CLOSE") {
|
|
1806
|
+
return;
|
|
1807
|
+
}
|
|
1313
1808
|
if (data?.type === "PASSKEY_LOGIN_RESULT") {
|
|
1314
1809
|
window.removeEventListener("message", handleMessage);
|
|
1315
1810
|
cleanup();
|
|
@@ -1339,6 +1834,11 @@ var PasskeyProviderClient = class {
|
|
|
1339
1834
|
error: data.error
|
|
1340
1835
|
});
|
|
1341
1836
|
}
|
|
1837
|
+
} else if (data?.type === "PASSKEY_RETRY_POPUP") {
|
|
1838
|
+
window.removeEventListener("message", handleMessage);
|
|
1839
|
+
cleanup();
|
|
1840
|
+
const popupUrl = data.data?.url?.replace("mode=iframe", "mode=popup") || `${this.getDialogUrl()}/dialog/auth?mode=popup${this.config.clientId ? `&clientId=${this.config.clientId}` : ""}`;
|
|
1841
|
+
this.waitForPopupAuthResponse(popupUrl).then(resolve);
|
|
1342
1842
|
} else if (data?.type === "PASSKEY_CLOSE") {
|
|
1343
1843
|
window.removeEventListener("message", handleMessage);
|
|
1344
1844
|
cleanup();
|
|
@@ -1354,6 +1854,77 @@ var PasskeyProviderClient = class {
|
|
|
1354
1854
|
window.addEventListener("message", handleMessage);
|
|
1355
1855
|
});
|
|
1356
1856
|
}
|
|
1857
|
+
/**
|
|
1858
|
+
* Open a popup for auth and wait for the result.
|
|
1859
|
+
* Used when iframe mode fails (e.g., due to password manager interference).
|
|
1860
|
+
*/
|
|
1861
|
+
waitForPopupAuthResponse(url) {
|
|
1862
|
+
const dialogOrigin = this.getDialogOrigin();
|
|
1863
|
+
const popup = this.openPopup(url);
|
|
1864
|
+
return new Promise((resolve) => {
|
|
1865
|
+
const pollTimer = setInterval(() => {
|
|
1866
|
+
if (popup?.closed) {
|
|
1867
|
+
clearInterval(pollTimer);
|
|
1868
|
+
window.removeEventListener("message", handleMessage);
|
|
1869
|
+
resolve({
|
|
1870
|
+
success: false,
|
|
1871
|
+
error: {
|
|
1872
|
+
code: "USER_CANCELLED",
|
|
1873
|
+
message: "Authentication was cancelled"
|
|
1874
|
+
}
|
|
1875
|
+
});
|
|
1876
|
+
}
|
|
1877
|
+
}, 500);
|
|
1878
|
+
const handleMessage = (event) => {
|
|
1879
|
+
if (event.origin !== dialogOrigin) return;
|
|
1880
|
+
const data = event.data;
|
|
1881
|
+
if (data?.type === "PASSKEY_LOGIN_RESULT") {
|
|
1882
|
+
clearInterval(pollTimer);
|
|
1883
|
+
window.removeEventListener("message", handleMessage);
|
|
1884
|
+
popup?.close();
|
|
1885
|
+
if (data.success) {
|
|
1886
|
+
resolve({
|
|
1887
|
+
success: true,
|
|
1888
|
+
username: data.data?.username,
|
|
1889
|
+
user: data.data?.user
|
|
1890
|
+
});
|
|
1891
|
+
} else {
|
|
1892
|
+
resolve({
|
|
1893
|
+
success: false,
|
|
1894
|
+
error: data.error
|
|
1895
|
+
});
|
|
1896
|
+
}
|
|
1897
|
+
} else if (data?.type === "PASSKEY_REGISTER_RESULT") {
|
|
1898
|
+
clearInterval(pollTimer);
|
|
1899
|
+
window.removeEventListener("message", handleMessage);
|
|
1900
|
+
popup?.close();
|
|
1901
|
+
if (data.success) {
|
|
1902
|
+
resolve({
|
|
1903
|
+
success: true,
|
|
1904
|
+
username: data.data?.username
|
|
1905
|
+
});
|
|
1906
|
+
} else {
|
|
1907
|
+
resolve({
|
|
1908
|
+
success: false,
|
|
1909
|
+
error: data.error
|
|
1910
|
+
});
|
|
1911
|
+
}
|
|
1912
|
+
} else if (data?.type === "PASSKEY_CLOSE") {
|
|
1913
|
+
clearInterval(pollTimer);
|
|
1914
|
+
window.removeEventListener("message", handleMessage);
|
|
1915
|
+
popup?.close();
|
|
1916
|
+
resolve({
|
|
1917
|
+
success: false,
|
|
1918
|
+
error: {
|
|
1919
|
+
code: "USER_CANCELLED",
|
|
1920
|
+
message: "Authentication was cancelled"
|
|
1921
|
+
}
|
|
1922
|
+
});
|
|
1923
|
+
}
|
|
1924
|
+
};
|
|
1925
|
+
window.addEventListener("message", handleMessage);
|
|
1926
|
+
});
|
|
1927
|
+
}
|
|
1357
1928
|
waitForAuthenticateResponse(_dialog, _iframe, cleanup) {
|
|
1358
1929
|
const dialogOrigin = this.getDialogOrigin();
|
|
1359
1930
|
return new Promise((resolve) => {
|
|
@@ -1393,6 +1964,44 @@ var PasskeyProviderClient = class {
|
|
|
1393
1964
|
window.addEventListener("message", handleMessage);
|
|
1394
1965
|
});
|
|
1395
1966
|
}
|
|
1967
|
+
waitForConnectResponse(_dialog, _iframe, cleanup) {
|
|
1968
|
+
const dialogOrigin = this.getDialogOrigin();
|
|
1969
|
+
return new Promise((resolve) => {
|
|
1970
|
+
const handleMessage = (event) => {
|
|
1971
|
+
if (event.origin !== dialogOrigin) return;
|
|
1972
|
+
const data = event.data;
|
|
1973
|
+
if (data?.type === "PASSKEY_CONNECT_RESULT") {
|
|
1974
|
+
window.removeEventListener("message", handleMessage);
|
|
1975
|
+
cleanup();
|
|
1976
|
+
if (data.success) {
|
|
1977
|
+
resolve({
|
|
1978
|
+
success: true,
|
|
1979
|
+
username: data.data?.username,
|
|
1980
|
+
autoConnected: data.data?.autoConnected
|
|
1981
|
+
});
|
|
1982
|
+
} else {
|
|
1983
|
+
resolve({
|
|
1984
|
+
success: false,
|
|
1985
|
+
action: data.data?.action,
|
|
1986
|
+
error: data.error
|
|
1987
|
+
});
|
|
1988
|
+
}
|
|
1989
|
+
} else if (data?.type === "PASSKEY_CLOSE") {
|
|
1990
|
+
window.removeEventListener("message", handleMessage);
|
|
1991
|
+
cleanup();
|
|
1992
|
+
resolve({
|
|
1993
|
+
success: false,
|
|
1994
|
+
action: "cancel",
|
|
1995
|
+
error: {
|
|
1996
|
+
code: "USER_CANCELLED",
|
|
1997
|
+
message: "Connection was cancelled"
|
|
1998
|
+
}
|
|
1999
|
+
});
|
|
2000
|
+
}
|
|
2001
|
+
};
|
|
2002
|
+
window.addEventListener("message", handleMessage);
|
|
2003
|
+
});
|
|
2004
|
+
}
|
|
1396
2005
|
waitForModalSigningResponse(requestId, _dialog, _iframe, cleanup) {
|
|
1397
2006
|
const dialogOrigin = this.getDialogOrigin();
|
|
1398
2007
|
return new Promise((resolve) => {
|
|
@@ -1481,9 +2090,7 @@ var PasskeyProviderClient = class {
|
|
|
1481
2090
|
const response = await fetch(
|
|
1482
2091
|
`${this.config.providerUrl}/api/sign/request/${requestId}`,
|
|
1483
2092
|
{
|
|
1484
|
-
headers: {
|
|
1485
|
-
"x-client-id": this.config.clientId
|
|
1486
|
-
}
|
|
2093
|
+
headers: this.config.clientId ? { "x-client-id": this.config.clientId } : {}
|
|
1487
2094
|
}
|
|
1488
2095
|
);
|
|
1489
2096
|
if (!response.ok) {
|
|
@@ -1601,7 +2208,7 @@ import {
|
|
|
1601
2208
|
} from "viem";
|
|
1602
2209
|
import { toAccount as toAccount2 } from "viem/accounts";
|
|
1603
2210
|
function createPasskeyWalletClient(config) {
|
|
1604
|
-
const provider = new
|
|
2211
|
+
const provider = new OneAuthClient({
|
|
1605
2212
|
providerUrl: config.providerUrl,
|
|
1606
2213
|
clientId: config.clientId,
|
|
1607
2214
|
dialogUrl: config.dialogUrl
|
|
@@ -1625,6 +2232,9 @@ function createPasskeyWalletClient(config) {
|
|
|
1625
2232
|
if (!result.success) {
|
|
1626
2233
|
throw new Error(result.error?.message || "Signing failed");
|
|
1627
2234
|
}
|
|
2235
|
+
if (!result.signature) {
|
|
2236
|
+
throw new Error("No signature received");
|
|
2237
|
+
}
|
|
1628
2238
|
return encodeWebAuthnSignature(result.signature);
|
|
1629
2239
|
};
|
|
1630
2240
|
const signTransactionImpl = async (transaction) => {
|
|
@@ -1645,6 +2255,9 @@ function createPasskeyWalletClient(config) {
|
|
|
1645
2255
|
if (!result.success) {
|
|
1646
2256
|
throw new Error(result.error?.message || "Signing failed");
|
|
1647
2257
|
}
|
|
2258
|
+
if (!result.signature) {
|
|
2259
|
+
throw new Error("No signature received");
|
|
2260
|
+
}
|
|
1648
2261
|
return encodeWebAuthnSignature(result.signature);
|
|
1649
2262
|
};
|
|
1650
2263
|
const signTypedDataImpl = async (typedData) => {
|
|
@@ -1666,8 +2279,35 @@ function createPasskeyWalletClient(config) {
|
|
|
1666
2279
|
if (!result.success) {
|
|
1667
2280
|
throw new Error(result.error?.message || "Signing failed");
|
|
1668
2281
|
}
|
|
2282
|
+
if (!result.signature) {
|
|
2283
|
+
throw new Error("No signature received");
|
|
2284
|
+
}
|
|
1669
2285
|
return encodeWebAuthnSignature(result.signature);
|
|
1670
2286
|
};
|
|
2287
|
+
const buildIntentPayload = async (calls, targetChainOverride) => {
|
|
2288
|
+
const targetChain = targetChainOverride ?? config.chain.id;
|
|
2289
|
+
const intentCalls = calls.map((call) => ({
|
|
2290
|
+
to: call.to,
|
|
2291
|
+
data: call.data || "0x",
|
|
2292
|
+
value: call.value !== void 0 ? call.value.toString() : "0",
|
|
2293
|
+
label: call.label,
|
|
2294
|
+
sublabel: call.sublabel
|
|
2295
|
+
}));
|
|
2296
|
+
if (config.signIntent) {
|
|
2297
|
+
const signedIntent = await config.signIntent({
|
|
2298
|
+
username: config.username,
|
|
2299
|
+
accountAddress: config.accountAddress,
|
|
2300
|
+
targetChain,
|
|
2301
|
+
calls: intentCalls
|
|
2302
|
+
});
|
|
2303
|
+
return { signedIntent };
|
|
2304
|
+
}
|
|
2305
|
+
return {
|
|
2306
|
+
username: config.username,
|
|
2307
|
+
targetChain,
|
|
2308
|
+
calls: intentCalls
|
|
2309
|
+
};
|
|
2310
|
+
};
|
|
1671
2311
|
const account = toAccount2({
|
|
1672
2312
|
address: config.accountAddress,
|
|
1673
2313
|
signMessage: ({ message }) => signMessageImpl(message),
|
|
@@ -1684,24 +2324,18 @@ function createPasskeyWalletClient(config) {
|
|
|
1684
2324
|
* Send a single transaction via intent flow
|
|
1685
2325
|
*/
|
|
1686
2326
|
async sendTransaction(transaction) {
|
|
2327
|
+
const targetChain = typeof transaction.chainId === "number" ? transaction.chainId : transaction.chain?.id;
|
|
1687
2328
|
const calls = [
|
|
1688
2329
|
{
|
|
1689
2330
|
to: transaction.to,
|
|
1690
|
-
data: transaction.data,
|
|
2331
|
+
data: transaction.data || "0x",
|
|
1691
2332
|
value: transaction.value
|
|
1692
2333
|
}
|
|
1693
2334
|
];
|
|
1694
2335
|
const closeOn = config.waitForHash ?? true ? "completed" : "preconfirmed";
|
|
2336
|
+
const intentPayload = await buildIntentPayload(calls, targetChain);
|
|
1695
2337
|
const result = await provider.sendIntent({
|
|
1696
|
-
|
|
1697
|
-
targetChain: config.chain.id,
|
|
1698
|
-
calls: calls.map((call) => ({
|
|
1699
|
-
to: call.to,
|
|
1700
|
-
data: call.data,
|
|
1701
|
-
value: call.value ? call.value.toString() : "0",
|
|
1702
|
-
label: call.label,
|
|
1703
|
-
sublabel: call.sublabel
|
|
1704
|
-
})),
|
|
2338
|
+
...intentPayload,
|
|
1705
2339
|
closeOn,
|
|
1706
2340
|
waitForHash: config.waitForHash ?? true,
|
|
1707
2341
|
hashTimeoutMs: config.hashTimeoutMs,
|
|
@@ -1716,18 +2350,12 @@ function createPasskeyWalletClient(config) {
|
|
|
1716
2350
|
* Send multiple calls as a single batched transaction
|
|
1717
2351
|
*/
|
|
1718
2352
|
async sendCalls(params) {
|
|
1719
|
-
const { calls } = params;
|
|
2353
|
+
const { calls, chainId: targetChain, tokenRequests } = params;
|
|
1720
2354
|
const closeOn = config.waitForHash ?? true ? "completed" : "preconfirmed";
|
|
2355
|
+
const intentPayload = await buildIntentPayload(calls, targetChain);
|
|
1721
2356
|
const result = await provider.sendIntent({
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
calls: calls.map((call) => ({
|
|
1725
|
-
to: call.to,
|
|
1726
|
-
data: call.data,
|
|
1727
|
-
value: call.value ? call.value.toString() : "0",
|
|
1728
|
-
label: call.label,
|
|
1729
|
-
sublabel: call.sublabel
|
|
1730
|
-
})),
|
|
2357
|
+
...intentPayload,
|
|
2358
|
+
tokenRequests,
|
|
1731
2359
|
closeOn,
|
|
1732
2360
|
waitForHash: config.waitForHash ?? true,
|
|
1733
2361
|
hashTimeoutMs: config.hashTimeoutMs,
|
|
@@ -2171,9 +2799,10 @@ function BatchQueueWidget({ onSignAll }) {
|
|
|
2171
2799
|
|
|
2172
2800
|
// src/verify.ts
|
|
2173
2801
|
import { keccak256, toBytes } from "viem";
|
|
2174
|
-
var
|
|
2802
|
+
var ETHEREUM_MESSAGE_PREFIX = "Ethereum Signed Message:\n";
|
|
2803
|
+
var PASSKEY_MESSAGE_PREFIX = ETHEREUM_MESSAGE_PREFIX;
|
|
2175
2804
|
function hashMessage2(message) {
|
|
2176
|
-
const prefixed =
|
|
2805
|
+
const prefixed = ETHEREUM_MESSAGE_PREFIX + message.length.toString() + message;
|
|
2177
2806
|
return keccak256(toBytes(prefixed));
|
|
2178
2807
|
}
|
|
2179
2808
|
function verifyMessageHash(message, signedHash) {
|
|
@@ -2184,8 +2813,11 @@ function verifyMessageHash(message, signedHash) {
|
|
|
2184
2813
|
export {
|
|
2185
2814
|
BatchQueueProvider,
|
|
2186
2815
|
BatchQueueWidget,
|
|
2816
|
+
ETHEREUM_MESSAGE_PREFIX,
|
|
2817
|
+
OneAuthClient,
|
|
2187
2818
|
PASSKEY_MESSAGE_PREFIX,
|
|
2188
|
-
PasskeyProviderClient,
|
|
2819
|
+
OneAuthClient as PasskeyProviderClient,
|
|
2820
|
+
createOneAuthProvider,
|
|
2189
2821
|
createPasskeyAccount,
|
|
2190
2822
|
createPasskeyProvider,
|
|
2191
2823
|
createPasskeyWalletClient,
|