@rhinestone/1auth 0.1.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +130 -0
- package/dist/{chunk-U7KZ4XMQ.mjs → chunk-X73ALCGW.mjs} +202 -36
- package/dist/chunk-X73ALCGW.mjs.map +1 -0
- package/dist/{client-B-HGKKaJ.d.mts → client-DKuPEx83.d.mts} +462 -27
- package/dist/{client-B-HGKKaJ.d.ts → client-DKuPEx83.d.ts} +462 -27
- package/dist/index.d.mts +19 -10
- package/dist/index.d.ts +19 -10
- package/dist/index.js +1417 -282
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1235 -266
- package/dist/index.mjs.map +1 -1
- package/dist/provider-CmJarV7y.d.mts +33 -0
- package/dist/provider-Dj5l4bWn.d.ts +33 -0
- package/dist/react.d.mts +2 -2
- package/dist/react.d.ts +2 -2
- package/dist/react.js +16 -9
- package/dist/react.js.map +1 -1
- package/dist/react.mjs +16 -9
- package/dist/react.mjs.map +1 -1
- package/dist/wagmi.d.mts +3 -3
- package/dist/wagmi.d.ts +3 -3
- package/dist/wagmi.js +193 -37
- package/dist/wagmi.js.map +1 -1
- package/dist/wagmi.mjs +1 -1
- package/package.json +19 -5
- package/dist/chunk-U7KZ4XMQ.mjs.map +0 -1
- package/dist/provider-CDIRlA4y.d.mts +0 -25
- package/dist/provider-Cy1StrOe.d.ts +0 -25
package/dist/index.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-X73ALCGW.mjs";
|
|
22
23
|
|
|
23
24
|
// src/client.ts
|
|
24
25
|
import { parseUnits, hashTypedData } from "viem";
|
|
@@ -26,7 +27,7 @@ var POPUP_WIDTH = 450;
|
|
|
26
27
|
var POPUP_HEIGHT = 600;
|
|
27
28
|
var DEFAULT_EMBED_WIDTH = "400px";
|
|
28
29
|
var DEFAULT_EMBED_HEIGHT = "500px";
|
|
29
|
-
var MODAL_WIDTH =
|
|
30
|
+
var MODAL_WIDTH = 340;
|
|
30
31
|
var DEFAULT_PROVIDER_URL = "https://passkey.1auth.box";
|
|
31
32
|
var OneAuthClient = class {
|
|
32
33
|
constructor(config) {
|
|
@@ -34,6 +35,12 @@ var OneAuthClient = class {
|
|
|
34
35
|
const dialogUrl = config.dialogUrl || providerUrl;
|
|
35
36
|
this.config = { ...config, providerUrl, dialogUrl };
|
|
36
37
|
this.theme = this.config.theme || {};
|
|
38
|
+
if (typeof document !== "undefined") {
|
|
39
|
+
this.injectPreconnect(providerUrl);
|
|
40
|
+
if (dialogUrl !== providerUrl) {
|
|
41
|
+
this.injectPreconnect(dialogUrl);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
37
44
|
}
|
|
38
45
|
/**
|
|
39
46
|
* Update the theme configuration at runtime
|
|
@@ -95,9 +102,7 @@ var OneAuthClient = class {
|
|
|
95
102
|
const response = await fetch(
|
|
96
103
|
`${this.config.providerUrl}/api/intent/status/${intentId}`,
|
|
97
104
|
{
|
|
98
|
-
headers: {
|
|
99
|
-
"x-client-id": this.config.clientId
|
|
100
|
-
}
|
|
105
|
+
headers: this.config.clientId ? { "x-client-id": this.config.clientId } : {}
|
|
101
106
|
}
|
|
102
107
|
);
|
|
103
108
|
if (response.ok) {
|
|
@@ -121,9 +126,11 @@ var OneAuthClient = class {
|
|
|
121
126
|
async authWithModal(options) {
|
|
122
127
|
const dialogUrl = this.getDialogUrl();
|
|
123
128
|
const params = new URLSearchParams({
|
|
124
|
-
clientId: this.config.clientId,
|
|
125
129
|
mode: "iframe"
|
|
126
130
|
});
|
|
131
|
+
if (this.config.clientId) {
|
|
132
|
+
params.set("clientId", this.config.clientId);
|
|
133
|
+
}
|
|
127
134
|
if (options?.username) {
|
|
128
135
|
params.set("username", options.username);
|
|
129
136
|
}
|
|
@@ -139,6 +146,182 @@ var OneAuthClient = class {
|
|
|
139
146
|
const { dialog, iframe, cleanup } = this.createModalDialog(url);
|
|
140
147
|
return this.waitForModalAuthResponse(dialog, iframe, cleanup);
|
|
141
148
|
}
|
|
149
|
+
/**
|
|
150
|
+
* Open the connect dialog (lightweight connection without passkey auth).
|
|
151
|
+
*
|
|
152
|
+
* This method shows a simple connection confirmation dialog that doesn't
|
|
153
|
+
* require a passkey signature. Users can optionally enable "auto-connect"
|
|
154
|
+
* to skip this dialog in the future.
|
|
155
|
+
*
|
|
156
|
+
* If the user has never connected before, this will return action: "switch"
|
|
157
|
+
* to indicate that the full auth modal should be opened instead.
|
|
158
|
+
*
|
|
159
|
+
* @example
|
|
160
|
+
* ```typescript
|
|
161
|
+
* const result = await client.connectWithModal();
|
|
162
|
+
*
|
|
163
|
+
* if (result.success) {
|
|
164
|
+
* console.log('Connected as:', result.username);
|
|
165
|
+
* } else if (result.action === 'switch') {
|
|
166
|
+
* // User needs to sign in first
|
|
167
|
+
* const authResult = await client.authWithModal();
|
|
168
|
+
* }
|
|
169
|
+
* ```
|
|
170
|
+
*/
|
|
171
|
+
async connectWithModal(options) {
|
|
172
|
+
const dialogUrl = this.getDialogUrl();
|
|
173
|
+
const params = new URLSearchParams({
|
|
174
|
+
mode: "iframe"
|
|
175
|
+
});
|
|
176
|
+
if (this.config.clientId) {
|
|
177
|
+
params.set("clientId", this.config.clientId);
|
|
178
|
+
}
|
|
179
|
+
const themeParams = this.getThemeParams(options?.theme);
|
|
180
|
+
if (themeParams) {
|
|
181
|
+
const themeParsed = new URLSearchParams(themeParams);
|
|
182
|
+
themeParsed.forEach((value, key) => params.set(key, value));
|
|
183
|
+
}
|
|
184
|
+
const url = `${dialogUrl}/dialog/connect?${params.toString()}`;
|
|
185
|
+
const { dialog, iframe, cleanup } = this.createModalDialog(url);
|
|
186
|
+
const ready = await this.waitForDialogReady(dialog, iframe, cleanup, {
|
|
187
|
+
mode: "iframe"
|
|
188
|
+
});
|
|
189
|
+
if (!ready) {
|
|
190
|
+
return {
|
|
191
|
+
success: false,
|
|
192
|
+
action: "cancel",
|
|
193
|
+
error: { code: "USER_CANCELLED", message: "Connection was cancelled" }
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
return this.waitForConnectResponse(dialog, iframe, cleanup);
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Check if a user has already granted consent for the requested fields.
|
|
200
|
+
* This is a read-only check — no dialog is shown.
|
|
201
|
+
*
|
|
202
|
+
* @example
|
|
203
|
+
* ```typescript
|
|
204
|
+
* const result = await client.checkConsent({
|
|
205
|
+
* username: "alice",
|
|
206
|
+
* fields: ["email"],
|
|
207
|
+
* });
|
|
208
|
+
* if (result.hasConsent) {
|
|
209
|
+
* console.log(result.data?.email);
|
|
210
|
+
* }
|
|
211
|
+
* ```
|
|
212
|
+
*/
|
|
213
|
+
async checkConsent(options) {
|
|
214
|
+
const clientId = options.clientId ?? this.config.clientId;
|
|
215
|
+
if (!clientId) {
|
|
216
|
+
return {
|
|
217
|
+
hasConsent: false
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
const username = options.username ?? options.accountAddress;
|
|
221
|
+
if (!username) {
|
|
222
|
+
return {
|
|
223
|
+
hasConsent: false
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
try {
|
|
227
|
+
const res = await fetch(`${this.config.providerUrl || "https://passkey.1auth.box"}/api/consent`, {
|
|
228
|
+
method: "POST",
|
|
229
|
+
headers: {
|
|
230
|
+
"Content-Type": "application/json",
|
|
231
|
+
...clientId ? { "x-client-id": clientId } : {}
|
|
232
|
+
},
|
|
233
|
+
body: JSON.stringify({
|
|
234
|
+
username,
|
|
235
|
+
requestedFields: options.fields,
|
|
236
|
+
clientId
|
|
237
|
+
})
|
|
238
|
+
});
|
|
239
|
+
if (!res.ok) {
|
|
240
|
+
return { hasConsent: false };
|
|
241
|
+
}
|
|
242
|
+
const data = await res.json();
|
|
243
|
+
return {
|
|
244
|
+
hasConsent: data.hasConsent ?? false,
|
|
245
|
+
data: data.data,
|
|
246
|
+
grantedAt: data.grantedAt
|
|
247
|
+
};
|
|
248
|
+
} catch {
|
|
249
|
+
return { hasConsent: false };
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Request consent from the user to share their data.
|
|
254
|
+
*
|
|
255
|
+
* First checks if consent was already granted (returns cached data immediately).
|
|
256
|
+
* If not, opens the consent dialog where the user can review and approve sharing.
|
|
257
|
+
*
|
|
258
|
+
* @example
|
|
259
|
+
* ```typescript
|
|
260
|
+
* const result = await client.requestConsent({
|
|
261
|
+
* username: "alice",
|
|
262
|
+
* fields: ["email", "deviceNames"],
|
|
263
|
+
* });
|
|
264
|
+
* if (result.success) {
|
|
265
|
+
* console.log(result.data?.email);
|
|
266
|
+
* console.log(result.cached); // true if no dialog was shown
|
|
267
|
+
* }
|
|
268
|
+
* ```
|
|
269
|
+
*/
|
|
270
|
+
async requestConsent(options) {
|
|
271
|
+
const clientId = options.clientId ?? this.config.clientId;
|
|
272
|
+
if (!clientId) {
|
|
273
|
+
return {
|
|
274
|
+
success: false,
|
|
275
|
+
error: { code: "INVALID_REQUEST", message: "clientId is required (set in config or options)" }
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
const username = options.username ?? options.accountAddress;
|
|
279
|
+
if (!username) {
|
|
280
|
+
return {
|
|
281
|
+
success: false,
|
|
282
|
+
error: { code: "INVALID_REQUEST", message: "username or accountAddress is required" }
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
if (!options.fields || options.fields.length === 0) {
|
|
286
|
+
return {
|
|
287
|
+
success: false,
|
|
288
|
+
error: { code: "INVALID_REQUEST", message: "At least one field is required" }
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
const existing = await this.checkConsent({ ...options, clientId });
|
|
292
|
+
if (existing.hasConsent && existing.data) {
|
|
293
|
+
return {
|
|
294
|
+
success: true,
|
|
295
|
+
data: existing.data,
|
|
296
|
+
grantedAt: existing.grantedAt,
|
|
297
|
+
cached: true
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
const dialogUrl = this.getDialogUrl();
|
|
301
|
+
const params = new URLSearchParams({
|
|
302
|
+
mode: "iframe",
|
|
303
|
+
username,
|
|
304
|
+
clientId,
|
|
305
|
+
fields: options.fields.join(",")
|
|
306
|
+
});
|
|
307
|
+
const themeParams = this.getThemeParams(options.theme);
|
|
308
|
+
if (themeParams) {
|
|
309
|
+
const themeParsed = new URLSearchParams(themeParams);
|
|
310
|
+
themeParsed.forEach((value, key) => params.set(key, value));
|
|
311
|
+
}
|
|
312
|
+
const url = `${dialogUrl}/dialog/consent?${params.toString()}`;
|
|
313
|
+
const { dialog, iframe, cleanup } = this.createModalDialog(url);
|
|
314
|
+
const ready = await this.waitForDialogReady(dialog, iframe, cleanup, {
|
|
315
|
+
mode: "iframe"
|
|
316
|
+
});
|
|
317
|
+
if (!ready) {
|
|
318
|
+
return {
|
|
319
|
+
success: false,
|
|
320
|
+
error: { code: "USER_CANCELLED", message: "User closed the dialog" }
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
return this.waitForConsentResponse(dialog, iframe, cleanup);
|
|
324
|
+
}
|
|
142
325
|
/**
|
|
143
326
|
* Authenticate a user with an optional challenge to sign.
|
|
144
327
|
*
|
|
@@ -174,9 +357,11 @@ var OneAuthClient = class {
|
|
|
174
357
|
async authenticate(options) {
|
|
175
358
|
const dialogUrl = this.getDialogUrl();
|
|
176
359
|
const params = new URLSearchParams({
|
|
177
|
-
clientId: this.config.clientId,
|
|
178
360
|
mode: "iframe"
|
|
179
361
|
});
|
|
362
|
+
if (this.config.clientId) {
|
|
363
|
+
params.set("clientId", this.config.clientId);
|
|
364
|
+
}
|
|
180
365
|
if (options?.challenge) {
|
|
181
366
|
params.set("challenge", options.challenge);
|
|
182
367
|
}
|
|
@@ -190,28 +375,30 @@ var OneAuthClient = class {
|
|
|
190
375
|
return this.waitForAuthenticateResponse(dialog, iframe, cleanup);
|
|
191
376
|
}
|
|
192
377
|
/**
|
|
193
|
-
* Show signing in a modal overlay (
|
|
378
|
+
* Show signing in a modal overlay (iframe dialog)
|
|
194
379
|
*/
|
|
195
380
|
async signWithModal(options) {
|
|
196
381
|
const dialogUrl = this.getDialogUrl();
|
|
197
382
|
const themeParams = this.getThemeParams(options?.theme);
|
|
198
383
|
const signingUrl = `${dialogUrl}/dialog/sign?mode=iframe${themeParams ? `&${themeParams}` : ""}`;
|
|
199
384
|
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
|
-
};
|
|
385
|
+
const ready = await this.waitForDialogReady(dialog, iframe, cleanup, {
|
|
386
|
+
mode: "iframe",
|
|
387
|
+
challenge: options.challenge,
|
|
388
|
+
username: options.username,
|
|
389
|
+
description: options.description,
|
|
390
|
+
transaction: options.transaction,
|
|
391
|
+
metadata: options.metadata
|
|
214
392
|
});
|
|
393
|
+
if (!ready) {
|
|
394
|
+
return {
|
|
395
|
+
success: false,
|
|
396
|
+
error: {
|
|
397
|
+
code: "USER_REJECTED",
|
|
398
|
+
message: "User closed the dialog"
|
|
399
|
+
}
|
|
400
|
+
};
|
|
401
|
+
}
|
|
215
402
|
return this.waitForSigningResponse(dialog, iframe, cleanup);
|
|
216
403
|
}
|
|
217
404
|
/**
|
|
@@ -262,7 +449,8 @@ var OneAuthClient = class {
|
|
|
262
449
|
}
|
|
263
450
|
};
|
|
264
451
|
}
|
|
265
|
-
|
|
452
|
+
const accountAddress = signedIntent?.accountAddress || options.accountAddress;
|
|
453
|
+
if (!username && !accountAddress) {
|
|
266
454
|
return {
|
|
267
455
|
success: false,
|
|
268
456
|
intentId: "",
|
|
@@ -284,158 +472,264 @@ var OneAuthClient = class {
|
|
|
284
472
|
}
|
|
285
473
|
};
|
|
286
474
|
}
|
|
475
|
+
const serializedTokenRequests = options.tokenRequests?.map((r) => ({
|
|
476
|
+
token: r.token,
|
|
477
|
+
amount: r.amount.toString()
|
|
478
|
+
}));
|
|
287
479
|
let prepareResponse;
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
480
|
+
const requestBody = signedIntent || {
|
|
481
|
+
username: options.username,
|
|
482
|
+
accountAddress: options.accountAddress,
|
|
483
|
+
targetChain: options.targetChain,
|
|
484
|
+
calls: options.calls,
|
|
485
|
+
tokenRequests: serializedTokenRequests,
|
|
486
|
+
sourceAssets: options.sourceAssets,
|
|
487
|
+
sourceChainId: options.sourceChainId,
|
|
488
|
+
...this.config.clientId && { clientId: this.config.clientId }
|
|
489
|
+
};
|
|
490
|
+
const dialogUrl = this.getDialogUrl();
|
|
491
|
+
const themeParams = this.getThemeParams();
|
|
492
|
+
const signingUrl = `${dialogUrl}/dialog/sign?mode=iframe${themeParams ? `&${themeParams}` : ""}`;
|
|
493
|
+
const { dialog, iframe, cleanup } = this.createModalDialog(signingUrl);
|
|
494
|
+
const [prepareResult, dialogResult] = await Promise.all([
|
|
495
|
+
this.prepareIntent(requestBody),
|
|
496
|
+
this.waitForDialogReadyDeferred(dialog, iframe, cleanup)
|
|
497
|
+
]);
|
|
498
|
+
if (!dialogResult.ready) {
|
|
499
|
+
return {
|
|
500
|
+
success: false,
|
|
501
|
+
intentId: "",
|
|
502
|
+
status: "failed",
|
|
503
|
+
error: { code: "USER_CANCELLED", message: "User closed the dialog" }
|
|
296
504
|
};
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
},
|
|
302
|
-
body: JSON.stringify(requestBody)
|
|
303
|
-
});
|
|
304
|
-
if (!response.ok) {
|
|
305
|
-
const errorData = await response.json().catch(() => ({}));
|
|
306
|
-
const errorMessage = errorData.error || "Failed to prepare intent";
|
|
307
|
-
if (errorMessage.includes("User not found")) {
|
|
308
|
-
localStorage.removeItem("1auth-user");
|
|
309
|
-
}
|
|
310
|
-
return {
|
|
311
|
-
success: false,
|
|
312
|
-
intentId: "",
|
|
313
|
-
status: "failed",
|
|
314
|
-
error: {
|
|
315
|
-
code: errorMessage.includes("User not found") ? "USER_NOT_FOUND" : "PREPARE_FAILED",
|
|
316
|
-
message: errorMessage
|
|
317
|
-
}
|
|
318
|
-
};
|
|
319
|
-
}
|
|
320
|
-
prepareResponse = await response.json();
|
|
321
|
-
} catch (error) {
|
|
505
|
+
}
|
|
506
|
+
if (!prepareResult.success) {
|
|
507
|
+
this.sendPrepareError(iframe, prepareResult.error.message);
|
|
508
|
+
await this.waitForDialogClose(dialog, cleanup);
|
|
322
509
|
return {
|
|
323
510
|
success: false,
|
|
324
511
|
intentId: "",
|
|
325
512
|
status: "failed",
|
|
326
|
-
error:
|
|
327
|
-
code: "NETWORK_ERROR",
|
|
328
|
-
message: error instanceof Error ? error.message : "Network error"
|
|
329
|
-
}
|
|
513
|
+
error: prepareResult.error
|
|
330
514
|
};
|
|
331
515
|
}
|
|
332
|
-
|
|
333
|
-
const signingUrl = `${dialogUrl}/dialog/sign?mode=iframe`;
|
|
334
|
-
const { dialog, iframe, cleanup } = this.createModalDialog(signingUrl);
|
|
516
|
+
prepareResponse = prepareResult.data;
|
|
335
517
|
const dialogOrigin = this.getDialogOrigin();
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
518
|
+
const initPayload = {
|
|
519
|
+
mode: "iframe",
|
|
520
|
+
calls,
|
|
521
|
+
chainId: targetChain,
|
|
522
|
+
transaction: prepareResponse.transaction,
|
|
523
|
+
challenge: prepareResponse.challenge,
|
|
524
|
+
username,
|
|
525
|
+
accountAddress: prepareResponse.accountAddress,
|
|
526
|
+
originMessages: prepareResponse.originMessages,
|
|
527
|
+
tokenRequests: serializedTokenRequests,
|
|
528
|
+
expiresAt: prepareResponse.expiresAt,
|
|
529
|
+
userId: prepareResponse.userId,
|
|
530
|
+
intentOp: prepareResponse.intentOp,
|
|
531
|
+
digestResult: prepareResponse.digestResult,
|
|
532
|
+
tier: prepareResult.tier
|
|
533
|
+
};
|
|
534
|
+
dialogResult.sendInit(initPayload);
|
|
535
|
+
const handleReReady = (event) => {
|
|
536
|
+
if (event.origin !== dialogOrigin) return;
|
|
537
|
+
if (event.data?.type === "PASSKEY_READY") {
|
|
538
|
+
iframe.contentWindow?.postMessage(
|
|
539
|
+
{ type: "PASSKEY_INIT", ...initPayload },
|
|
540
|
+
dialogOrigin
|
|
541
|
+
);
|
|
542
|
+
}
|
|
543
|
+
};
|
|
544
|
+
window.addEventListener("message", handleReReady);
|
|
545
|
+
const signingResult = await this.waitForSigningWithRefresh(
|
|
546
|
+
dialog,
|
|
547
|
+
iframe,
|
|
548
|
+
cleanup,
|
|
549
|
+
dialogOrigin,
|
|
550
|
+
// Refresh callback - called when dialog requests a quote refresh
|
|
551
|
+
async () => {
|
|
552
|
+
console.log("[SDK] Dialog requested quote refresh, re-preparing intent");
|
|
553
|
+
try {
|
|
554
|
+
const refreshResponse = await fetch(`${this.config.providerUrl}/api/intent/prepare`, {
|
|
555
|
+
method: "POST",
|
|
556
|
+
headers: { "Content-Type": "application/json" },
|
|
557
|
+
body: JSON.stringify(requestBody),
|
|
558
|
+
credentials: "include"
|
|
559
|
+
});
|
|
560
|
+
if (!refreshResponse.ok) {
|
|
561
|
+
console.error("[SDK] Quote refresh failed:", await refreshResponse.text());
|
|
562
|
+
return null;
|
|
563
|
+
}
|
|
564
|
+
const refreshedData = await refreshResponse.json();
|
|
565
|
+
prepareResponse = refreshedData;
|
|
566
|
+
return {
|
|
567
|
+
intentOp: refreshedData.intentOp,
|
|
568
|
+
expiresAt: refreshedData.expiresAt,
|
|
569
|
+
challenge: refreshedData.challenge,
|
|
570
|
+
originMessages: refreshedData.originMessages,
|
|
571
|
+
transaction: refreshedData.transaction,
|
|
572
|
+
digestResult: refreshedData.digestResult
|
|
573
|
+
};
|
|
574
|
+
} catch (error) {
|
|
575
|
+
console.error("[SDK] Quote refresh error:", error);
|
|
576
|
+
return null;
|
|
353
577
|
}
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
const signingResult = await this.waitForSigningResponse(dialog, iframe, cleanup);
|
|
578
|
+
}
|
|
579
|
+
);
|
|
580
|
+
window.removeEventListener("message", handleReReady);
|
|
358
581
|
if (!signingResult.success) {
|
|
359
582
|
return {
|
|
360
583
|
success: false,
|
|
361
|
-
intentId:
|
|
584
|
+
intentId: "",
|
|
585
|
+
// No intentId yet - signing was cancelled before execute
|
|
362
586
|
status: "failed",
|
|
363
587
|
error: signingResult.error
|
|
364
588
|
};
|
|
365
589
|
}
|
|
590
|
+
const dialogExecutedIntent = "intentId" in signingResult && signingResult.intentId;
|
|
366
591
|
let executeResponse;
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
592
|
+
if (dialogExecutedIntent) {
|
|
593
|
+
executeResponse = {
|
|
594
|
+
success: true,
|
|
595
|
+
intentId: signingResult.intentId,
|
|
596
|
+
status: "pending"
|
|
597
|
+
};
|
|
598
|
+
} else {
|
|
599
|
+
try {
|
|
600
|
+
const response = await fetch(`${this.config.providerUrl}/api/intent/execute`, {
|
|
601
|
+
method: "POST",
|
|
602
|
+
headers: {
|
|
603
|
+
"Content-Type": "application/json"
|
|
604
|
+
},
|
|
605
|
+
body: JSON.stringify({
|
|
606
|
+
// Data from prepare response (no intentId yet - created on execute)
|
|
607
|
+
intentOp: prepareResponse.intentOp,
|
|
608
|
+
userId: prepareResponse.userId,
|
|
609
|
+
targetChain: prepareResponse.targetChain,
|
|
610
|
+
calls: prepareResponse.calls,
|
|
611
|
+
expiresAt: prepareResponse.expiresAt,
|
|
612
|
+
digestResult: prepareResponse.digestResult,
|
|
613
|
+
// Signature from dialog
|
|
614
|
+
signature: signingResult.signature,
|
|
615
|
+
passkey: signingResult.passkey
|
|
616
|
+
// Include passkey info for signature encoding
|
|
617
|
+
})
|
|
618
|
+
});
|
|
619
|
+
if (!response.ok) {
|
|
620
|
+
const errorData = await response.json().catch(() => ({}));
|
|
621
|
+
this.sendTransactionStatus(iframe, "failed");
|
|
622
|
+
await this.waitForDialogClose(dialog, cleanup);
|
|
623
|
+
return {
|
|
624
|
+
success: false,
|
|
625
|
+
intentId: "",
|
|
626
|
+
// No intentId - execute failed before creation
|
|
627
|
+
status: "failed",
|
|
628
|
+
error: {
|
|
629
|
+
code: "EXECUTE_FAILED",
|
|
630
|
+
message: errorData.error || "Failed to execute intent"
|
|
631
|
+
}
|
|
632
|
+
};
|
|
633
|
+
}
|
|
634
|
+
executeResponse = await response.json();
|
|
635
|
+
} catch (error) {
|
|
382
636
|
this.sendTransactionStatus(iframe, "failed");
|
|
383
637
|
await this.waitForDialogClose(dialog, cleanup);
|
|
384
638
|
return {
|
|
385
639
|
success: false,
|
|
386
|
-
intentId:
|
|
640
|
+
intentId: "",
|
|
641
|
+
// No intentId - network error before creation
|
|
387
642
|
status: "failed",
|
|
388
643
|
error: {
|
|
389
|
-
code: "
|
|
390
|
-
message:
|
|
644
|
+
code: "NETWORK_ERROR",
|
|
645
|
+
message: error instanceof Error ? error.message : "Network error"
|
|
391
646
|
}
|
|
392
647
|
};
|
|
393
648
|
}
|
|
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
649
|
}
|
|
408
|
-
const closeOn = options.closeOn || "preconfirmed";
|
|
409
|
-
const acceptPreconfirmations = closeOn !== "completed";
|
|
410
650
|
let finalStatus = executeResponse.status;
|
|
411
651
|
let finalTxHash = executeResponse.transactionHash;
|
|
412
652
|
if (finalStatus === "pending") {
|
|
413
|
-
this.sendTransactionStatus(iframe, "
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
653
|
+
this.sendTransactionStatus(iframe, "pending");
|
|
654
|
+
let userClosedEarly = false;
|
|
655
|
+
const dialogOrigin2 = this.getDialogOrigin();
|
|
656
|
+
const earlyCloseHandler = (event) => {
|
|
657
|
+
if (event.origin !== dialogOrigin2) return;
|
|
658
|
+
if (event.data?.type === "PASSKEY_CLOSE") {
|
|
659
|
+
userClosedEarly = true;
|
|
660
|
+
cleanup();
|
|
661
|
+
}
|
|
662
|
+
};
|
|
663
|
+
window.addEventListener("message", earlyCloseHandler);
|
|
664
|
+
const maxAttempts = 120;
|
|
665
|
+
const pollIntervalMs = 1500;
|
|
666
|
+
let lastStatus = "pending";
|
|
667
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
668
|
+
if (userClosedEarly) break;
|
|
669
|
+
try {
|
|
670
|
+
const statusResponse = await fetch(
|
|
671
|
+
`${this.config.providerUrl}/api/intent/status/${executeResponse.intentId}`,
|
|
672
|
+
{
|
|
673
|
+
method: "GET",
|
|
674
|
+
headers: this.config.clientId ? { "x-client-id": this.config.clientId } : {}
|
|
675
|
+
}
|
|
676
|
+
);
|
|
677
|
+
if (statusResponse.ok) {
|
|
678
|
+
const statusResult = await statusResponse.json();
|
|
679
|
+
finalStatus = statusResult.status;
|
|
680
|
+
finalTxHash = statusResult.transactionHash;
|
|
681
|
+
if (finalStatus !== lastStatus) {
|
|
682
|
+
this.sendTransactionStatus(iframe, finalStatus, finalTxHash);
|
|
683
|
+
lastStatus = finalStatus;
|
|
684
|
+
}
|
|
685
|
+
const closeOn2 = options.closeOn || "preconfirmed";
|
|
686
|
+
const successStatuses2 = {
|
|
687
|
+
claimed: ["claimed", "preconfirmed", "filled", "completed"],
|
|
688
|
+
preconfirmed: ["preconfirmed", "filled", "completed"],
|
|
689
|
+
filled: ["filled", "completed"],
|
|
690
|
+
completed: ["completed"]
|
|
691
|
+
};
|
|
692
|
+
const isTerminal = finalStatus === "failed" || finalStatus === "expired";
|
|
693
|
+
const isSuccess = successStatuses2[closeOn2]?.includes(finalStatus) ?? false;
|
|
694
|
+
if (isTerminal || isSuccess) {
|
|
695
|
+
break;
|
|
420
696
|
}
|
|
421
697
|
}
|
|
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());
|
|
698
|
+
} catch (pollError) {
|
|
699
|
+
console.error("Failed to poll intent status:", pollError);
|
|
429
700
|
}
|
|
430
|
-
|
|
431
|
-
|
|
701
|
+
await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
|
|
702
|
+
}
|
|
703
|
+
window.removeEventListener("message", earlyCloseHandler);
|
|
704
|
+
if (userClosedEarly) {
|
|
705
|
+
cleanup();
|
|
706
|
+
return {
|
|
707
|
+
success: false,
|
|
708
|
+
intentId: executeResponse.intentId,
|
|
709
|
+
status: finalStatus,
|
|
710
|
+
transactionHash: finalTxHash,
|
|
711
|
+
operationId: executeResponse.operationId,
|
|
712
|
+
error: {
|
|
713
|
+
code: "USER_CANCELLED",
|
|
714
|
+
message: "User closed the dialog"
|
|
715
|
+
}
|
|
716
|
+
};
|
|
432
717
|
}
|
|
433
718
|
}
|
|
434
|
-
const
|
|
719
|
+
const closeOn = options.closeOn || "preconfirmed";
|
|
720
|
+
const successStatuses = {
|
|
721
|
+
claimed: ["claimed", "preconfirmed", "filled", "completed"],
|
|
722
|
+
preconfirmed: ["preconfirmed", "filled", "completed"],
|
|
723
|
+
filled: ["filled", "completed"],
|
|
724
|
+
completed: ["completed"]
|
|
725
|
+
};
|
|
726
|
+
const isSuccessStatus = successStatuses[closeOn]?.includes(finalStatus) ?? false;
|
|
727
|
+
const displayStatus = isSuccessStatus ? "confirmed" : finalStatus;
|
|
728
|
+
const closePromise = this.waitForDialogClose(dialog, cleanup);
|
|
435
729
|
this.sendTransactionStatus(iframe, displayStatus, finalTxHash);
|
|
436
|
-
await
|
|
730
|
+
await closePromise;
|
|
437
731
|
if (options.waitForHash && !finalTxHash) {
|
|
438
|
-
const hash = await this.waitForTransactionHash(
|
|
732
|
+
const hash = await this.waitForTransactionHash(executeResponse.intentId, {
|
|
439
733
|
timeoutMs: options.hashTimeoutMs,
|
|
440
734
|
intervalMs: options.hashIntervalMs
|
|
441
735
|
});
|
|
@@ -446,7 +740,7 @@ var OneAuthClient = class {
|
|
|
446
740
|
finalStatus = "failed";
|
|
447
741
|
return {
|
|
448
742
|
success: false,
|
|
449
|
-
intentId:
|
|
743
|
+
intentId: executeResponse.intentId,
|
|
450
744
|
status: finalStatus,
|
|
451
745
|
transactionHash: finalTxHash,
|
|
452
746
|
operationId: executeResponse.operationId,
|
|
@@ -458,14 +752,223 @@ var OneAuthClient = class {
|
|
|
458
752
|
}
|
|
459
753
|
}
|
|
460
754
|
return {
|
|
461
|
-
success:
|
|
462
|
-
intentId:
|
|
755
|
+
success: isSuccessStatus,
|
|
756
|
+
intentId: executeResponse.intentId,
|
|
463
757
|
status: finalStatus,
|
|
464
758
|
transactionHash: finalTxHash,
|
|
465
759
|
operationId: executeResponse.operationId,
|
|
466
760
|
error: executeResponse.error
|
|
467
761
|
};
|
|
468
762
|
}
|
|
763
|
+
/**
|
|
764
|
+
* Send a batch of intents for multi-chain execution with a single passkey tap.
|
|
765
|
+
*
|
|
766
|
+
* This method prepares multiple intents, shows a paginated review,
|
|
767
|
+
* and signs all intents with a single passkey tap via a shared merkle tree.
|
|
768
|
+
*
|
|
769
|
+
* @example
|
|
770
|
+
* ```typescript
|
|
771
|
+
* const result = await client.sendBatchIntent({
|
|
772
|
+
* username: 'alice',
|
|
773
|
+
* intents: [
|
|
774
|
+
* {
|
|
775
|
+
* targetChain: 8453, // Base
|
|
776
|
+
* calls: [{ to: '0x...', data: '0x...', label: 'Swap on Base' }],
|
|
777
|
+
* },
|
|
778
|
+
* {
|
|
779
|
+
* targetChain: 42161, // Arbitrum
|
|
780
|
+
* calls: [{ to: '0x...', data: '0x...', label: 'Mint on Arbitrum' }],
|
|
781
|
+
* },
|
|
782
|
+
* ],
|
|
783
|
+
* });
|
|
784
|
+
*
|
|
785
|
+
* if (result.success) {
|
|
786
|
+
* console.log(`${result.successCount} intents submitted`);
|
|
787
|
+
* }
|
|
788
|
+
* ```
|
|
789
|
+
*/
|
|
790
|
+
async sendBatchIntent(options) {
|
|
791
|
+
if (!options.username && !options.accountAddress) {
|
|
792
|
+
return {
|
|
793
|
+
success: false,
|
|
794
|
+
results: [],
|
|
795
|
+
successCount: 0,
|
|
796
|
+
failureCount: 0
|
|
797
|
+
};
|
|
798
|
+
}
|
|
799
|
+
if (!options.intents?.length) {
|
|
800
|
+
return {
|
|
801
|
+
success: false,
|
|
802
|
+
results: [],
|
|
803
|
+
successCount: 0,
|
|
804
|
+
failureCount: 0
|
|
805
|
+
};
|
|
806
|
+
}
|
|
807
|
+
const serializedIntents = options.intents.map((intent) => ({
|
|
808
|
+
targetChain: intent.targetChain,
|
|
809
|
+
calls: intent.calls,
|
|
810
|
+
tokenRequests: intent.tokenRequests?.map((r) => ({
|
|
811
|
+
token: r.token,
|
|
812
|
+
amount: r.amount.toString()
|
|
813
|
+
})),
|
|
814
|
+
sourceAssets: intent.sourceAssets,
|
|
815
|
+
sourceChainId: intent.sourceChainId,
|
|
816
|
+
moduleInstall: intent.moduleInstall
|
|
817
|
+
}));
|
|
818
|
+
const requestBody = {
|
|
819
|
+
...options.username && { username: options.username },
|
|
820
|
+
...options.accountAddress && { accountAddress: options.accountAddress },
|
|
821
|
+
intents: serializedIntents,
|
|
822
|
+
...this.config.clientId && { clientId: this.config.clientId }
|
|
823
|
+
};
|
|
824
|
+
const dialogUrl = this.getDialogUrl();
|
|
825
|
+
const themeParams = this.getThemeParams();
|
|
826
|
+
const signingUrl = `${dialogUrl}/dialog/sign?mode=iframe${themeParams ? `&${themeParams}` : ""}`;
|
|
827
|
+
const { dialog, iframe, cleanup } = this.createModalDialog(signingUrl);
|
|
828
|
+
const [prepareResult, dialogResult] = await Promise.all([
|
|
829
|
+
this.prepareBatchIntent(requestBody),
|
|
830
|
+
this.waitForDialogReadyDeferred(dialog, iframe, cleanup)
|
|
831
|
+
]);
|
|
832
|
+
if (!dialogResult.ready) {
|
|
833
|
+
return {
|
|
834
|
+
success: false,
|
|
835
|
+
results: [],
|
|
836
|
+
successCount: 0,
|
|
837
|
+
failureCount: 0
|
|
838
|
+
};
|
|
839
|
+
}
|
|
840
|
+
if (!prepareResult.success) {
|
|
841
|
+
const failedIntents = prepareResult.failedIntents;
|
|
842
|
+
const failureResults = failedIntents?.map((f) => ({
|
|
843
|
+
index: f.index,
|
|
844
|
+
success: false,
|
|
845
|
+
intentId: "",
|
|
846
|
+
status: "failed",
|
|
847
|
+
error: { message: f.error, code: "PREPARE_FAILED" }
|
|
848
|
+
})) ?? [];
|
|
849
|
+
this.sendPrepareError(iframe, prepareResult.error);
|
|
850
|
+
await this.waitForDialogClose(dialog, cleanup);
|
|
851
|
+
return {
|
|
852
|
+
success: false,
|
|
853
|
+
results: failureResults,
|
|
854
|
+
successCount: 0,
|
|
855
|
+
failureCount: failureResults.length,
|
|
856
|
+
error: prepareResult.error
|
|
857
|
+
};
|
|
858
|
+
}
|
|
859
|
+
let prepareResponse = prepareResult.data;
|
|
860
|
+
const dialogOrigin = this.getDialogOrigin();
|
|
861
|
+
const batchInitPayload = {
|
|
862
|
+
mode: "iframe",
|
|
863
|
+
batchMode: true,
|
|
864
|
+
batchIntents: prepareResponse.intents,
|
|
865
|
+
batchFailedIntents: prepareResponse.failedIntents,
|
|
866
|
+
challenge: prepareResponse.challenge,
|
|
867
|
+
username: options.username,
|
|
868
|
+
accountAddress: prepareResponse.accountAddress,
|
|
869
|
+
userId: prepareResponse.userId,
|
|
870
|
+
expiresAt: prepareResponse.expiresAt,
|
|
871
|
+
tier: prepareResult.tier
|
|
872
|
+
};
|
|
873
|
+
dialogResult.sendInit(batchInitPayload);
|
|
874
|
+
const handleBatchReReady = (event) => {
|
|
875
|
+
if (event.origin !== dialogOrigin) return;
|
|
876
|
+
if (event.data?.type === "PASSKEY_READY") {
|
|
877
|
+
iframe.contentWindow?.postMessage(
|
|
878
|
+
{ type: "PASSKEY_INIT", ...batchInitPayload },
|
|
879
|
+
dialogOrigin
|
|
880
|
+
);
|
|
881
|
+
}
|
|
882
|
+
};
|
|
883
|
+
window.addEventListener("message", handleBatchReReady);
|
|
884
|
+
const batchResult = await new Promise((resolve) => {
|
|
885
|
+
const handleMessage = async (event) => {
|
|
886
|
+
if (event.origin !== dialogOrigin) return;
|
|
887
|
+
const message = event.data;
|
|
888
|
+
if (message?.type === "PASSKEY_REFRESH_QUOTE") {
|
|
889
|
+
console.log("[SDK] Batch dialog requested quote refresh, re-preparing all intents");
|
|
890
|
+
try {
|
|
891
|
+
const refreshResponse = await fetch(`${this.config.providerUrl}/api/intent/batch-prepare`, {
|
|
892
|
+
method: "POST",
|
|
893
|
+
headers: { "Content-Type": "application/json" },
|
|
894
|
+
body: JSON.stringify(requestBody)
|
|
895
|
+
});
|
|
896
|
+
if (refreshResponse.ok) {
|
|
897
|
+
const refreshed = await refreshResponse.json();
|
|
898
|
+
prepareResponse = refreshed;
|
|
899
|
+
iframe.contentWindow?.postMessage({
|
|
900
|
+
type: "PASSKEY_REFRESH_COMPLETE",
|
|
901
|
+
batchIntents: refreshed.intents,
|
|
902
|
+
challenge: refreshed.challenge,
|
|
903
|
+
expiresAt: refreshed.expiresAt
|
|
904
|
+
}, dialogOrigin);
|
|
905
|
+
} else {
|
|
906
|
+
iframe.contentWindow?.postMessage({
|
|
907
|
+
type: "PASSKEY_REFRESH_ERROR",
|
|
908
|
+
error: "Failed to refresh batch quotes"
|
|
909
|
+
}, dialogOrigin);
|
|
910
|
+
}
|
|
911
|
+
} catch {
|
|
912
|
+
iframe.contentWindow?.postMessage({
|
|
913
|
+
type: "PASSKEY_REFRESH_ERROR",
|
|
914
|
+
error: "Failed to refresh batch quotes"
|
|
915
|
+
}, dialogOrigin);
|
|
916
|
+
}
|
|
917
|
+
return;
|
|
918
|
+
}
|
|
919
|
+
if (message?.type === "PASSKEY_SIGNING_RESULT") {
|
|
920
|
+
window.removeEventListener("message", handleMessage);
|
|
921
|
+
if (message.success && message.data?.batchResults) {
|
|
922
|
+
const rawResults = message.data.batchResults;
|
|
923
|
+
const results = rawResults.map((r) => ({
|
|
924
|
+
index: r.index,
|
|
925
|
+
success: r.success ?? r.status !== "FAILED",
|
|
926
|
+
intentId: r.intentId || r.operationId || "",
|
|
927
|
+
status: r.status === "FAILED" ? "failed" : "pending",
|
|
928
|
+
error: r.error ? { code: "EXECUTE_FAILED", message: r.error } : void 0
|
|
929
|
+
}));
|
|
930
|
+
const prepareFailures = (prepareResponse.failedIntents ?? []).map((f) => ({
|
|
931
|
+
index: f.index,
|
|
932
|
+
success: false,
|
|
933
|
+
intentId: "",
|
|
934
|
+
status: "failed",
|
|
935
|
+
error: { code: "PREPARE_FAILED", message: f.error }
|
|
936
|
+
}));
|
|
937
|
+
const allResults = [...results, ...prepareFailures].sort((a, b) => a.index - b.index);
|
|
938
|
+
const successCount = allResults.filter((r) => r.success).length;
|
|
939
|
+
await this.waitForDialogClose(dialog, cleanup);
|
|
940
|
+
resolve({
|
|
941
|
+
success: successCount === allResults.length,
|
|
942
|
+
results: allResults,
|
|
943
|
+
successCount,
|
|
944
|
+
failureCount: allResults.length - successCount
|
|
945
|
+
});
|
|
946
|
+
} else {
|
|
947
|
+
cleanup();
|
|
948
|
+
resolve({
|
|
949
|
+
success: false,
|
|
950
|
+
results: [],
|
|
951
|
+
successCount: 0,
|
|
952
|
+
failureCount: 0
|
|
953
|
+
});
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
if (message?.type === "PASSKEY_CLOSE") {
|
|
957
|
+
window.removeEventListener("message", handleMessage);
|
|
958
|
+
cleanup();
|
|
959
|
+
resolve({
|
|
960
|
+
success: false,
|
|
961
|
+
results: [],
|
|
962
|
+
successCount: 0,
|
|
963
|
+
failureCount: 0
|
|
964
|
+
});
|
|
965
|
+
}
|
|
966
|
+
};
|
|
967
|
+
window.addEventListener("message", handleMessage);
|
|
968
|
+
});
|
|
969
|
+
window.removeEventListener("message", handleBatchReReady);
|
|
970
|
+
return batchResult;
|
|
971
|
+
}
|
|
469
972
|
/**
|
|
470
973
|
* Send transaction status to the dialog iframe
|
|
471
974
|
*/
|
|
@@ -542,7 +1045,77 @@ var OneAuthClient = class {
|
|
|
542
1045
|
const payload = message?.data;
|
|
543
1046
|
if (message?.type === "PASSKEY_SIGNING_RESULT") {
|
|
544
1047
|
window.removeEventListener("message", handleMessage);
|
|
545
|
-
if (message.success && payload?.
|
|
1048
|
+
if (message.success && payload?.intentId) {
|
|
1049
|
+
resolve({
|
|
1050
|
+
success: true,
|
|
1051
|
+
intentId: payload.intentId
|
|
1052
|
+
});
|
|
1053
|
+
} else if (message.success && payload?.signature) {
|
|
1054
|
+
resolve({
|
|
1055
|
+
success: true,
|
|
1056
|
+
signature: payload.signature,
|
|
1057
|
+
passkey: payload.passkey,
|
|
1058
|
+
signedHash: payload.signedHash
|
|
1059
|
+
});
|
|
1060
|
+
} else {
|
|
1061
|
+
resolve({
|
|
1062
|
+
success: false,
|
|
1063
|
+
error: message.error || {
|
|
1064
|
+
code: "SIGNING_FAILED",
|
|
1065
|
+
message: "Signing failed"
|
|
1066
|
+
}
|
|
1067
|
+
});
|
|
1068
|
+
}
|
|
1069
|
+
} else if (message?.type === "PASSKEY_CLOSE") {
|
|
1070
|
+
window.removeEventListener("message", handleMessage);
|
|
1071
|
+
cleanup();
|
|
1072
|
+
resolve({
|
|
1073
|
+
success: false,
|
|
1074
|
+
error: {
|
|
1075
|
+
code: "USER_REJECTED",
|
|
1076
|
+
message: "User closed the dialog"
|
|
1077
|
+
}
|
|
1078
|
+
});
|
|
1079
|
+
}
|
|
1080
|
+
};
|
|
1081
|
+
window.addEventListener("message", handleMessage);
|
|
1082
|
+
});
|
|
1083
|
+
}
|
|
1084
|
+
/**
|
|
1085
|
+
* Wait for signing result with auto-refresh support
|
|
1086
|
+
* This method handles both signing results and quote refresh requests from the dialog
|
|
1087
|
+
*/
|
|
1088
|
+
waitForSigningWithRefresh(dialog, iframe, cleanup, dialogOrigin, onRefresh) {
|
|
1089
|
+
console.log("[SDK] waitForSigningWithRefresh, expecting origin:", dialogOrigin);
|
|
1090
|
+
return new Promise((resolve) => {
|
|
1091
|
+
const handleMessage = async (event) => {
|
|
1092
|
+
if (event.origin !== dialogOrigin) return;
|
|
1093
|
+
const message = event.data;
|
|
1094
|
+
if (message?.type === "PASSKEY_REFRESH_QUOTE") {
|
|
1095
|
+
console.log("[SDK] Received quote refresh request from dialog");
|
|
1096
|
+
const refreshedData = await onRefresh();
|
|
1097
|
+
if (refreshedData) {
|
|
1098
|
+
iframe.contentWindow?.postMessage({
|
|
1099
|
+
type: "PASSKEY_REFRESH_COMPLETE",
|
|
1100
|
+
...refreshedData
|
|
1101
|
+
}, dialogOrigin);
|
|
1102
|
+
} else {
|
|
1103
|
+
iframe.contentWindow?.postMessage({
|
|
1104
|
+
type: "PASSKEY_REFRESH_ERROR",
|
|
1105
|
+
error: "Failed to refresh quote"
|
|
1106
|
+
}, dialogOrigin);
|
|
1107
|
+
}
|
|
1108
|
+
return;
|
|
1109
|
+
}
|
|
1110
|
+
const payload = message?.data;
|
|
1111
|
+
if (message?.type === "PASSKEY_SIGNING_RESULT") {
|
|
1112
|
+
window.removeEventListener("message", handleMessage);
|
|
1113
|
+
if (message.success && payload?.intentId) {
|
|
1114
|
+
resolve({
|
|
1115
|
+
success: true,
|
|
1116
|
+
intentId: payload.intentId
|
|
1117
|
+
});
|
|
1118
|
+
} else if (message.success && payload?.signature) {
|
|
546
1119
|
resolve({
|
|
547
1120
|
success: true,
|
|
548
1121
|
signature: payload.signature,
|
|
@@ -574,28 +1147,165 @@ var OneAuthClient = class {
|
|
|
574
1147
|
});
|
|
575
1148
|
}
|
|
576
1149
|
/**
|
|
577
|
-
* Wait for the dialog to be closed
|
|
1150
|
+
* Wait for the dialog to be closed
|
|
1151
|
+
*/
|
|
1152
|
+
waitForDialogClose(dialog, cleanup) {
|
|
1153
|
+
const dialogOrigin = this.getDialogOrigin();
|
|
1154
|
+
return new Promise((resolve) => {
|
|
1155
|
+
const handleMessage = (event) => {
|
|
1156
|
+
if (event.origin !== dialogOrigin) return;
|
|
1157
|
+
if (event.data?.type === "PASSKEY_CLOSE") {
|
|
1158
|
+
window.removeEventListener("message", handleMessage);
|
|
1159
|
+
cleanup();
|
|
1160
|
+
resolve();
|
|
1161
|
+
}
|
|
1162
|
+
};
|
|
1163
|
+
const handleClose = () => {
|
|
1164
|
+
window.removeEventListener("message", handleMessage);
|
|
1165
|
+
dialog.removeEventListener("close", handleClose);
|
|
1166
|
+
cleanup();
|
|
1167
|
+
resolve();
|
|
1168
|
+
};
|
|
1169
|
+
window.addEventListener("message", handleMessage);
|
|
1170
|
+
dialog.addEventListener("close", handleClose);
|
|
1171
|
+
});
|
|
1172
|
+
}
|
|
1173
|
+
/**
|
|
1174
|
+
* Inject a preconnect link tag to pre-warm DNS + TLS for a given URL.
|
|
1175
|
+
*/
|
|
1176
|
+
injectPreconnect(url) {
|
|
1177
|
+
try {
|
|
1178
|
+
const origin = new URL(url).origin;
|
|
1179
|
+
if (document.querySelector(`link[rel="preconnect"][href="${origin}"]`)) return;
|
|
1180
|
+
const link = document.createElement("link");
|
|
1181
|
+
link.rel = "preconnect";
|
|
1182
|
+
link.href = origin;
|
|
1183
|
+
link.crossOrigin = "anonymous";
|
|
1184
|
+
document.head.appendChild(link);
|
|
1185
|
+
} catch {
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
1188
|
+
/**
|
|
1189
|
+
* Wait for the dialog iframe to signal ready without sending init data.
|
|
1190
|
+
* Returns a sendInit function the caller uses once prepare data is available.
|
|
1191
|
+
*/
|
|
1192
|
+
waitForDialogReadyDeferred(dialog, iframe, cleanup) {
|
|
1193
|
+
const dialogOrigin = this.getDialogOrigin();
|
|
1194
|
+
return new Promise((resolve) => {
|
|
1195
|
+
let settled = false;
|
|
1196
|
+
const teardown = () => {
|
|
1197
|
+
if (settled) return;
|
|
1198
|
+
settled = true;
|
|
1199
|
+
clearTimeout(readyTimeout);
|
|
1200
|
+
window.removeEventListener("message", handleMessage);
|
|
1201
|
+
dialog.removeEventListener("close", handleClose);
|
|
1202
|
+
};
|
|
1203
|
+
const handleMessage = (event) => {
|
|
1204
|
+
if (event.origin !== dialogOrigin) return;
|
|
1205
|
+
if (event.data?.type === "PASSKEY_READY") {
|
|
1206
|
+
teardown();
|
|
1207
|
+
resolve({
|
|
1208
|
+
ready: true,
|
|
1209
|
+
sendInit: (initMessage) => {
|
|
1210
|
+
iframe.contentWindow?.postMessage(
|
|
1211
|
+
{ type: "PASSKEY_INIT", ...initMessage },
|
|
1212
|
+
dialogOrigin
|
|
1213
|
+
);
|
|
1214
|
+
}
|
|
1215
|
+
});
|
|
1216
|
+
} else if (event.data?.type === "PASSKEY_CLOSE") {
|
|
1217
|
+
teardown();
|
|
1218
|
+
cleanup();
|
|
1219
|
+
resolve({ ready: false });
|
|
1220
|
+
}
|
|
1221
|
+
};
|
|
1222
|
+
const handleClose = () => {
|
|
1223
|
+
teardown();
|
|
1224
|
+
resolve({ ready: false });
|
|
1225
|
+
};
|
|
1226
|
+
const readyTimeout = setTimeout(() => {
|
|
1227
|
+
teardown();
|
|
1228
|
+
cleanup();
|
|
1229
|
+
resolve({ ready: false });
|
|
1230
|
+
}, 1e4);
|
|
1231
|
+
window.addEventListener("message", handleMessage);
|
|
1232
|
+
dialog.addEventListener("close", handleClose);
|
|
1233
|
+
});
|
|
1234
|
+
}
|
|
1235
|
+
/**
|
|
1236
|
+
* Prepare an intent by calling the orchestrator for a quote.
|
|
1237
|
+
* Returns the prepare response and the origin tier from the response header.
|
|
1238
|
+
*/
|
|
1239
|
+
async prepareIntent(requestBody) {
|
|
1240
|
+
try {
|
|
1241
|
+
const response = await fetch(`${this.config.providerUrl}/api/intent/prepare`, {
|
|
1242
|
+
method: "POST",
|
|
1243
|
+
headers: { "Content-Type": "application/json" },
|
|
1244
|
+
body: JSON.stringify(requestBody)
|
|
1245
|
+
});
|
|
1246
|
+
if (!response.ok) {
|
|
1247
|
+
const errorData = await response.json().catch(() => ({}));
|
|
1248
|
+
const errorMessage = errorData.error || "Failed to prepare intent";
|
|
1249
|
+
if (errorMessage.includes("User not found")) {
|
|
1250
|
+
localStorage.removeItem("1auth-user");
|
|
1251
|
+
}
|
|
1252
|
+
return {
|
|
1253
|
+
success: false,
|
|
1254
|
+
error: {
|
|
1255
|
+
code: errorMessage.includes("User not found") ? "USER_NOT_FOUND" : "PREPARE_FAILED",
|
|
1256
|
+
message: errorMessage
|
|
1257
|
+
}
|
|
1258
|
+
};
|
|
1259
|
+
}
|
|
1260
|
+
const tier = response.headers.get("X-Origin-Tier");
|
|
1261
|
+
return { success: true, data: await response.json(), tier };
|
|
1262
|
+
} catch (error) {
|
|
1263
|
+
return {
|
|
1264
|
+
success: false,
|
|
1265
|
+
error: {
|
|
1266
|
+
code: "NETWORK_ERROR",
|
|
1267
|
+
message: error instanceof Error ? error.message : "Network error"
|
|
1268
|
+
}
|
|
1269
|
+
};
|
|
1270
|
+
}
|
|
1271
|
+
}
|
|
1272
|
+
/**
|
|
1273
|
+
* Prepare a batch intent by calling the orchestrator for quotes on all intents.
|
|
1274
|
+
*/
|
|
1275
|
+
async prepareBatchIntent(requestBody) {
|
|
1276
|
+
try {
|
|
1277
|
+
const response = await fetch(`${this.config.providerUrl}/api/intent/batch-prepare`, {
|
|
1278
|
+
method: "POST",
|
|
1279
|
+
headers: { "Content-Type": "application/json" },
|
|
1280
|
+
body: JSON.stringify(requestBody)
|
|
1281
|
+
});
|
|
1282
|
+
if (!response.ok) {
|
|
1283
|
+
const errorData = await response.json().catch(() => ({}));
|
|
1284
|
+
const errorMessage = errorData.error || "Failed to prepare batch intent";
|
|
1285
|
+
if (errorMessage.includes("User not found")) {
|
|
1286
|
+
localStorage.removeItem("1auth-user");
|
|
1287
|
+
}
|
|
1288
|
+
return {
|
|
1289
|
+
success: false,
|
|
1290
|
+
error: errorMessage,
|
|
1291
|
+
failedIntents: errorData.failedIntents
|
|
1292
|
+
};
|
|
1293
|
+
}
|
|
1294
|
+
const tier = response.headers.get("X-Origin-Tier");
|
|
1295
|
+
return { success: true, data: await response.json(), tier };
|
|
1296
|
+
} catch {
|
|
1297
|
+
return { success: false, error: "Network error" };
|
|
1298
|
+
}
|
|
1299
|
+
}
|
|
1300
|
+
/**
|
|
1301
|
+
* Send a prepare error message to the dialog iframe.
|
|
578
1302
|
*/
|
|
579
|
-
|
|
1303
|
+
sendPrepareError(iframe, error) {
|
|
580
1304
|
const dialogOrigin = this.getDialogOrigin();
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
window.removeEventListener("message", handleMessage);
|
|
586
|
-
cleanup();
|
|
587
|
-
resolve();
|
|
588
|
-
}
|
|
589
|
-
};
|
|
590
|
-
const handleClose = () => {
|
|
591
|
-
window.removeEventListener("message", handleMessage);
|
|
592
|
-
dialog.removeEventListener("close", handleClose);
|
|
593
|
-
cleanup();
|
|
594
|
-
resolve();
|
|
595
|
-
};
|
|
596
|
-
window.addEventListener("message", handleMessage);
|
|
597
|
-
dialog.addEventListener("close", handleClose);
|
|
598
|
-
});
|
|
1305
|
+
iframe.contentWindow?.postMessage(
|
|
1306
|
+
{ type: "PASSKEY_PREPARE_ERROR", error },
|
|
1307
|
+
dialogOrigin
|
|
1308
|
+
);
|
|
599
1309
|
}
|
|
600
1310
|
/**
|
|
601
1311
|
* Poll for intent status
|
|
@@ -608,9 +1318,7 @@ var OneAuthClient = class {
|
|
|
608
1318
|
const response = await fetch(
|
|
609
1319
|
`${this.config.providerUrl}/api/intent/status/${intentId}`,
|
|
610
1320
|
{
|
|
611
|
-
headers: {
|
|
612
|
-
"x-client-id": this.config.clientId
|
|
613
|
-
}
|
|
1321
|
+
headers: this.config.clientId ? { "x-client-id": this.config.clientId } : {}
|
|
614
1322
|
}
|
|
615
1323
|
);
|
|
616
1324
|
if (!response.ok) {
|
|
@@ -645,6 +1353,43 @@ var OneAuthClient = class {
|
|
|
645
1353
|
};
|
|
646
1354
|
}
|
|
647
1355
|
}
|
|
1356
|
+
/**
|
|
1357
|
+
* Get the history of intents for the authenticated user.
|
|
1358
|
+
*
|
|
1359
|
+
* Requires an active session (user must be logged in).
|
|
1360
|
+
*
|
|
1361
|
+
* @example
|
|
1362
|
+
* ```typescript
|
|
1363
|
+
* // Get recent intents
|
|
1364
|
+
* const history = await client.getIntentHistory({ limit: 10 });
|
|
1365
|
+
*
|
|
1366
|
+
* // Filter by status
|
|
1367
|
+
* const pending = await client.getIntentHistory({ status: 'pending' });
|
|
1368
|
+
*
|
|
1369
|
+
* // Filter by date range
|
|
1370
|
+
* const lastWeek = await client.getIntentHistory({
|
|
1371
|
+
* from: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString(),
|
|
1372
|
+
* });
|
|
1373
|
+
* ```
|
|
1374
|
+
*/
|
|
1375
|
+
async getIntentHistory(options) {
|
|
1376
|
+
const queryParams = new URLSearchParams();
|
|
1377
|
+
if (options?.limit) queryParams.set("limit", String(options.limit));
|
|
1378
|
+
if (options?.offset) queryParams.set("offset", String(options.offset));
|
|
1379
|
+
if (options?.status) queryParams.set("status", options.status);
|
|
1380
|
+
if (options?.from) queryParams.set("from", options.from);
|
|
1381
|
+
if (options?.to) queryParams.set("to", options.to);
|
|
1382
|
+
const url = `${this.config.providerUrl}/api/intent/history${queryParams.toString() ? `?${queryParams}` : ""}`;
|
|
1383
|
+
const response = await fetch(url, {
|
|
1384
|
+
headers: this.config.clientId ? { "x-client-id": this.config.clientId } : {},
|
|
1385
|
+
credentials: "include"
|
|
1386
|
+
});
|
|
1387
|
+
if (!response.ok) {
|
|
1388
|
+
const errorData = await response.json().catch(() => ({}));
|
|
1389
|
+
throw new Error(errorData.error || "Failed to get intent history");
|
|
1390
|
+
}
|
|
1391
|
+
return response.json();
|
|
1392
|
+
}
|
|
648
1393
|
/**
|
|
649
1394
|
* Send a swap intent through the Rhinestone orchestrator
|
|
650
1395
|
*
|
|
@@ -707,17 +1452,21 @@ var OneAuthClient = class {
|
|
|
707
1452
|
};
|
|
708
1453
|
}
|
|
709
1454
|
};
|
|
710
|
-
|
|
711
|
-
if (
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
1455
|
+
let fromTokenAddress;
|
|
1456
|
+
if (options.fromToken) {
|
|
1457
|
+
const fromTokenResult = resolveToken(options.fromToken, "fromToken");
|
|
1458
|
+
if (!fromTokenResult.address) {
|
|
1459
|
+
return {
|
|
1460
|
+
success: false,
|
|
1461
|
+
intentId: "",
|
|
1462
|
+
status: "failed",
|
|
1463
|
+
error: {
|
|
1464
|
+
code: "INVALID_TOKEN",
|
|
1465
|
+
message: fromTokenResult.error || `Unknown fromToken: ${options.fromToken}`
|
|
1466
|
+
}
|
|
1467
|
+
};
|
|
1468
|
+
}
|
|
1469
|
+
fromTokenAddress = fromTokenResult.address;
|
|
721
1470
|
}
|
|
722
1471
|
const toTokenResult = resolveToken(options.toToken, "toToken");
|
|
723
1472
|
if (!toTokenResult.address) {
|
|
@@ -731,18 +1480,17 @@ var OneAuthClient = class {
|
|
|
731
1480
|
}
|
|
732
1481
|
};
|
|
733
1482
|
}
|
|
734
|
-
const fromTokenAddress = fromTokenResult.address;
|
|
735
1483
|
const toTokenAddress = toTokenResult.address;
|
|
736
1484
|
console.log("[SDK sendSwap] Token resolution:", {
|
|
737
|
-
fromToken: options.fromToken,
|
|
738
|
-
fromTokenAddress,
|
|
1485
|
+
fromToken: options.fromToken ?? "Any",
|
|
1486
|
+
fromTokenAddress: fromTokenAddress ?? "orchestrator picks",
|
|
739
1487
|
toToken: options.toToken,
|
|
740
1488
|
toTokenAddress,
|
|
741
1489
|
targetChain: options.targetChain
|
|
742
1490
|
});
|
|
743
1491
|
const formatTokenLabel = (token, fallback) => {
|
|
744
1492
|
if (!token.startsWith("0x")) {
|
|
745
|
-
return token
|
|
1493
|
+
return token;
|
|
746
1494
|
}
|
|
747
1495
|
try {
|
|
748
1496
|
return getTokenSymbol(token, options.targetChain);
|
|
@@ -750,15 +1498,11 @@ var OneAuthClient = class {
|
|
|
750
1498
|
return fallback;
|
|
751
1499
|
}
|
|
752
1500
|
};
|
|
753
|
-
const fromSymbol = formatTokenLabel(
|
|
754
|
-
options.fromToken,
|
|
755
|
-
`${options.fromToken.slice(0, 6)}...${options.fromToken.slice(-4)}`
|
|
756
|
-
);
|
|
757
1501
|
const toSymbol = formatTokenLabel(
|
|
758
1502
|
options.toToken,
|
|
759
1503
|
`${options.toToken.slice(0, 6)}...${options.toToken.slice(-4)}`
|
|
760
1504
|
);
|
|
761
|
-
const isFromNativeEth = fromTokenAddress === "0x0000000000000000000000000000000000000000";
|
|
1505
|
+
const isFromNativeEth = fromTokenAddress ? fromTokenAddress === "0x0000000000000000000000000000000000000000" : false;
|
|
762
1506
|
const isToNativeEth = toTokenAddress === "0x0000000000000000000000000000000000000000";
|
|
763
1507
|
const KNOWN_DECIMALS = {
|
|
764
1508
|
ETH: 18,
|
|
@@ -768,31 +1512,33 @@ var OneAuthClient = class {
|
|
|
768
1512
|
USDT0: 6
|
|
769
1513
|
};
|
|
770
1514
|
const getDecimals = (symbol, chainId) => {
|
|
771
|
-
const upperSymbol = symbol.toUpperCase();
|
|
772
1515
|
try {
|
|
773
|
-
const
|
|
774
|
-
|
|
1516
|
+
const match = getSupportedTokens(chainId).find(
|
|
1517
|
+
(t) => t.symbol.toUpperCase() === symbol.toUpperCase()
|
|
1518
|
+
);
|
|
1519
|
+
if (match) {
|
|
1520
|
+
console.log(`[SDK] getTokenDecimals(${match.symbol}, ${chainId}) = ${match.decimals}`);
|
|
1521
|
+
return match.decimals;
|
|
1522
|
+
}
|
|
1523
|
+
const decimals = getTokenDecimals(symbol, chainId);
|
|
1524
|
+
console.log(`[SDK] getTokenDecimals(${symbol}, ${chainId}) = ${decimals}`);
|
|
775
1525
|
return decimals;
|
|
776
1526
|
} catch (e) {
|
|
777
|
-
|
|
1527
|
+
const upperSymbol = symbol.toUpperCase();
|
|
1528
|
+
console.warn(`[SDK] getTokenDecimals failed for ${symbol}, using fallback`, e);
|
|
778
1529
|
return KNOWN_DECIMALS[upperSymbol] ?? 18;
|
|
779
1530
|
}
|
|
780
1531
|
};
|
|
781
|
-
const fromDecimals = getDecimals(options.fromToken, options.targetChain);
|
|
782
1532
|
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
|
-
}
|
|
1533
|
+
const isBridge = options.fromToken ? options.fromToken.toUpperCase() === options.toToken.toUpperCase() : false;
|
|
1534
|
+
const tokenRequests = [{
|
|
1535
|
+
token: toTokenAddress,
|
|
1536
|
+
amount: parseUnits(options.amount, toDecimals)
|
|
1537
|
+
}];
|
|
791
1538
|
console.log("[SDK sendSwap] Building intent:", {
|
|
792
1539
|
isBridge,
|
|
793
1540
|
isFromNativeEth,
|
|
794
1541
|
isToNativeEth,
|
|
795
|
-
fromDecimals,
|
|
796
1542
|
toDecimals,
|
|
797
1543
|
tokenRequests
|
|
798
1544
|
});
|
|
@@ -803,15 +1549,20 @@ var OneAuthClient = class {
|
|
|
803
1549
|
{
|
|
804
1550
|
// Minimal call - just signals to orchestrator we want the tokenRequests delivered
|
|
805
1551
|
to: toTokenAddress,
|
|
806
|
-
value: "0"
|
|
1552
|
+
value: "0",
|
|
1553
|
+
// SDK provides labels so dialog shows "Buy ETH" not "Send ETH / To: 0x000..."
|
|
1554
|
+
label: `Buy ${toSymbol}`,
|
|
1555
|
+
sublabel: `${options.amount} ${toSymbol}`
|
|
807
1556
|
}
|
|
808
1557
|
],
|
|
809
1558
|
// Request specific output tokens - this is what actually matters for swaps
|
|
810
1559
|
tokenRequests,
|
|
811
1560
|
// Constrain orchestrator to use only the fromToken as input
|
|
812
1561
|
// This ensures the swap uses the correct source token
|
|
813
|
-
//
|
|
814
|
-
sourceAssets: options.sourceAssets || [options.fromToken
|
|
1562
|
+
// Use canonical symbol casing from registry (e.g. "MockUSD" not "MOCKUSD")
|
|
1563
|
+
sourceAssets: options.sourceAssets || (options.fromToken ? [options.fromToken] : void 0),
|
|
1564
|
+
// Pass source chain ID so orchestrator knows which chain to look for tokens on
|
|
1565
|
+
sourceChainId: options.sourceChainId,
|
|
815
1566
|
closeOn: options.closeOn || "preconfirmed",
|
|
816
1567
|
waitForHash: options.waitForHash,
|
|
817
1568
|
hashTimeoutMs: options.hashTimeoutMs,
|
|
@@ -820,7 +1571,7 @@ var OneAuthClient = class {
|
|
|
820
1571
|
return {
|
|
821
1572
|
...result,
|
|
822
1573
|
quote: result.success ? {
|
|
823
|
-
fromToken: fromTokenAddress,
|
|
1574
|
+
fromToken: fromTokenAddress ?? options.fromToken,
|
|
824
1575
|
toToken: toTokenAddress,
|
|
825
1576
|
amountIn: options.amount,
|
|
826
1577
|
amountOut: "",
|
|
@@ -862,26 +1613,24 @@ var OneAuthClient = class {
|
|
|
862
1613
|
const themeParams = this.getThemeParams(options?.theme);
|
|
863
1614
|
const signingUrl = `${dialogUrl}/dialog/sign?mode=iframe${themeParams ? `&${themeParams}` : ""}`;
|
|
864
1615
|
const { dialog, iframe, cleanup } = this.createModalDialog(signingUrl);
|
|
865
|
-
const
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
resolve();
|
|
1616
|
+
const ready = await this.waitForDialogReady(dialog, iframe, cleanup, {
|
|
1617
|
+
mode: "iframe",
|
|
1618
|
+
message: options.message,
|
|
1619
|
+
challenge: options.challenge || options.message,
|
|
1620
|
+
username: options.username,
|
|
1621
|
+
accountAddress: options.accountAddress,
|
|
1622
|
+
description: options.description,
|
|
1623
|
+
metadata: options.metadata
|
|
1624
|
+
});
|
|
1625
|
+
if (!ready) {
|
|
1626
|
+
return {
|
|
1627
|
+
success: false,
|
|
1628
|
+
error: {
|
|
1629
|
+
code: "USER_REJECTED",
|
|
1630
|
+
message: "User closed the dialog"
|
|
881
1631
|
}
|
|
882
1632
|
};
|
|
883
|
-
|
|
884
|
-
});
|
|
1633
|
+
}
|
|
885
1634
|
const signingResult = await this.waitForSigningResponse(dialog, iframe, cleanup);
|
|
886
1635
|
cleanup();
|
|
887
1636
|
if (signingResult.success) {
|
|
@@ -950,31 +1699,29 @@ var OneAuthClient = class {
|
|
|
950
1699
|
const themeParams = this.getThemeParams(options?.theme);
|
|
951
1700
|
const signingUrl = `${dialogUrl}/dialog/sign?mode=iframe${themeParams ? `&${themeParams}` : ""}`;
|
|
952
1701
|
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
|
-
|
|
973
|
-
resolve();
|
|
1702
|
+
const ready = await this.waitForDialogReady(dialog, iframe, cleanup, {
|
|
1703
|
+
mode: "iframe",
|
|
1704
|
+
signingMode: "typedData",
|
|
1705
|
+
typedData: {
|
|
1706
|
+
domain: options.domain,
|
|
1707
|
+
types: options.types,
|
|
1708
|
+
primaryType: options.primaryType,
|
|
1709
|
+
message: options.message
|
|
1710
|
+
},
|
|
1711
|
+
challenge: signedHash,
|
|
1712
|
+
username: options.username,
|
|
1713
|
+
accountAddress: options.accountAddress,
|
|
1714
|
+
description: options.description
|
|
1715
|
+
});
|
|
1716
|
+
if (!ready) {
|
|
1717
|
+
return {
|
|
1718
|
+
success: false,
|
|
1719
|
+
error: {
|
|
1720
|
+
code: "USER_REJECTED",
|
|
1721
|
+
message: "User closed the dialog"
|
|
974
1722
|
}
|
|
975
1723
|
};
|
|
976
|
-
|
|
977
|
-
});
|
|
1724
|
+
}
|
|
978
1725
|
const signingResult = await this.waitForSigningResponse(dialog, iframe, cleanup);
|
|
979
1726
|
cleanup();
|
|
980
1727
|
if (signingResult.success) {
|
|
@@ -1037,7 +1784,7 @@ var OneAuthClient = class {
|
|
|
1037
1784
|
iframe.style.borderRadius = "12px";
|
|
1038
1785
|
iframe.style.boxShadow = "0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)";
|
|
1039
1786
|
iframe.id = `passkey-embed-${requestId}`;
|
|
1040
|
-
iframe.allow = "publickey-credentials-get *; publickey-credentials-create
|
|
1787
|
+
iframe.allow = "publickey-credentials-get *; publickey-credentials-create *; identity-credentials-get";
|
|
1041
1788
|
iframe.onload = () => {
|
|
1042
1789
|
options.onReady?.();
|
|
1043
1790
|
};
|
|
@@ -1128,9 +1875,7 @@ var OneAuthClient = class {
|
|
|
1128
1875
|
const response = await fetch(
|
|
1129
1876
|
`${this.config.providerUrl}/api/users/${encodeURIComponent(username)}/passkeys`,
|
|
1130
1877
|
{
|
|
1131
|
-
headers: {
|
|
1132
|
-
"x-client-id": this.config.clientId
|
|
1133
|
-
}
|
|
1878
|
+
headers: this.config.clientId ? { "x-client-id": this.config.clientId } : {}
|
|
1134
1879
|
}
|
|
1135
1880
|
);
|
|
1136
1881
|
if (!response.ok) {
|
|
@@ -1149,7 +1894,7 @@ var OneAuthClient = class {
|
|
|
1149
1894
|
"Content-Type": "application/json"
|
|
1150
1895
|
},
|
|
1151
1896
|
body: JSON.stringify({
|
|
1152
|
-
clientId: this.config.clientId,
|
|
1897
|
+
...this.config.clientId && { clientId: this.config.clientId },
|
|
1153
1898
|
username: options.username,
|
|
1154
1899
|
challenge: options.challenge,
|
|
1155
1900
|
description: options.description,
|
|
@@ -1175,6 +1920,50 @@ var OneAuthClient = class {
|
|
|
1175
1920
|
`width=${POPUP_WIDTH},height=${POPUP_HEIGHT},left=${left},top=${top},popup=true`
|
|
1176
1921
|
);
|
|
1177
1922
|
}
|
|
1923
|
+
/**
|
|
1924
|
+
* Wait for the dialog iframe to signal ready, then send init data.
|
|
1925
|
+
* Also handles early close (X button, escape, backdrop) during the ready phase.
|
|
1926
|
+
* Returns true if dialog is ready, false if it was closed before becoming ready.
|
|
1927
|
+
*/
|
|
1928
|
+
waitForDialogReady(dialog, iframe, cleanup, initMessage) {
|
|
1929
|
+
const dialogOrigin = this.getDialogOrigin();
|
|
1930
|
+
return new Promise((resolve) => {
|
|
1931
|
+
let settled = false;
|
|
1932
|
+
const teardown = () => {
|
|
1933
|
+
if (settled) return;
|
|
1934
|
+
settled = true;
|
|
1935
|
+
clearTimeout(readyTimeout);
|
|
1936
|
+
window.removeEventListener("message", handleMessage);
|
|
1937
|
+
dialog.removeEventListener("close", handleClose);
|
|
1938
|
+
};
|
|
1939
|
+
const handleMessage = (event) => {
|
|
1940
|
+
if (event.origin !== dialogOrigin) return;
|
|
1941
|
+
if (event.data?.type === "PASSKEY_READY") {
|
|
1942
|
+
teardown();
|
|
1943
|
+
iframe.contentWindow?.postMessage({
|
|
1944
|
+
type: "PASSKEY_INIT",
|
|
1945
|
+
...initMessage
|
|
1946
|
+
}, dialogOrigin);
|
|
1947
|
+
resolve(true);
|
|
1948
|
+
} else if (event.data?.type === "PASSKEY_CLOSE") {
|
|
1949
|
+
teardown();
|
|
1950
|
+
cleanup();
|
|
1951
|
+
resolve(false);
|
|
1952
|
+
}
|
|
1953
|
+
};
|
|
1954
|
+
const handleClose = () => {
|
|
1955
|
+
teardown();
|
|
1956
|
+
resolve(false);
|
|
1957
|
+
};
|
|
1958
|
+
const readyTimeout = setTimeout(() => {
|
|
1959
|
+
teardown();
|
|
1960
|
+
cleanup();
|
|
1961
|
+
resolve(false);
|
|
1962
|
+
}, 1e4);
|
|
1963
|
+
window.addEventListener("message", handleMessage);
|
|
1964
|
+
dialog.addEventListener("close", handleClose);
|
|
1965
|
+
});
|
|
1966
|
+
}
|
|
1178
1967
|
/**
|
|
1179
1968
|
* Create a modal dialog with an iframe inside.
|
|
1180
1969
|
*/
|
|
@@ -1211,7 +2000,9 @@ var OneAuthClient = class {
|
|
|
1211
2000
|
border-radius: 14px;
|
|
1212
2001
|
border: none;
|
|
1213
2002
|
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.12), 0 2px 8px rgba(0, 0, 0, 0.08);
|
|
1214
|
-
transition:
|
|
2003
|
+
transition: height 0.15s ease-out;
|
|
2004
|
+
max-height: calc(100vh - 100px);
|
|
2005
|
+
max-height: calc(100dvh - 100px);
|
|
1215
2006
|
}
|
|
1216
2007
|
|
|
1217
2008
|
@media (min-width: 769px) {
|
|
@@ -1273,14 +2064,10 @@ var OneAuthClient = class {
|
|
|
1273
2064
|
const iframe = document.createElement("iframe");
|
|
1274
2065
|
iframe.setAttribute(
|
|
1275
2066
|
"allow",
|
|
1276
|
-
"publickey-credentials-get *; publickey-credentials-create *; clipboard-write"
|
|
2067
|
+
"publickey-credentials-get *; publickey-credentials-create *; clipboard-write; identity-credentials-get"
|
|
1277
2068
|
);
|
|
1278
2069
|
iframe.setAttribute("aria-label", "Passkey Authentication");
|
|
1279
2070
|
iframe.setAttribute("tabindex", "0");
|
|
1280
|
-
iframe.setAttribute(
|
|
1281
|
-
"sandbox",
|
|
1282
|
-
"allow-forms allow-scripts allow-same-origin allow-popups allow-popups-to-escape-sandbox"
|
|
1283
|
-
);
|
|
1284
2071
|
iframe.setAttribute("src", url);
|
|
1285
2072
|
iframe.setAttribute("title", "Passkey");
|
|
1286
2073
|
iframe.style.border = "none";
|
|
@@ -1290,10 +2077,8 @@ var OneAuthClient = class {
|
|
|
1290
2077
|
const handleMessage = (event) => {
|
|
1291
2078
|
if (event.origin !== hostUrl.origin) return;
|
|
1292
2079
|
if (event.data?.type === "PASSKEY_RESIZE") {
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
iframe.style.width = `${event.data.width}px`;
|
|
1296
|
-
}
|
|
2080
|
+
const maxHeight = window.innerHeight - 100;
|
|
2081
|
+
iframe.style.height = `${Math.min(event.data.height, maxHeight)}px`;
|
|
1297
2082
|
} else if (event.data?.type === "PASSKEY_DISCONNECT") {
|
|
1298
2083
|
localStorage.removeItem("1auth-user");
|
|
1299
2084
|
}
|
|
@@ -1311,7 +2096,10 @@ var OneAuthClient = class {
|
|
|
1311
2096
|
}
|
|
1312
2097
|
});
|
|
1313
2098
|
dialog.showModal();
|
|
2099
|
+
let cleanedUp = false;
|
|
1314
2100
|
const cleanup = () => {
|
|
2101
|
+
if (cleanedUp) return;
|
|
2102
|
+
cleanedUp = true;
|
|
1315
2103
|
window.removeEventListener("message", handleMessage);
|
|
1316
2104
|
document.removeEventListener("keydown", handleEscape);
|
|
1317
2105
|
dialog.close();
|
|
@@ -1319,12 +2107,24 @@ var OneAuthClient = class {
|
|
|
1319
2107
|
};
|
|
1320
2108
|
return { dialog, iframe, cleanup };
|
|
1321
2109
|
}
|
|
1322
|
-
waitForModalAuthResponse(_dialog,
|
|
2110
|
+
waitForModalAuthResponse(_dialog, iframe, cleanup) {
|
|
1323
2111
|
const dialogOrigin = this.getDialogOrigin();
|
|
1324
2112
|
return new Promise((resolve) => {
|
|
2113
|
+
let dialogReady = false;
|
|
1325
2114
|
const handleMessage = (event) => {
|
|
1326
2115
|
if (event.origin !== dialogOrigin) return;
|
|
1327
2116
|
const data = event.data;
|
|
2117
|
+
if (data?.type === "PASSKEY_READY") {
|
|
2118
|
+
dialogReady = true;
|
|
2119
|
+
iframe.contentWindow?.postMessage({
|
|
2120
|
+
type: "PASSKEY_INIT",
|
|
2121
|
+
mode: "iframe"
|
|
2122
|
+
}, dialogOrigin);
|
|
2123
|
+
return;
|
|
2124
|
+
}
|
|
2125
|
+
if (!dialogReady && data?.type === "PASSKEY_CLOSE") {
|
|
2126
|
+
return;
|
|
2127
|
+
}
|
|
1328
2128
|
if (data?.type === "PASSKEY_LOGIN_RESULT") {
|
|
1329
2129
|
window.removeEventListener("message", handleMessage);
|
|
1330
2130
|
cleanup();
|
|
@@ -1332,6 +2132,7 @@ var OneAuthClient = class {
|
|
|
1332
2132
|
resolve({
|
|
1333
2133
|
success: true,
|
|
1334
2134
|
username: data.data?.username,
|
|
2135
|
+
address: data.data?.address,
|
|
1335
2136
|
user: data.data?.user
|
|
1336
2137
|
});
|
|
1337
2138
|
} else {
|
|
@@ -1346,7 +2147,8 @@ var OneAuthClient = class {
|
|
|
1346
2147
|
if (data.success) {
|
|
1347
2148
|
resolve({
|
|
1348
2149
|
success: true,
|
|
1349
|
-
username: data.data?.username
|
|
2150
|
+
username: data.data?.username,
|
|
2151
|
+
address: data.data?.address
|
|
1350
2152
|
});
|
|
1351
2153
|
} else {
|
|
1352
2154
|
resolve({
|
|
@@ -1354,6 +2156,11 @@ var OneAuthClient = class {
|
|
|
1354
2156
|
error: data.error
|
|
1355
2157
|
});
|
|
1356
2158
|
}
|
|
2159
|
+
} else if (data?.type === "PASSKEY_RETRY_POPUP") {
|
|
2160
|
+
window.removeEventListener("message", handleMessage);
|
|
2161
|
+
cleanup();
|
|
2162
|
+
const popupUrl = data.data?.url?.replace("mode=iframe", "mode=popup") || `${this.getDialogUrl()}/dialog/auth?mode=popup${this.config.clientId ? `&clientId=${this.config.clientId}` : ""}`;
|
|
2163
|
+
this.waitForPopupAuthResponse(popupUrl).then(resolve);
|
|
1357
2164
|
} else if (data?.type === "PASSKEY_CLOSE") {
|
|
1358
2165
|
window.removeEventListener("message", handleMessage);
|
|
1359
2166
|
cleanup();
|
|
@@ -1369,6 +2176,79 @@ var OneAuthClient = class {
|
|
|
1369
2176
|
window.addEventListener("message", handleMessage);
|
|
1370
2177
|
});
|
|
1371
2178
|
}
|
|
2179
|
+
/**
|
|
2180
|
+
* Open a popup for auth and wait for the result.
|
|
2181
|
+
* Used when iframe mode fails (e.g., due to password manager interference).
|
|
2182
|
+
*/
|
|
2183
|
+
waitForPopupAuthResponse(url) {
|
|
2184
|
+
const dialogOrigin = this.getDialogOrigin();
|
|
2185
|
+
const popup = this.openPopup(url);
|
|
2186
|
+
return new Promise((resolve) => {
|
|
2187
|
+
const pollTimer = setInterval(() => {
|
|
2188
|
+
if (popup?.closed) {
|
|
2189
|
+
clearInterval(pollTimer);
|
|
2190
|
+
window.removeEventListener("message", handleMessage);
|
|
2191
|
+
resolve({
|
|
2192
|
+
success: false,
|
|
2193
|
+
error: {
|
|
2194
|
+
code: "USER_CANCELLED",
|
|
2195
|
+
message: "Authentication was cancelled"
|
|
2196
|
+
}
|
|
2197
|
+
});
|
|
2198
|
+
}
|
|
2199
|
+
}, 500);
|
|
2200
|
+
const handleMessage = (event) => {
|
|
2201
|
+
if (event.origin !== dialogOrigin) return;
|
|
2202
|
+
const data = event.data;
|
|
2203
|
+
if (data?.type === "PASSKEY_LOGIN_RESULT") {
|
|
2204
|
+
clearInterval(pollTimer);
|
|
2205
|
+
window.removeEventListener("message", handleMessage);
|
|
2206
|
+
popup?.close();
|
|
2207
|
+
if (data.success) {
|
|
2208
|
+
resolve({
|
|
2209
|
+
success: true,
|
|
2210
|
+
username: data.data?.username,
|
|
2211
|
+
address: data.data?.address,
|
|
2212
|
+
user: data.data?.user
|
|
2213
|
+
});
|
|
2214
|
+
} else {
|
|
2215
|
+
resolve({
|
|
2216
|
+
success: false,
|
|
2217
|
+
error: data.error
|
|
2218
|
+
});
|
|
2219
|
+
}
|
|
2220
|
+
} else if (data?.type === "PASSKEY_REGISTER_RESULT") {
|
|
2221
|
+
clearInterval(pollTimer);
|
|
2222
|
+
window.removeEventListener("message", handleMessage);
|
|
2223
|
+
popup?.close();
|
|
2224
|
+
if (data.success) {
|
|
2225
|
+
resolve({
|
|
2226
|
+
success: true,
|
|
2227
|
+
username: data.data?.username,
|
|
2228
|
+
address: data.data?.address
|
|
2229
|
+
});
|
|
2230
|
+
} else {
|
|
2231
|
+
resolve({
|
|
2232
|
+
success: false,
|
|
2233
|
+
error: data.error
|
|
2234
|
+
});
|
|
2235
|
+
}
|
|
2236
|
+
} else if (data?.type === "PASSKEY_CLOSE") {
|
|
2237
|
+
clearInterval(pollTimer);
|
|
2238
|
+
window.removeEventListener("message", handleMessage);
|
|
2239
|
+
popup?.close();
|
|
2240
|
+
resolve({
|
|
2241
|
+
success: false,
|
|
2242
|
+
error: {
|
|
2243
|
+
code: "USER_CANCELLED",
|
|
2244
|
+
message: "Authentication was cancelled"
|
|
2245
|
+
}
|
|
2246
|
+
});
|
|
2247
|
+
}
|
|
2248
|
+
};
|
|
2249
|
+
window.addEventListener("message", handleMessage);
|
|
2250
|
+
});
|
|
2251
|
+
}
|
|
1372
2252
|
waitForAuthenticateResponse(_dialog, _iframe, cleanup) {
|
|
1373
2253
|
const dialogOrigin = this.getDialogOrigin();
|
|
1374
2254
|
return new Promise((resolve) => {
|
|
@@ -1408,6 +2288,84 @@ var OneAuthClient = class {
|
|
|
1408
2288
|
window.addEventListener("message", handleMessage);
|
|
1409
2289
|
});
|
|
1410
2290
|
}
|
|
2291
|
+
waitForConnectResponse(_dialog, _iframe, cleanup) {
|
|
2292
|
+
const dialogOrigin = this.getDialogOrigin();
|
|
2293
|
+
return new Promise((resolve) => {
|
|
2294
|
+
const handleMessage = (event) => {
|
|
2295
|
+
if (event.origin !== dialogOrigin) return;
|
|
2296
|
+
const data = event.data;
|
|
2297
|
+
if (data?.type === "PASSKEY_CONNECT_RESULT") {
|
|
2298
|
+
window.removeEventListener("message", handleMessage);
|
|
2299
|
+
cleanup();
|
|
2300
|
+
if (data.success) {
|
|
2301
|
+
resolve({
|
|
2302
|
+
success: true,
|
|
2303
|
+
username: data.data?.username,
|
|
2304
|
+
address: data.data?.address,
|
|
2305
|
+
autoConnected: data.data?.autoConnected
|
|
2306
|
+
});
|
|
2307
|
+
} else {
|
|
2308
|
+
resolve({
|
|
2309
|
+
success: false,
|
|
2310
|
+
action: data.data?.action,
|
|
2311
|
+
error: data.error
|
|
2312
|
+
});
|
|
2313
|
+
}
|
|
2314
|
+
} else if (data?.type === "PASSKEY_CLOSE") {
|
|
2315
|
+
window.removeEventListener("message", handleMessage);
|
|
2316
|
+
cleanup();
|
|
2317
|
+
resolve({
|
|
2318
|
+
success: false,
|
|
2319
|
+
action: "cancel",
|
|
2320
|
+
error: {
|
|
2321
|
+
code: "USER_CANCELLED",
|
|
2322
|
+
message: "Connection was cancelled"
|
|
2323
|
+
}
|
|
2324
|
+
});
|
|
2325
|
+
}
|
|
2326
|
+
};
|
|
2327
|
+
window.addEventListener("message", handleMessage);
|
|
2328
|
+
});
|
|
2329
|
+
}
|
|
2330
|
+
waitForConsentResponse(_dialog, _iframe, cleanup) {
|
|
2331
|
+
const dialogOrigin = this.getDialogOrigin();
|
|
2332
|
+
return new Promise((resolve) => {
|
|
2333
|
+
const handleMessage = (event) => {
|
|
2334
|
+
if (event.origin !== dialogOrigin) return;
|
|
2335
|
+
const data = event.data;
|
|
2336
|
+
if (data?.type === "PASSKEY_CONSENT_RESULT") {
|
|
2337
|
+
window.removeEventListener("message", handleMessage);
|
|
2338
|
+
cleanup();
|
|
2339
|
+
if (data.success) {
|
|
2340
|
+
resolve({
|
|
2341
|
+
success: true,
|
|
2342
|
+
data: data.data,
|
|
2343
|
+
grantedAt: data.data?.grantedAt
|
|
2344
|
+
});
|
|
2345
|
+
} else {
|
|
2346
|
+
resolve({
|
|
2347
|
+
success: false,
|
|
2348
|
+
error: data.error ?? {
|
|
2349
|
+
code: "USER_REJECTED",
|
|
2350
|
+
message: "User denied the consent request"
|
|
2351
|
+
}
|
|
2352
|
+
});
|
|
2353
|
+
}
|
|
2354
|
+
} else if (data?.type === "PASSKEY_CLOSE") {
|
|
2355
|
+
window.removeEventListener("message", handleMessage);
|
|
2356
|
+
cleanup();
|
|
2357
|
+
resolve({
|
|
2358
|
+
success: false,
|
|
2359
|
+
error: {
|
|
2360
|
+
code: "USER_CANCELLED",
|
|
2361
|
+
message: "User closed the dialog"
|
|
2362
|
+
}
|
|
2363
|
+
});
|
|
2364
|
+
}
|
|
2365
|
+
};
|
|
2366
|
+
window.addEventListener("message", handleMessage);
|
|
2367
|
+
});
|
|
2368
|
+
}
|
|
1411
2369
|
waitForModalSigningResponse(requestId, _dialog, _iframe, cleanup) {
|
|
1412
2370
|
const dialogOrigin = this.getDialogOrigin();
|
|
1413
2371
|
return new Promise((resolve) => {
|
|
@@ -1496,9 +2454,7 @@ var OneAuthClient = class {
|
|
|
1496
2454
|
const response = await fetch(
|
|
1497
2455
|
`${this.config.providerUrl}/api/sign/request/${requestId}`,
|
|
1498
2456
|
{
|
|
1499
|
-
headers: {
|
|
1500
|
-
"x-client-id": this.config.clientId
|
|
1501
|
-
}
|
|
2457
|
+
headers: this.config.clientId ? { "x-client-id": this.config.clientId } : {}
|
|
1502
2458
|
}
|
|
1503
2459
|
);
|
|
1504
2460
|
if (!response.ok) {
|
|
@@ -1640,6 +2596,9 @@ function createPasskeyWalletClient(config) {
|
|
|
1640
2596
|
if (!result.success) {
|
|
1641
2597
|
throw new Error(result.error?.message || "Signing failed");
|
|
1642
2598
|
}
|
|
2599
|
+
if (!result.signature) {
|
|
2600
|
+
throw new Error("No signature received");
|
|
2601
|
+
}
|
|
1643
2602
|
return encodeWebAuthnSignature(result.signature);
|
|
1644
2603
|
};
|
|
1645
2604
|
const signTransactionImpl = async (transaction) => {
|
|
@@ -1660,6 +2619,9 @@ function createPasskeyWalletClient(config) {
|
|
|
1660
2619
|
if (!result.success) {
|
|
1661
2620
|
throw new Error(result.error?.message || "Signing failed");
|
|
1662
2621
|
}
|
|
2622
|
+
if (!result.signature) {
|
|
2623
|
+
throw new Error("No signature received");
|
|
2624
|
+
}
|
|
1663
2625
|
return encodeWebAuthnSignature(result.signature);
|
|
1664
2626
|
};
|
|
1665
2627
|
const signTypedDataImpl = async (typedData) => {
|
|
@@ -1681,6 +2643,9 @@ function createPasskeyWalletClient(config) {
|
|
|
1681
2643
|
if (!result.success) {
|
|
1682
2644
|
throw new Error(result.error?.message || "Signing failed");
|
|
1683
2645
|
}
|
|
2646
|
+
if (!result.signature) {
|
|
2647
|
+
throw new Error("No signature received");
|
|
2648
|
+
}
|
|
1684
2649
|
return encodeWebAuthnSignature(result.signature);
|
|
1685
2650
|
};
|
|
1686
2651
|
const buildIntentPayload = async (calls, targetChainOverride) => {
|
|
@@ -1749,11 +2714,12 @@ function createPasskeyWalletClient(config) {
|
|
|
1749
2714
|
* Send multiple calls as a single batched transaction
|
|
1750
2715
|
*/
|
|
1751
2716
|
async sendCalls(params) {
|
|
1752
|
-
const { calls, chainId: targetChain } = params;
|
|
2717
|
+
const { calls, chainId: targetChain, tokenRequests } = params;
|
|
1753
2718
|
const closeOn = config.waitForHash ?? true ? "completed" : "preconfirmed";
|
|
1754
2719
|
const intentPayload = await buildIntentPayload(calls, targetChain);
|
|
1755
2720
|
const result = await provider.sendIntent({
|
|
1756
2721
|
...intentPayload,
|
|
2722
|
+
tokenRequests,
|
|
1757
2723
|
closeOn,
|
|
1758
2724
|
waitForHash: config.waitForHash ?? true,
|
|
1759
2725
|
hashTimeoutMs: config.hashTimeoutMs,
|
|
@@ -2197,9 +3163,10 @@ function BatchQueueWidget({ onSignAll }) {
|
|
|
2197
3163
|
|
|
2198
3164
|
// src/verify.ts
|
|
2199
3165
|
import { keccak256, toBytes } from "viem";
|
|
2200
|
-
var
|
|
3166
|
+
var ETHEREUM_MESSAGE_PREFIX = "Ethereum Signed Message:\n";
|
|
3167
|
+
var PASSKEY_MESSAGE_PREFIX = ETHEREUM_MESSAGE_PREFIX;
|
|
2201
3168
|
function hashMessage2(message) {
|
|
2202
|
-
const prefixed =
|
|
3169
|
+
const prefixed = ETHEREUM_MESSAGE_PREFIX + message.length.toString() + message;
|
|
2203
3170
|
return keccak256(toBytes(prefixed));
|
|
2204
3171
|
}
|
|
2205
3172
|
function verifyMessageHash(message, signedHash) {
|
|
@@ -2210,9 +3177,11 @@ function verifyMessageHash(message, signedHash) {
|
|
|
2210
3177
|
export {
|
|
2211
3178
|
BatchQueueProvider,
|
|
2212
3179
|
BatchQueueWidget,
|
|
3180
|
+
ETHEREUM_MESSAGE_PREFIX,
|
|
2213
3181
|
OneAuthClient,
|
|
2214
3182
|
PASSKEY_MESSAGE_PREFIX,
|
|
2215
3183
|
OneAuthClient as PasskeyProviderClient,
|
|
3184
|
+
createOneAuthProvider,
|
|
2216
3185
|
createPasskeyAccount,
|
|
2217
3186
|
createPasskeyProvider,
|
|
2218
3187
|
createPasskeyWalletClient,
|