@portal-hq/web 3.6.2-alpha → 3.7.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.
Files changed (45) hide show
  1. package/hypernative.d.ts +346 -0
  2. package/lib/commonjs/index.js +144 -2
  3. package/lib/commonjs/index.test.js +119 -2
  4. package/lib/commonjs/integrations/security/hypernative/index.js +101 -0
  5. package/lib/commonjs/integrations/security/hypernative/index.test.js +151 -0
  6. package/lib/commonjs/integrations/security/index.js +16 -0
  7. package/lib/commonjs/integrations/trading/zero-x/index.js +17 -4
  8. package/lib/commonjs/integrations/trading/zero-x/index.test.js +61 -15
  9. package/lib/commonjs/mpc/index.js +156 -5
  10. package/lib/commonjs/mpc/index.test.js +794 -5
  11. package/lib/commonjs/passkeys/index.js +394 -0
  12. package/lib/commonjs/passkeys/types.js +2 -0
  13. package/lib/commonjs/provider/index.js +5 -2
  14. package/lib/esm/index.js +144 -2
  15. package/lib/esm/index.test.js +119 -2
  16. package/lib/esm/integrations/security/hypernative/index.js +98 -0
  17. package/lib/esm/integrations/security/hypernative/index.test.js +146 -0
  18. package/lib/esm/integrations/security/index.js +10 -0
  19. package/lib/esm/integrations/trading/zero-x/index.js +17 -4
  20. package/lib/esm/integrations/trading/zero-x/index.test.js +62 -16
  21. package/lib/esm/mpc/index.js +156 -5
  22. package/lib/esm/mpc/index.test.js +795 -6
  23. package/lib/esm/passkeys/index.js +390 -0
  24. package/lib/esm/passkeys/types.js +1 -0
  25. package/lib/esm/provider/index.js +5 -2
  26. package/lifi-types.d.ts +1236 -0
  27. package/package.json +6 -3
  28. package/src/__mocks/constants.ts +422 -5
  29. package/src/__mocks/portal/mpc.ts +1 -0
  30. package/src/index.test.ts +179 -3
  31. package/src/index.ts +212 -4
  32. package/src/integrations/security/hypernative/index.test.ts +196 -0
  33. package/src/integrations/security/hypernative/index.ts +106 -0
  34. package/src/integrations/security/index.ts +14 -0
  35. package/src/integrations/trading/zero-x/index.test.ts +98 -19
  36. package/src/integrations/trading/zero-x/index.ts +29 -9
  37. package/src/mpc/index.test.ts +944 -7
  38. package/src/mpc/index.ts +200 -10
  39. package/src/passkeys/index.ts +536 -0
  40. package/src/passkeys/types.ts +78 -0
  41. package/src/provider/index.ts +5 -0
  42. package/tsconfig.json +7 -1
  43. package/types.d.ts +45 -12
  44. package/yieldxyz-types.d.ts +778 -0
  45. package/zero-x.d.ts +204 -0
@@ -0,0 +1,394 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __rest = (this && this.__rest) || function (s, e) {
12
+ var t = {};
13
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
14
+ t[p] = s[p];
15
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
16
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
17
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
18
+ t[p[i]] = s[p[i]];
19
+ }
20
+ return t;
21
+ };
22
+ Object.defineProperty(exports, "__esModule", { value: true });
23
+ exports.PasskeyService = void 0;
24
+ class PasskeyService {
25
+ constructor({ defaultDomain, getJwt, fetchImpl }) {
26
+ this.defaultDomain = defaultDomain;
27
+ this.getJwt = getJwt;
28
+ this.fetchImpl = fetchImpl !== null && fetchImpl !== void 0 ? fetchImpl : fetch.bind(globalThis);
29
+ }
30
+ /**
31
+ * Generate MPC backup share and register a passkey in the top-level window
32
+ * while storing the provided encryption key with the relying party.
33
+ */
34
+ registerPasskeyAndStoreKey(params) {
35
+ return __awaiter(this, void 0, void 0, function* () {
36
+ const { customDomain, encryptionKey } = params;
37
+ const { origin, relyingPartyOrigins, relyingPartyId, relyingPartyName } = this.resolveParams(params);
38
+ const status = yield this.getStatus(origin, relyingPartyId);
39
+ const normalizedStatus = status === null || status === void 0 ? void 0 : status.toLowerCase();
40
+ const shouldRegister = !normalizedStatus ||
41
+ normalizedStatus === 'not registered' ||
42
+ normalizedStatus === 'registered';
43
+ if (!shouldRegister) {
44
+ yield this.loginAndStoreKey({
45
+ customDomain,
46
+ relyingPartyId,
47
+ relyingPartyOrigins,
48
+ encryptionKey,
49
+ });
50
+ return;
51
+ }
52
+ const begin = yield this.postJson(`${origin}/passkeys/begin-registration`, {
53
+ relyingParty: relyingPartyId,
54
+ relyingPartyName,
55
+ relyingPartyOrigins,
56
+ });
57
+ const credential = yield this.createCredential(begin.options.publicKey);
58
+ const attestation = serializeAttestationCredential(credential);
59
+ yield this.postJsonVoid(`${origin}/passkeys/finish-registration`, {
60
+ sessionId: begin.sessionId,
61
+ relyingParty: relyingPartyId,
62
+ relyingPartyOrigins,
63
+ attestation,
64
+ encryptionKey,
65
+ });
66
+ });
67
+ }
68
+ /**
69
+ * Authenticate with passkey in the current browsing context and return the encryption key.
70
+ */
71
+ authenticatePasskeyAndRetrieveKey(params) {
72
+ return __awaiter(this, void 0, void 0, function* () {
73
+ const { origin, relyingPartyOrigins, relyingPartyId, relyingPartyName } = this.resolveParams(params);
74
+ const begin = yield this.postJson(`${origin}/passkeys/begin-authentication`, {
75
+ relyingParty: relyingPartyId,
76
+ relyingPartyName,
77
+ relyingPartyOrigins,
78
+ });
79
+ const credential = yield this.getCredential(begin.options.publicKey);
80
+ const assertion = serializeAssertionCredential(credential);
81
+ const finish = yield this.postJson(`${origin}/passkeys/finish-authentication`, {
82
+ sessionId: begin.sessionId,
83
+ relyingParty: relyingPartyId,
84
+ relyingPartyOrigins,
85
+ assertion,
86
+ });
87
+ if (!(finish === null || finish === void 0 ? void 0 : finish.encryptionKey)) {
88
+ throw new Error('Passkey authentication succeeded but no encryption key was returned.');
89
+ }
90
+ return finish.encryptionKey;
91
+ });
92
+ }
93
+ /**
94
+ * Register a passkey without tying it to an encryption key.
95
+ * The encryption key can be stored later using authenticatePasskeyAndWriteKey.
96
+ */
97
+ registerPasskey(params) {
98
+ return __awaiter(this, void 0, void 0, function* () {
99
+ const { origin, relyingPartyOrigins, relyingPartyId, relyingPartyName } = this.resolveParams(params);
100
+ const status = yield this.getStatus(origin, relyingPartyId);
101
+ const normalizedStatus = status === null || status === void 0 ? void 0 : status.toLowerCase();
102
+ if (normalizedStatus && normalizedStatus !== 'not registered' && normalizedStatus !== 'registered') {
103
+ throw new Error(`Passkey already exists with status: ${status}. Cannot register new credential.`);
104
+ }
105
+ const begin = yield this.postJson(`${origin}/passkeys/begin-credential-registration`, {
106
+ relyingParty: relyingPartyId,
107
+ relyingPartyName,
108
+ relyingPartyOrigins,
109
+ });
110
+ const credential = yield this.createCredential(begin.options.publicKey);
111
+ const attestation = serializeAttestationCredential(credential);
112
+ yield this.postJsonVoid(`${origin}/passkeys/finish-credential-registration`, {
113
+ sessionId: begin.sessionId,
114
+ relyingParty: relyingPartyId,
115
+ relyingPartyOrigins,
116
+ attestation,
117
+ });
118
+ });
119
+ }
120
+ /**
121
+ * Authenticate with passkey and store an encryption key.
122
+ * Used after registerPasskey to associate an encryption key with the passkey.
123
+ */
124
+ authenticatePasskeyAndWriteKey(params) {
125
+ return __awaiter(this, void 0, void 0, function* () {
126
+ const { encryptionKey } = params;
127
+ const { origin, relyingPartyOrigins, relyingPartyId, relyingPartyName } = this.resolveParams(params);
128
+ const begin = yield this.postJson(`${origin}/passkeys/begin-login`, {
129
+ relyingParty: relyingPartyId,
130
+ relyingPartyName,
131
+ relyingPartyOrigins,
132
+ });
133
+ const credential = yield this.getCredential(begin.options.publicKey);
134
+ const assertion = serializeAssertionCredential(credential);
135
+ yield this.postJsonVoid(`${origin}/passkeys/finish-login/write`, {
136
+ sessionId: begin.sessionId,
137
+ relyingParty: relyingPartyId,
138
+ relyingPartyOrigins,
139
+ assertion,
140
+ encryptionKey,
141
+ });
142
+ });
143
+ }
144
+ /**
145
+ * Utility used by combined backup flow to produce reusable payloads.
146
+ */
147
+ generateBackupSharePayload(cipherText, encryptionKey) {
148
+ return {
149
+ cipherText,
150
+ encryptionKey,
151
+ };
152
+ }
153
+ createCredential(options) {
154
+ return __awaiter(this, void 0, void 0, function* () {
155
+ const publicKey = decodeCreationOptions(options);
156
+ const credential = (yield navigator.credentials.create({
157
+ publicKey,
158
+ }));
159
+ if (!credential) {
160
+ throw new Error('Passkey registration was cancelled or failed to create credential.');
161
+ }
162
+ return credential;
163
+ });
164
+ }
165
+ loginAndStoreKey(params) {
166
+ return __awaiter(this, void 0, void 0, function* () {
167
+ const { customDomain, relyingPartyId, relyingPartyOrigins, encryptionKey } = params;
168
+ const origin = this.normalizeDomain(customDomain);
169
+ const begin = yield this.postJson(`${origin}/passkeys/begin-login`, {
170
+ relyingParty: relyingPartyId,
171
+ relyingPartyOrigins,
172
+ });
173
+ const credential = yield this.getCredential(begin.options.publicKey);
174
+ const assertion = serializeAssertionCredential(credential);
175
+ yield this.postJsonVoid(`${origin}/passkeys/finish-login/write`, {
176
+ sessionId: begin.sessionId,
177
+ relyingParty: relyingPartyId,
178
+ relyingPartyOrigins,
179
+ assertion,
180
+ encryptionKey,
181
+ });
182
+ });
183
+ }
184
+ getStatus(origin, relyingPartyId) {
185
+ return __awaiter(this, void 0, void 0, function* () {
186
+ try {
187
+ const query = new URLSearchParams({ relyingParty: relyingPartyId });
188
+ const url = `${origin}/passkeys/status?${query.toString()}`;
189
+ const response = yield this.fetchImpl(url, {
190
+ method: 'GET',
191
+ headers: yield this.buildHeaders(),
192
+ });
193
+ if (!response.ok) {
194
+ return undefined;
195
+ }
196
+ const raw = yield response.text();
197
+ if (!raw) {
198
+ return undefined;
199
+ }
200
+ const data = JSON.parse(raw);
201
+ return data.status;
202
+ }
203
+ catch (error) {
204
+ console.warn('[Portal] Failed to fetch passkey status', error);
205
+ return undefined;
206
+ }
207
+ });
208
+ }
209
+ getCredential(options) {
210
+ return __awaiter(this, void 0, void 0, function* () {
211
+ const publicKey = decodeRequestOptions(options);
212
+ const credential = (yield navigator.credentials.get({
213
+ publicKey,
214
+ }));
215
+ if (!credential) {
216
+ throw new Error('Passkey authentication was cancelled or failed to resolve credential.');
217
+ }
218
+ return credential;
219
+ });
220
+ }
221
+ normalizeDomain(customDomain) {
222
+ const domain = customDomain !== null && customDomain !== void 0 ? customDomain : this.defaultDomain;
223
+ if (!domain) {
224
+ throw new Error('Passkey domain is not configured.');
225
+ }
226
+ if (domain.startsWith('http://') || domain.startsWith('https://')) {
227
+ return domain;
228
+ }
229
+ return `https://${domain}`;
230
+ }
231
+ buildRelyingPartyOrigins(origin) {
232
+ var _a;
233
+ const set = new Set([origin]);
234
+ if (typeof window !== 'undefined' && ((_a = window.location) === null || _a === void 0 ? void 0 : _a.origin)) {
235
+ set.add(window.location.origin);
236
+ }
237
+ return Array.from(set);
238
+ }
239
+ resolveParams(options) {
240
+ const origin = this.normalizeDomain(options.customDomain);
241
+ const relyingPartyOrigins = this.buildRelyingPartyOrigins(origin);
242
+ return {
243
+ origin,
244
+ relyingPartyOrigins,
245
+ relyingPartyId: options.relyingPartyId,
246
+ relyingPartyName: options.relyingPartyName,
247
+ };
248
+ }
249
+ postJson(url, body) {
250
+ return __awaiter(this, void 0, void 0, function* () {
251
+ const response = yield this.fetchImpl(url, {
252
+ method: 'POST',
253
+ headers: yield this.buildHeaders(),
254
+ body: JSON.stringify(body),
255
+ });
256
+ if (!response.ok) {
257
+ const text = yield safeReadText(response);
258
+ throw new Error(`Passkey request failed (${response.status} ${response.statusText}): ${text}`);
259
+ }
260
+ return response.json();
261
+ });
262
+ }
263
+ postJsonVoid(url, body) {
264
+ return __awaiter(this, void 0, void 0, function* () {
265
+ const response = yield this.fetchImpl(url, {
266
+ method: 'POST',
267
+ headers: yield this.buildHeaders(),
268
+ body: JSON.stringify(body),
269
+ });
270
+ if (!response.ok) {
271
+ const text = yield safeReadText(response);
272
+ throw new Error(`Passkey request failed (${response.status} ${response.statusText}): ${text}`);
273
+ }
274
+ });
275
+ }
276
+ buildHeaders() {
277
+ return __awaiter(this, void 0, void 0, function* () {
278
+ const jwt = yield this.getJwt();
279
+ return {
280
+ 'Content-Type': 'application/json',
281
+ Authorization: `Bearer ${jwt}`,
282
+ };
283
+ });
284
+ }
285
+ }
286
+ exports.PasskeyService = PasskeyService;
287
+ const decodeCreationOptions = (options) => {
288
+ const { excludeCredentials } = options, rest = __rest(options, ["excludeCredentials"]);
289
+ const publicKey = Object.assign(Object.assign({}, rest), { challenge: base64UrlToArrayBuffer(options.challenge), user: Object.assign(Object.assign({}, options.user), { id: base64UrlToArrayBuffer(options.user.id) }) });
290
+ if (excludeCredentials) {
291
+ publicKey.excludeCredentials = excludeCredentials.map((credential) => ({
292
+ type: credential.type,
293
+ id: base64UrlToArrayBuffer(credential.id),
294
+ transports: credential.transports,
295
+ }));
296
+ }
297
+ return publicKey;
298
+ };
299
+ const decodeRequestOptions = (options) => {
300
+ const { allowCredentials } = options, rest = __rest(options, ["allowCredentials"]);
301
+ const publicKey = Object.assign(Object.assign({}, rest), { challenge: base64UrlToArrayBuffer(options.challenge) });
302
+ if (allowCredentials) {
303
+ publicKey.allowCredentials = allowCredentials.map((credential) => ({
304
+ type: credential.type,
305
+ id: base64UrlToArrayBuffer(credential.id),
306
+ transports: credential.transports,
307
+ }));
308
+ }
309
+ return publicKey;
310
+ };
311
+ const serializeAttestationCredential = (credential) => {
312
+ var _a;
313
+ const { response } = credential;
314
+ const attestationResponse = response;
315
+ const payload = {
316
+ id: credential.id,
317
+ rawId: arrayBufferToBase64Url(credential.rawId),
318
+ type: credential.type,
319
+ response: {
320
+ clientDataJSON: arrayBufferToBase64Url(attestationResponse.clientDataJSON),
321
+ attestationObject: attestationResponse.attestationObject
322
+ ? arrayBufferToBase64Url(attestationResponse.attestationObject)
323
+ : undefined,
324
+ transports: (_a = attestationResponse.getTransports) === null || _a === void 0 ? void 0 : _a.call(attestationResponse),
325
+ },
326
+ clientExtensionResults: credential.getClientExtensionResults(),
327
+ };
328
+ return JSON.stringify(payload);
329
+ };
330
+ const serializeAssertionCredential = (credential) => {
331
+ const { response } = credential;
332
+ const assertionResponse = response;
333
+ const payload = {
334
+ id: credential.id,
335
+ rawId: arrayBufferToBase64Url(credential.rawId),
336
+ type: credential.type,
337
+ response: {
338
+ clientDataJSON: arrayBufferToBase64Url(assertionResponse.clientDataJSON),
339
+ authenticatorData: arrayBufferToBase64Url(assertionResponse.authenticatorData),
340
+ signature: arrayBufferToBase64Url(assertionResponse.signature),
341
+ userHandle: assertionResponse.userHandle
342
+ ? arrayBufferToBase64Url(assertionResponse.userHandle)
343
+ : null,
344
+ },
345
+ clientExtensionResults: credential.getClientExtensionResults(),
346
+ };
347
+ return JSON.stringify(payload);
348
+ };
349
+ const base64UrlToArrayBuffer = (value) => {
350
+ const normalized = value.replace(/-/g, '+').replace(/_/g, '/');
351
+ const padded = normalized.padEnd(normalized.length + ((4 - (normalized.length % 4)) % 4), '=');
352
+ const binary = typeof atob === 'function'
353
+ ? atob(padded)
354
+ : decodeWithBufferFallback(padded);
355
+ const bytes = new Uint8Array(binary.length);
356
+ for (let i = 0; i < binary.length; i++) {
357
+ bytes[i] = binary.charCodeAt(i);
358
+ }
359
+ return bytes.buffer;
360
+ };
361
+ const arrayBufferToBase64Url = (buffer) => {
362
+ const bytes = new Uint8Array(buffer);
363
+ let binary = '';
364
+ for (let i = 0; i < bytes.byteLength; i++) {
365
+ binary += String.fromCharCode(bytes[i]);
366
+ }
367
+ const base64 = typeof btoa === 'function'
368
+ ? btoa(binary)
369
+ : encodeWithBufferFallback(binary);
370
+ return base64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
371
+ };
372
+ const safeReadText = (response) => __awaiter(void 0, void 0, void 0, function* () {
373
+ try {
374
+ return yield response.text();
375
+ }
376
+ catch (error) {
377
+ return error.message;
378
+ }
379
+ });
380
+ exports.default = PasskeyService;
381
+ const decodeWithBufferFallback = (value) => {
382
+ const bufferCtor = globalThis.Buffer;
383
+ if (bufferCtor) {
384
+ return bufferCtor.from(value, 'base64').toString('binary');
385
+ }
386
+ throw new Error('Base64 decoding is not supported in this environment.');
387
+ };
388
+ const encodeWithBufferFallback = (binary) => {
389
+ const bufferCtor = globalThis.Buffer;
390
+ if (bufferCtor) {
391
+ return bufferCtor.from(binary, 'binary').toString('base64');
392
+ }
393
+ throw new Error('Base64 encoding is not supported in this environment.');
394
+ };
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -260,7 +260,7 @@ class Provider {
260
260
  * @param args The arguments of the request being made
261
261
  * @returns Promise<any>
262
262
  */
263
- request({ chainId: requestChainId, method, params, }) {
263
+ request({ chainId: requestChainId, method, params, sponsorGas, }) {
264
264
  return __awaiter(this, void 0, void 0, function* () {
265
265
  const isSignerMethod = signerMethods.includes(method);
266
266
  const chainId = this.getCAIP2ChainId(requestChainId);
@@ -288,6 +288,7 @@ class Provider {
288
288
  chainId,
289
289
  method,
290
290
  params,
291
+ sponsorGas,
291
292
  });
292
293
  if (transactionHash) {
293
294
  this.emit('portal_signatureReceived', {
@@ -403,7 +404,7 @@ class Provider {
403
404
  * @param args The arguments of the request being made
404
405
  * @returns Promise<any>
405
406
  */
406
- handleSigningRequest({ chainId, method, params, }) {
407
+ handleSigningRequest({ chainId, method, params, sponsorGas, }) {
407
408
  return __awaiter(this, void 0, void 0, function* () {
408
409
  const isApproved = passiveSignerMethods.includes(method)
409
410
  ? true
@@ -435,6 +436,7 @@ class Provider {
435
436
  method,
436
437
  params: this.buildParams(method, params),
437
438
  rpcUrl: this.portal.getRpcUrl(chainId),
439
+ sponsorGas,
438
440
  });
439
441
  return result;
440
442
  }
@@ -448,6 +450,7 @@ class Provider {
448
450
  method,
449
451
  params: this.buildParams(method, params),
450
452
  rpcUrl: this.portal.getRpcUrl(chainId),
453
+ sponsorGas,
451
454
  });
452
455
  return result;
453
456
  }
package/lib/esm/index.js CHANGED
@@ -12,6 +12,8 @@ import Mpc from './mpc';
12
12
  import Provider, { RequestMethod } from './provider';
13
13
  import Yield from './integrations/yield';
14
14
  import Trading from './integrations/trading';
15
+ import Security from './integrations/security';
16
+ import PasskeyService from './passkeys';
15
17
  class Portal {
16
18
  get ready() {
17
19
  return this.mpc.ready;
@@ -56,6 +58,7 @@ class Portal {
56
58
  });
57
59
  this.yield = new Yield({ mpc: this.mpc });
58
60
  this.trading = new Trading({ mpc: this.mpc });
61
+ this.security = new Security({ mpc: this.mpc });
59
62
  this.provider = new Provider({
60
63
  portal: this,
61
64
  chainId: chainId ? Number(chainId) : undefined,
@@ -119,6 +122,98 @@ class Portal {
119
122
  return address;
120
123
  });
121
124
  }
125
+ generateBackupShare(progress = () => {
126
+ // Noop
127
+ }) {
128
+ return __awaiter(this, void 0, void 0, function* () {
129
+ const response = yield this.mpc.backup({
130
+ backupMethod: BackupMethods.custom,
131
+ backupConfigs: {},
132
+ host: this.host,
133
+ mpcVersion: this.mpcVersion,
134
+ featureFlags: this.featureFlags,
135
+ }, progress);
136
+ if (!response.encryptionKey) {
137
+ throw new Error('[Portal] Custom backup did not return an encryption key. Please ensure you are using the latest iframe bundle.');
138
+ }
139
+ return {
140
+ cipherText: response.cipherText,
141
+ encryptionKey: response.encryptionKey,
142
+ };
143
+ });
144
+ }
145
+ registerPasskeyAndStoreEncryptionKey(cipherText, encryptionKey, options = {}) {
146
+ return __awaiter(this, void 0, void 0, function* () {
147
+ const { service, customDomain, relyingPartyId, relyingPartyName } = this.resolvePasskeyOptions(options);
148
+ yield service.registerPasskeyAndStoreKey({
149
+ customDomain,
150
+ relyingPartyName,
151
+ encryptionKey,
152
+ relyingPartyId,
153
+ cipherText,
154
+ });
155
+ });
156
+ }
157
+ authenticatePasskeyAndRetrieveKey(options = {}) {
158
+ return __awaiter(this, void 0, void 0, function* () {
159
+ const { service, customDomain, relyingPartyId, relyingPartyName } = this.resolvePasskeyOptions(options);
160
+ return yield service.authenticatePasskeyAndRetrieveKey({
161
+ customDomain,
162
+ relyingPartyName,
163
+ relyingPartyId,
164
+ });
165
+ });
166
+ }
167
+ /**
168
+ * Register a passkey without tying it to an encryption key.
169
+ * The encryption key can be stored later using authenticatePasskeyAndWriteKey.
170
+ */
171
+ registerPasskey(options = {}) {
172
+ return __awaiter(this, void 0, void 0, function* () {
173
+ const { service, customDomain, relyingPartyId, relyingPartyName } = this.resolvePasskeyOptions(options);
174
+ yield service.registerPasskey({
175
+ customDomain,
176
+ relyingPartyName,
177
+ relyingPartyId,
178
+ });
179
+ });
180
+ }
181
+ /**
182
+ * Authenticate with passkey and store an encryption key.
183
+ * Used after registerPasskey to associate an encryption key with the passkey.
184
+ */
185
+ authenticatePasskeyAndWriteKey(encryptionKey, options = {}) {
186
+ return __awaiter(this, void 0, void 0, function* () {
187
+ const { service, customDomain, relyingPartyId, relyingPartyName } = this.resolvePasskeyOptions(options);
188
+ yield service.authenticatePasskeyAndWriteKey({
189
+ customDomain,
190
+ relyingPartyName,
191
+ relyingPartyId,
192
+ encryptionKey,
193
+ });
194
+ });
195
+ }
196
+ backupWithPasskey(options = {}, progress = () => {
197
+ // Noop
198
+ }) {
199
+ return __awaiter(this, void 0, void 0, function* () {
200
+ const { usePopup = true, customDomain, relyingPartyName, backupMethod = BackupMethods.passkey, } = options;
201
+ if (usePopup) {
202
+ yield this.backupWallet(backupMethod, progress, {});
203
+ return;
204
+ }
205
+ if (backupMethod !== BackupMethods.passkey) {
206
+ throw new Error(`[Portal] Direct passkey backup currently supports only BackupMethods.passkey (received ${backupMethod}).`);
207
+ }
208
+ const { cipherText, encryptionKey } = yield this.generateBackupShare(progress);
209
+ yield this.registerPasskeyAndStoreEncryptionKey(cipherText, encryptionKey, {
210
+ customDomain,
211
+ relyingPartyName,
212
+ usePopup,
213
+ });
214
+ yield this.storedClientBackupShare(true, BackupMethods.passkey);
215
+ });
216
+ }
122
217
  backupWallet(backupMethod, progress = () => {
123
218
  // Noop
124
219
  }, backupConfigs = {}) {
@@ -332,6 +427,7 @@ class Portal {
332
427
  chainId,
333
428
  method: 'eth_sendTransaction',
334
429
  params: [buildTxResponse.transaction],
430
+ sponsorGas: params.sponsorGas,
335
431
  });
336
432
  break;
337
433
  }
@@ -340,6 +436,7 @@ class Portal {
340
436
  chainId,
341
437
  method: 'sol_signAndSendTransaction',
342
438
  params: [buildTxResponse.transaction],
439
+ sponsorGas: params.sponsorGas,
343
440
  });
344
441
  break;
345
442
  }
@@ -682,7 +779,7 @@ class Portal {
682
779
  getQuote(apiKey, args, chainId) {
683
780
  var _a;
684
781
  return __awaiter(this, void 0, void 0, function* () {
685
- return (_a = this.mpc) === null || _a === void 0 ? void 0 : _a.getQuote(apiKey, args, chainId);
782
+ return (_a = this.mpc) === null || _a === void 0 ? void 0 : _a.getQuote(chainId, args, apiKey);
686
783
  });
687
784
  }
688
785
  /**
@@ -691,7 +788,7 @@ class Portal {
691
788
  getSources(apiKey, chainId) {
692
789
  var _a;
693
790
  return __awaiter(this, void 0, void 0, function* () {
694
- return (_a = this.mpc) === null || _a === void 0 ? void 0 : _a.getSources(apiKey, chainId);
791
+ return (_a = this.mpc) === null || _a === void 0 ? void 0 : _a.getSources(chainId, apiKey);
695
792
  });
696
793
  }
697
794
  /*******************************
@@ -779,6 +876,50 @@ class Portal {
779
876
  }
780
877
  return chainId;
781
878
  }
879
+ ensurePasskeyService() {
880
+ const defaultDomain = this.getDefaultPasskeyDomain();
881
+ if (!this.passkeyService ||
882
+ this.passkeyServiceDefaultDomain !== defaultDomain) {
883
+ this.passkeyService = new PasskeyService({
884
+ defaultDomain,
885
+ getJwt: () => this.mpc.getPasskeyJwt(),
886
+ });
887
+ this.passkeyServiceDefaultDomain = defaultDomain;
888
+ }
889
+ return this.passkeyService;
890
+ }
891
+ getDefaultPasskeyDomain() {
892
+ var _a, _b;
893
+ return (_b = (_a = this.passkeyConfig) === null || _a === void 0 ? void 0 : _a.webAuthnHost) !== null && _b !== void 0 ? _b : 'backup.web.portalhq.io';
894
+ }
895
+ extractRpId(domain) {
896
+ const targetDomain = domain !== null && domain !== void 0 ? domain : this.getDefaultPasskeyDomain();
897
+ try {
898
+ const normalized = targetDomain.startsWith('http')
899
+ ? targetDomain
900
+ : `https://${targetDomain}`;
901
+ return new URL(normalized).hostname;
902
+ }
903
+ catch (_a) {
904
+ return targetDomain;
905
+ }
906
+ }
907
+ resolvePasskeyOptions(options) {
908
+ var _a, _b, _c, _d, _e;
909
+ if (options.usePopup) {
910
+ throw new Error('[Portal] This method does not support the popup flow. Use usePopup: false.');
911
+ }
912
+ const service = this.ensurePasskeyService();
913
+ const domain = (_a = options.customDomain) !== null && _a !== void 0 ? _a : this.getDefaultPasskeyDomain();
914
+ const relyingPartyId = (_b = options.relyingPartyId) !== null && _b !== void 0 ? _b : this.extractRpId(domain);
915
+ const relyingPartyName = (_e = (_c = options.relyingPartyName) !== null && _c !== void 0 ? _c : (_d = this.passkeyConfig) === null || _d === void 0 ? void 0 : _d.relyingParty) !== null && _e !== void 0 ? _e : 'Portal';
916
+ return {
917
+ service,
918
+ customDomain: options.customDomain,
919
+ relyingPartyId,
920
+ relyingPartyName,
921
+ };
922
+ }
782
923
  }
783
924
  export { MpcError, MpcErrorCodes } from './mpc';
784
925
  export { PortalMpcError } from './mpc/errors';
@@ -800,6 +941,7 @@ export var BackupMethods;
800
941
  BackupMethods["gdrive"] = "GDRIVE";
801
942
  BackupMethods["password"] = "PASSWORD";
802
943
  BackupMethods["passkey"] = "PASSKEY";
944
+ BackupMethods["custom"] = "CUSTOM";
803
945
  BackupMethods["unknown"] = "UNKNOWN";
804
946
  })(BackupMethods || (BackupMethods = {}));
805
947
  export var GetTransactionsOrder;