@rhinestone/1auth 0.1.1 → 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-U7KZ4XMQ.mjs → chunk-TACK3LJN.mjs} +162 -22
- package/dist/chunk-TACK3LJN.mjs.map +1 -0
- package/dist/{client-B-HGKKaJ.d.mts → client-DyYGKWj3.d.mts} +302 -22
- package/dist/{client-B-HGKKaJ.d.ts → client-DyYGKWj3.d.ts} +302 -22
- package/dist/index.d.mts +20 -11
- package/dist/index.d.ts +20 -11
- package/dist/index.js +966 -221
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +805 -200
- 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 +2 -2
- package/dist/react.d.ts +2 -2
- package/dist/react.js.map +1 -1
- package/dist/react.mjs.map +1 -1
- package/dist/wagmi.d.mts +3 -3
- package/dist/wagmi.d.ts +3 -3
- package/dist/wagmi.js +153 -23
- package/dist/wagmi.js.map +1 -1
- package/dist/wagmi.mjs +1 -1
- package/package.json +19 -6
- package/dist/chunk-U7KZ4XMQ.mjs.map +0 -1
- package/dist/provider-CDIRlA4y.d.mts +0 -25
- package/dist/provider-Cy1StrOe.d.ts +0 -25
package/dist/index.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";
|
|
@@ -95,9 +96,7 @@ var OneAuthClient = class {
|
|
|
95
96
|
const response = await fetch(
|
|
96
97
|
`${this.config.providerUrl}/api/intent/status/${intentId}`,
|
|
97
98
|
{
|
|
98
|
-
headers: {
|
|
99
|
-
"x-client-id": this.config.clientId
|
|
100
|
-
}
|
|
99
|
+
headers: this.config.clientId ? { "x-client-id": this.config.clientId } : {}
|
|
101
100
|
}
|
|
102
101
|
);
|
|
103
102
|
if (response.ok) {
|
|
@@ -121,9 +120,11 @@ var OneAuthClient = class {
|
|
|
121
120
|
async authWithModal(options) {
|
|
122
121
|
const dialogUrl = this.getDialogUrl();
|
|
123
122
|
const params = new URLSearchParams({
|
|
124
|
-
clientId: this.config.clientId,
|
|
125
123
|
mode: "iframe"
|
|
126
124
|
});
|
|
125
|
+
if (this.config.clientId) {
|
|
126
|
+
params.set("clientId", this.config.clientId);
|
|
127
|
+
}
|
|
127
128
|
if (options?.username) {
|
|
128
129
|
params.set("username", options.username);
|
|
129
130
|
}
|
|
@@ -139,6 +140,55 @@ var OneAuthClient = class {
|
|
|
139
140
|
const { dialog, iframe, cleanup } = this.createModalDialog(url);
|
|
140
141
|
return this.waitForModalAuthResponse(dialog, iframe, cleanup);
|
|
141
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
|
+
}
|
|
142
192
|
/**
|
|
143
193
|
* Authenticate a user with an optional challenge to sign.
|
|
144
194
|
*
|
|
@@ -174,9 +224,11 @@ var OneAuthClient = class {
|
|
|
174
224
|
async authenticate(options) {
|
|
175
225
|
const dialogUrl = this.getDialogUrl();
|
|
176
226
|
const params = new URLSearchParams({
|
|
177
|
-
clientId: this.config.clientId,
|
|
178
227
|
mode: "iframe"
|
|
179
228
|
});
|
|
229
|
+
if (this.config.clientId) {
|
|
230
|
+
params.set("clientId", this.config.clientId);
|
|
231
|
+
}
|
|
180
232
|
if (options?.challenge) {
|
|
181
233
|
params.set("challenge", options.challenge);
|
|
182
234
|
}
|
|
@@ -190,28 +242,30 @@ var OneAuthClient = class {
|
|
|
190
242
|
return this.waitForAuthenticateResponse(dialog, iframe, cleanup);
|
|
191
243
|
}
|
|
192
244
|
/**
|
|
193
|
-
* Show signing in a modal overlay (
|
|
245
|
+
* Show signing in a modal overlay (iframe dialog)
|
|
194
246
|
*/
|
|
195
247
|
async signWithModal(options) {
|
|
196
248
|
const dialogUrl = this.getDialogUrl();
|
|
197
249
|
const themeParams = this.getThemeParams(options?.theme);
|
|
198
250
|
const signingUrl = `${dialogUrl}/dialog/sign?mode=iframe${themeParams ? `&${themeParams}` : ""}`;
|
|
199
251
|
const { dialog, iframe, cleanup } = this.createModalDialog(signingUrl);
|
|
200
|
-
const
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
username: options.username,
|
|
208
|
-
description: options.description,
|
|
209
|
-
transaction: options.transaction,
|
|
210
|
-
metadata: options.metadata
|
|
211
|
-
}, dialogOrigin);
|
|
212
|
-
resolve();
|
|
213
|
-
};
|
|
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
|
|
214
259
|
});
|
|
260
|
+
if (!ready) {
|
|
261
|
+
return {
|
|
262
|
+
success: false,
|
|
263
|
+
error: {
|
|
264
|
+
code: "USER_REJECTED",
|
|
265
|
+
message: "User closed the dialog"
|
|
266
|
+
}
|
|
267
|
+
};
|
|
268
|
+
}
|
|
215
269
|
return this.waitForSigningResponse(dialog, iframe, cleanup);
|
|
216
270
|
}
|
|
217
271
|
/**
|
|
@@ -284,16 +338,21 @@ var OneAuthClient = class {
|
|
|
284
338
|
}
|
|
285
339
|
};
|
|
286
340
|
}
|
|
341
|
+
const serializedTokenRequests = options.tokenRequests?.map((r) => ({
|
|
342
|
+
token: r.token,
|
|
343
|
+
amount: r.amount.toString()
|
|
344
|
+
}));
|
|
287
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
|
+
};
|
|
288
355
|
try {
|
|
289
|
-
const requestBody = signedIntent || {
|
|
290
|
-
username: options.username,
|
|
291
|
-
targetChain: options.targetChain,
|
|
292
|
-
calls: options.calls,
|
|
293
|
-
tokenRequests: options.tokenRequests,
|
|
294
|
-
sourceAssets: options.sourceAssets,
|
|
295
|
-
clientId: this.config.clientId
|
|
296
|
-
};
|
|
297
356
|
const response = await fetch(`${this.config.providerUrl}/api/intent/prepare`, {
|
|
298
357
|
method: "POST",
|
|
299
358
|
headers: {
|
|
@@ -330,112 +389,190 @@ var OneAuthClient = class {
|
|
|
330
389
|
};
|
|
331
390
|
}
|
|
332
391
|
const dialogUrl = this.getDialogUrl();
|
|
333
|
-
const
|
|
392
|
+
const themeParams = this.getThemeParams();
|
|
393
|
+
const signingUrl = `${dialogUrl}/dialog/sign?mode=iframe${themeParams ? `&${themeParams}` : ""}`;
|
|
334
394
|
const { dialog, iframe, cleanup } = this.createModalDialog(signingUrl);
|
|
335
395
|
const dialogOrigin = this.getDialogOrigin();
|
|
336
|
-
await
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
accountAddress: prepareResponse.accountAddress,
|
|
350
|
-
intentId: prepareResponse.intentId
|
|
351
|
-
}, dialogOrigin);
|
|
352
|
-
resolve();
|
|
353
|
-
}
|
|
354
|
-
};
|
|
355
|
-
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
|
|
356
409
|
});
|
|
357
|
-
|
|
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
|
+
);
|
|
358
452
|
if (!signingResult.success) {
|
|
359
453
|
return {
|
|
360
454
|
success: false,
|
|
361
|
-
intentId:
|
|
455
|
+
intentId: "",
|
|
456
|
+
// No intentId yet - signing was cancelled before execute
|
|
362
457
|
status: "failed",
|
|
363
458
|
error: signingResult.error
|
|
364
459
|
};
|
|
365
460
|
}
|
|
461
|
+
const dialogExecutedIntent = "intentId" in signingResult && signingResult.intentId;
|
|
366
462
|
let executeResponse;
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
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) {
|
|
382
506
|
this.sendTransactionStatus(iframe, "failed");
|
|
383
507
|
await this.waitForDialogClose(dialog, cleanup);
|
|
384
508
|
return {
|
|
385
509
|
success: false,
|
|
386
|
-
intentId:
|
|
510
|
+
intentId: "",
|
|
511
|
+
// No intentId - network error before creation
|
|
387
512
|
status: "failed",
|
|
388
513
|
error: {
|
|
389
|
-
code: "
|
|
390
|
-
message:
|
|
514
|
+
code: "NETWORK_ERROR",
|
|
515
|
+
message: error instanceof Error ? error.message : "Network error"
|
|
391
516
|
}
|
|
392
517
|
};
|
|
393
518
|
}
|
|
394
|
-
executeResponse = await response.json();
|
|
395
|
-
} catch (error) {
|
|
396
|
-
this.sendTransactionStatus(iframe, "failed");
|
|
397
|
-
await this.waitForDialogClose(dialog, cleanup);
|
|
398
|
-
return {
|
|
399
|
-
success: false,
|
|
400
|
-
intentId: prepareResponse.intentId,
|
|
401
|
-
status: "failed",
|
|
402
|
-
error: {
|
|
403
|
-
code: "NETWORK_ERROR",
|
|
404
|
-
message: error instanceof Error ? error.message : "Network error"
|
|
405
|
-
}
|
|
406
|
-
};
|
|
407
519
|
}
|
|
408
|
-
const closeOn = options.closeOn || "preconfirmed";
|
|
409
|
-
const acceptPreconfirmations = closeOn !== "completed";
|
|
410
520
|
let finalStatus = executeResponse.status;
|
|
411
521
|
let finalTxHash = executeResponse.transactionHash;
|
|
412
522
|
if (finalStatus === "pending") {
|
|
413
|
-
this.sendTransactionStatus(iframe, "
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
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;
|
|
420
555
|
}
|
|
421
556
|
}
|
|
422
|
-
)
|
|
423
|
-
|
|
424
|
-
const waitResult = await waitResponse.json();
|
|
425
|
-
finalStatus = waitResult.status === "preconfirmed" || waitResult.status === "completed" ? "completed" : waitResult.status;
|
|
426
|
-
finalTxHash = waitResult.transactionHash;
|
|
427
|
-
} else {
|
|
428
|
-
console.error("Wait endpoint failed:", await waitResponse.text());
|
|
557
|
+
} catch (pollError) {
|
|
558
|
+
console.error("Failed to poll intent status:", pollError);
|
|
429
559
|
}
|
|
430
|
-
|
|
431
|
-
console.error("Failed to wait for intent:", waitError);
|
|
560
|
+
await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
|
|
432
561
|
}
|
|
433
562
|
}
|
|
434
|
-
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;
|
|
435
572
|
this.sendTransactionStatus(iframe, displayStatus, finalTxHash);
|
|
436
573
|
await this.waitForDialogClose(dialog, cleanup);
|
|
437
574
|
if (options.waitForHash && !finalTxHash) {
|
|
438
|
-
const hash = await this.waitForTransactionHash(
|
|
575
|
+
const hash = await this.waitForTransactionHash(executeResponse.intentId, {
|
|
439
576
|
timeoutMs: options.hashTimeoutMs,
|
|
440
577
|
intervalMs: options.hashIntervalMs
|
|
441
578
|
});
|
|
@@ -446,7 +583,7 @@ var OneAuthClient = class {
|
|
|
446
583
|
finalStatus = "failed";
|
|
447
584
|
return {
|
|
448
585
|
success: false,
|
|
449
|
-
intentId:
|
|
586
|
+
intentId: executeResponse.intentId,
|
|
450
587
|
status: finalStatus,
|
|
451
588
|
transactionHash: finalTxHash,
|
|
452
589
|
operationId: executeResponse.operationId,
|
|
@@ -458,14 +595,204 @@ var OneAuthClient = class {
|
|
|
458
595
|
}
|
|
459
596
|
}
|
|
460
597
|
return {
|
|
461
|
-
success:
|
|
462
|
-
intentId:
|
|
598
|
+
success: isSuccessStatus,
|
|
599
|
+
intentId: executeResponse.intentId,
|
|
463
600
|
status: finalStatus,
|
|
464
601
|
transactionHash: finalTxHash,
|
|
465
602
|
operationId: executeResponse.operationId,
|
|
466
603
|
error: executeResponse.error
|
|
467
604
|
};
|
|
468
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
|
+
}
|
|
469
796
|
/**
|
|
470
797
|
* Send transaction status to the dialog iframe
|
|
471
798
|
*/
|
|
@@ -542,7 +869,77 @@ var OneAuthClient = class {
|
|
|
542
869
|
const payload = message?.data;
|
|
543
870
|
if (message?.type === "PASSKEY_SIGNING_RESULT") {
|
|
544
871
|
window.removeEventListener("message", handleMessage);
|
|
545
|
-
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) {
|
|
546
943
|
resolve({
|
|
547
944
|
success: true,
|
|
548
945
|
signature: payload.signature,
|
|
@@ -608,9 +1005,7 @@ var OneAuthClient = class {
|
|
|
608
1005
|
const response = await fetch(
|
|
609
1006
|
`${this.config.providerUrl}/api/intent/status/${intentId}`,
|
|
610
1007
|
{
|
|
611
|
-
headers: {
|
|
612
|
-
"x-client-id": this.config.clientId
|
|
613
|
-
}
|
|
1008
|
+
headers: this.config.clientId ? { "x-client-id": this.config.clientId } : {}
|
|
614
1009
|
}
|
|
615
1010
|
);
|
|
616
1011
|
if (!response.ok) {
|
|
@@ -645,6 +1040,43 @@ var OneAuthClient = class {
|
|
|
645
1040
|
};
|
|
646
1041
|
}
|
|
647
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
|
+
}
|
|
648
1080
|
/**
|
|
649
1081
|
* Send a swap intent through the Rhinestone orchestrator
|
|
650
1082
|
*
|
|
@@ -707,17 +1139,21 @@ var OneAuthClient = class {
|
|
|
707
1139
|
};
|
|
708
1140
|
}
|
|
709
1141
|
};
|
|
710
|
-
|
|
711
|
-
if (
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
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;
|
|
721
1157
|
}
|
|
722
1158
|
const toTokenResult = resolveToken(options.toToken, "toToken");
|
|
723
1159
|
if (!toTokenResult.address) {
|
|
@@ -731,18 +1167,17 @@ var OneAuthClient = class {
|
|
|
731
1167
|
}
|
|
732
1168
|
};
|
|
733
1169
|
}
|
|
734
|
-
const fromTokenAddress = fromTokenResult.address;
|
|
735
1170
|
const toTokenAddress = toTokenResult.address;
|
|
736
1171
|
console.log("[SDK sendSwap] Token resolution:", {
|
|
737
|
-
fromToken: options.fromToken,
|
|
738
|
-
fromTokenAddress,
|
|
1172
|
+
fromToken: options.fromToken ?? "Any",
|
|
1173
|
+
fromTokenAddress: fromTokenAddress ?? "orchestrator picks",
|
|
739
1174
|
toToken: options.toToken,
|
|
740
1175
|
toTokenAddress,
|
|
741
1176
|
targetChain: options.targetChain
|
|
742
1177
|
});
|
|
743
1178
|
const formatTokenLabel = (token, fallback) => {
|
|
744
1179
|
if (!token.startsWith("0x")) {
|
|
745
|
-
return token
|
|
1180
|
+
return token;
|
|
746
1181
|
}
|
|
747
1182
|
try {
|
|
748
1183
|
return getTokenSymbol(token, options.targetChain);
|
|
@@ -750,15 +1185,11 @@ var OneAuthClient = class {
|
|
|
750
1185
|
return fallback;
|
|
751
1186
|
}
|
|
752
1187
|
};
|
|
753
|
-
const fromSymbol = formatTokenLabel(
|
|
754
|
-
options.fromToken,
|
|
755
|
-
`${options.fromToken.slice(0, 6)}...${options.fromToken.slice(-4)}`
|
|
756
|
-
);
|
|
757
1188
|
const toSymbol = formatTokenLabel(
|
|
758
1189
|
options.toToken,
|
|
759
1190
|
`${options.toToken.slice(0, 6)}...${options.toToken.slice(-4)}`
|
|
760
1191
|
);
|
|
761
|
-
const isFromNativeEth = fromTokenAddress === "0x0000000000000000000000000000000000000000";
|
|
1192
|
+
const isFromNativeEth = fromTokenAddress ? fromTokenAddress === "0x0000000000000000000000000000000000000000" : false;
|
|
762
1193
|
const isToNativeEth = toTokenAddress === "0x0000000000000000000000000000000000000000";
|
|
763
1194
|
const KNOWN_DECIMALS = {
|
|
764
1195
|
ETH: 18,
|
|
@@ -768,31 +1199,33 @@ var OneAuthClient = class {
|
|
|
768
1199
|
USDT0: 6
|
|
769
1200
|
};
|
|
770
1201
|
const getDecimals = (symbol, chainId) => {
|
|
771
|
-
const upperSymbol = symbol.toUpperCase();
|
|
772
1202
|
try {
|
|
773
|
-
const
|
|
774
|
-
|
|
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}`);
|
|
775
1212
|
return decimals;
|
|
776
1213
|
} catch (e) {
|
|
777
|
-
|
|
1214
|
+
const upperSymbol = symbol.toUpperCase();
|
|
1215
|
+
console.warn(`[SDK] getTokenDecimals failed for ${symbol}, using fallback`, e);
|
|
778
1216
|
return KNOWN_DECIMALS[upperSymbol] ?? 18;
|
|
779
1217
|
}
|
|
780
1218
|
};
|
|
781
|
-
const fromDecimals = getDecimals(options.fromToken, options.targetChain);
|
|
782
1219
|
const toDecimals = getDecimals(options.toToken, options.targetChain);
|
|
783
|
-
const isBridge = options.fromToken.toUpperCase() === options.toToken.toUpperCase();
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
amount: parseUnits(options.amount, toDecimals).toString()
|
|
789
|
-
}];
|
|
790
|
-
}
|
|
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
|
+
}];
|
|
791
1225
|
console.log("[SDK sendSwap] Building intent:", {
|
|
792
1226
|
isBridge,
|
|
793
1227
|
isFromNativeEth,
|
|
794
1228
|
isToNativeEth,
|
|
795
|
-
fromDecimals,
|
|
796
1229
|
toDecimals,
|
|
797
1230
|
tokenRequests
|
|
798
1231
|
});
|
|
@@ -803,15 +1236,20 @@ var OneAuthClient = class {
|
|
|
803
1236
|
{
|
|
804
1237
|
// Minimal call - just signals to orchestrator we want the tokenRequests delivered
|
|
805
1238
|
to: toTokenAddress,
|
|
806
|
-
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}`
|
|
807
1243
|
}
|
|
808
1244
|
],
|
|
809
1245
|
// Request specific output tokens - this is what actually matters for swaps
|
|
810
1246
|
tokenRequests,
|
|
811
1247
|
// Constrain orchestrator to use only the fromToken as input
|
|
812
1248
|
// This ensures the swap uses the correct source token
|
|
813
|
-
//
|
|
814
|
-
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,
|
|
815
1253
|
closeOn: options.closeOn || "preconfirmed",
|
|
816
1254
|
waitForHash: options.waitForHash,
|
|
817
1255
|
hashTimeoutMs: options.hashTimeoutMs,
|
|
@@ -820,7 +1258,7 @@ var OneAuthClient = class {
|
|
|
820
1258
|
return {
|
|
821
1259
|
...result,
|
|
822
1260
|
quote: result.success ? {
|
|
823
|
-
fromToken: fromTokenAddress,
|
|
1261
|
+
fromToken: fromTokenAddress ?? options.fromToken,
|
|
824
1262
|
toToken: toTokenAddress,
|
|
825
1263
|
amountIn: options.amount,
|
|
826
1264
|
amountOut: "",
|
|
@@ -862,26 +1300,23 @@ var OneAuthClient = class {
|
|
|
862
1300
|
const themeParams = this.getThemeParams(options?.theme);
|
|
863
1301
|
const signingUrl = `${dialogUrl}/dialog/sign?mode=iframe${themeParams ? `&${themeParams}` : ""}`;
|
|
864
1302
|
const { dialog, iframe, cleanup } = this.createModalDialog(signingUrl);
|
|
865
|
-
const
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
}, dialogOrigin);
|
|
880
|
-
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"
|
|
881
1317
|
}
|
|
882
1318
|
};
|
|
883
|
-
|
|
884
|
-
});
|
|
1319
|
+
}
|
|
885
1320
|
const signingResult = await this.waitForSigningResponse(dialog, iframe, cleanup);
|
|
886
1321
|
cleanup();
|
|
887
1322
|
if (signingResult.success) {
|
|
@@ -950,31 +1385,28 @@ var OneAuthClient = class {
|
|
|
950
1385
|
const themeParams = this.getThemeParams(options?.theme);
|
|
951
1386
|
const signingUrl = `${dialogUrl}/dialog/sign?mode=iframe${themeParams ? `&${themeParams}` : ""}`;
|
|
952
1387
|
const { dialog, iframe, cleanup } = this.createModalDialog(signingUrl);
|
|
953
|
-
const
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
}, dialogOrigin);
|
|
973
|
-
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"
|
|
974
1407
|
}
|
|
975
1408
|
};
|
|
976
|
-
|
|
977
|
-
});
|
|
1409
|
+
}
|
|
978
1410
|
const signingResult = await this.waitForSigningResponse(dialog, iframe, cleanup);
|
|
979
1411
|
cleanup();
|
|
980
1412
|
if (signingResult.success) {
|
|
@@ -1128,9 +1560,7 @@ var OneAuthClient = class {
|
|
|
1128
1560
|
const response = await fetch(
|
|
1129
1561
|
`${this.config.providerUrl}/api/users/${encodeURIComponent(username)}/passkeys`,
|
|
1130
1562
|
{
|
|
1131
|
-
headers: {
|
|
1132
|
-
"x-client-id": this.config.clientId
|
|
1133
|
-
}
|
|
1563
|
+
headers: this.config.clientId ? { "x-client-id": this.config.clientId } : {}
|
|
1134
1564
|
}
|
|
1135
1565
|
);
|
|
1136
1566
|
if (!response.ok) {
|
|
@@ -1149,7 +1579,7 @@ var OneAuthClient = class {
|
|
|
1149
1579
|
"Content-Type": "application/json"
|
|
1150
1580
|
},
|
|
1151
1581
|
body: JSON.stringify({
|
|
1152
|
-
clientId: this.config.clientId,
|
|
1582
|
+
...this.config.clientId && { clientId: this.config.clientId },
|
|
1153
1583
|
username: options.username,
|
|
1154
1584
|
challenge: options.challenge,
|
|
1155
1585
|
description: options.description,
|
|
@@ -1175,6 +1605,44 @@ var OneAuthClient = class {
|
|
|
1175
1605
|
`width=${POPUP_WIDTH},height=${POPUP_HEIGHT},left=${left},top=${top},popup=true`
|
|
1176
1606
|
);
|
|
1177
1607
|
}
|
|
1608
|
+
/**
|
|
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
|
+
}
|
|
1178
1646
|
/**
|
|
1179
1647
|
* Create a modal dialog with an iframe inside.
|
|
1180
1648
|
*/
|
|
@@ -1319,12 +1787,24 @@ var OneAuthClient = class {
|
|
|
1319
1787
|
};
|
|
1320
1788
|
return { dialog, iframe, cleanup };
|
|
1321
1789
|
}
|
|
1322
|
-
waitForModalAuthResponse(_dialog,
|
|
1790
|
+
waitForModalAuthResponse(_dialog, iframe, cleanup) {
|
|
1323
1791
|
const dialogOrigin = this.getDialogOrigin();
|
|
1324
1792
|
return new Promise((resolve) => {
|
|
1793
|
+
let dialogReady = false;
|
|
1325
1794
|
const handleMessage = (event) => {
|
|
1326
1795
|
if (event.origin !== dialogOrigin) return;
|
|
1327
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
|
+
}
|
|
1328
1808
|
if (data?.type === "PASSKEY_LOGIN_RESULT") {
|
|
1329
1809
|
window.removeEventListener("message", handleMessage);
|
|
1330
1810
|
cleanup();
|
|
@@ -1354,6 +1834,11 @@ var OneAuthClient = class {
|
|
|
1354
1834
|
error: data.error
|
|
1355
1835
|
});
|
|
1356
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);
|
|
1357
1842
|
} else if (data?.type === "PASSKEY_CLOSE") {
|
|
1358
1843
|
window.removeEventListener("message", handleMessage);
|
|
1359
1844
|
cleanup();
|
|
@@ -1369,6 +1854,77 @@ var OneAuthClient = class {
|
|
|
1369
1854
|
window.addEventListener("message", handleMessage);
|
|
1370
1855
|
});
|
|
1371
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
|
+
}
|
|
1372
1928
|
waitForAuthenticateResponse(_dialog, _iframe, cleanup) {
|
|
1373
1929
|
const dialogOrigin = this.getDialogOrigin();
|
|
1374
1930
|
return new Promise((resolve) => {
|
|
@@ -1408,6 +1964,44 @@ var OneAuthClient = class {
|
|
|
1408
1964
|
window.addEventListener("message", handleMessage);
|
|
1409
1965
|
});
|
|
1410
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
|
+
}
|
|
1411
2005
|
waitForModalSigningResponse(requestId, _dialog, _iframe, cleanup) {
|
|
1412
2006
|
const dialogOrigin = this.getDialogOrigin();
|
|
1413
2007
|
return new Promise((resolve) => {
|
|
@@ -1496,9 +2090,7 @@ var OneAuthClient = class {
|
|
|
1496
2090
|
const response = await fetch(
|
|
1497
2091
|
`${this.config.providerUrl}/api/sign/request/${requestId}`,
|
|
1498
2092
|
{
|
|
1499
|
-
headers: {
|
|
1500
|
-
"x-client-id": this.config.clientId
|
|
1501
|
-
}
|
|
2093
|
+
headers: this.config.clientId ? { "x-client-id": this.config.clientId } : {}
|
|
1502
2094
|
}
|
|
1503
2095
|
);
|
|
1504
2096
|
if (!response.ok) {
|
|
@@ -1640,6 +2232,9 @@ function createPasskeyWalletClient(config) {
|
|
|
1640
2232
|
if (!result.success) {
|
|
1641
2233
|
throw new Error(result.error?.message || "Signing failed");
|
|
1642
2234
|
}
|
|
2235
|
+
if (!result.signature) {
|
|
2236
|
+
throw new Error("No signature received");
|
|
2237
|
+
}
|
|
1643
2238
|
return encodeWebAuthnSignature(result.signature);
|
|
1644
2239
|
};
|
|
1645
2240
|
const signTransactionImpl = async (transaction) => {
|
|
@@ -1660,6 +2255,9 @@ function createPasskeyWalletClient(config) {
|
|
|
1660
2255
|
if (!result.success) {
|
|
1661
2256
|
throw new Error(result.error?.message || "Signing failed");
|
|
1662
2257
|
}
|
|
2258
|
+
if (!result.signature) {
|
|
2259
|
+
throw new Error("No signature received");
|
|
2260
|
+
}
|
|
1663
2261
|
return encodeWebAuthnSignature(result.signature);
|
|
1664
2262
|
};
|
|
1665
2263
|
const signTypedDataImpl = async (typedData) => {
|
|
@@ -1681,6 +2279,9 @@ function createPasskeyWalletClient(config) {
|
|
|
1681
2279
|
if (!result.success) {
|
|
1682
2280
|
throw new Error(result.error?.message || "Signing failed");
|
|
1683
2281
|
}
|
|
2282
|
+
if (!result.signature) {
|
|
2283
|
+
throw new Error("No signature received");
|
|
2284
|
+
}
|
|
1684
2285
|
return encodeWebAuthnSignature(result.signature);
|
|
1685
2286
|
};
|
|
1686
2287
|
const buildIntentPayload = async (calls, targetChainOverride) => {
|
|
@@ -1749,11 +2350,12 @@ function createPasskeyWalletClient(config) {
|
|
|
1749
2350
|
* Send multiple calls as a single batched transaction
|
|
1750
2351
|
*/
|
|
1751
2352
|
async sendCalls(params) {
|
|
1752
|
-
const { calls, chainId: targetChain } = params;
|
|
2353
|
+
const { calls, chainId: targetChain, tokenRequests } = params;
|
|
1753
2354
|
const closeOn = config.waitForHash ?? true ? "completed" : "preconfirmed";
|
|
1754
2355
|
const intentPayload = await buildIntentPayload(calls, targetChain);
|
|
1755
2356
|
const result = await provider.sendIntent({
|
|
1756
2357
|
...intentPayload,
|
|
2358
|
+
tokenRequests,
|
|
1757
2359
|
closeOn,
|
|
1758
2360
|
waitForHash: config.waitForHash ?? true,
|
|
1759
2361
|
hashTimeoutMs: config.hashTimeoutMs,
|
|
@@ -2197,9 +2799,10 @@ function BatchQueueWidget({ onSignAll }) {
|
|
|
2197
2799
|
|
|
2198
2800
|
// src/verify.ts
|
|
2199
2801
|
import { keccak256, toBytes } from "viem";
|
|
2200
|
-
var
|
|
2802
|
+
var ETHEREUM_MESSAGE_PREFIX = "Ethereum Signed Message:\n";
|
|
2803
|
+
var PASSKEY_MESSAGE_PREFIX = ETHEREUM_MESSAGE_PREFIX;
|
|
2201
2804
|
function hashMessage2(message) {
|
|
2202
|
-
const prefixed =
|
|
2805
|
+
const prefixed = ETHEREUM_MESSAGE_PREFIX + message.length.toString() + message;
|
|
2203
2806
|
return keccak256(toBytes(prefixed));
|
|
2204
2807
|
}
|
|
2205
2808
|
function verifyMessageHash(message, signedHash) {
|
|
@@ -2210,9 +2813,11 @@ function verifyMessageHash(message, signedHash) {
|
|
|
2210
2813
|
export {
|
|
2211
2814
|
BatchQueueProvider,
|
|
2212
2815
|
BatchQueueWidget,
|
|
2816
|
+
ETHEREUM_MESSAGE_PREFIX,
|
|
2213
2817
|
OneAuthClient,
|
|
2214
2818
|
PASSKEY_MESSAGE_PREFIX,
|
|
2215
2819
|
OneAuthClient as PasskeyProviderClient,
|
|
2820
|
+
createOneAuthProvider,
|
|
2216
2821
|
createPasskeyAccount,
|
|
2217
2822
|
createPasskeyProvider,
|
|
2218
2823
|
createPasskeyWalletClient,
|