@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,390 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ var __rest = (this && this.__rest) || function (s, e) {
11
+ var t = {};
12
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
13
+ t[p] = s[p];
14
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
15
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
16
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
17
+ t[p[i]] = s[p[i]];
18
+ }
19
+ return t;
20
+ };
21
+ export class PasskeyService {
22
+ constructor({ defaultDomain, getJwt, fetchImpl }) {
23
+ this.defaultDomain = defaultDomain;
24
+ this.getJwt = getJwt;
25
+ this.fetchImpl = fetchImpl !== null && fetchImpl !== void 0 ? fetchImpl : fetch.bind(globalThis);
26
+ }
27
+ /**
28
+ * Generate MPC backup share and register a passkey in the top-level window
29
+ * while storing the provided encryption key with the relying party.
30
+ */
31
+ registerPasskeyAndStoreKey(params) {
32
+ return __awaiter(this, void 0, void 0, function* () {
33
+ const { customDomain, encryptionKey } = params;
34
+ const { origin, relyingPartyOrigins, relyingPartyId, relyingPartyName } = this.resolveParams(params);
35
+ const status = yield this.getStatus(origin, relyingPartyId);
36
+ const normalizedStatus = status === null || status === void 0 ? void 0 : status.toLowerCase();
37
+ const shouldRegister = !normalizedStatus ||
38
+ normalizedStatus === 'not registered' ||
39
+ normalizedStatus === 'registered';
40
+ if (!shouldRegister) {
41
+ yield this.loginAndStoreKey({
42
+ customDomain,
43
+ relyingPartyId,
44
+ relyingPartyOrigins,
45
+ encryptionKey,
46
+ });
47
+ return;
48
+ }
49
+ const begin = yield this.postJson(`${origin}/passkeys/begin-registration`, {
50
+ relyingParty: relyingPartyId,
51
+ relyingPartyName,
52
+ relyingPartyOrigins,
53
+ });
54
+ const credential = yield this.createCredential(begin.options.publicKey);
55
+ const attestation = serializeAttestationCredential(credential);
56
+ yield this.postJsonVoid(`${origin}/passkeys/finish-registration`, {
57
+ sessionId: begin.sessionId,
58
+ relyingParty: relyingPartyId,
59
+ relyingPartyOrigins,
60
+ attestation,
61
+ encryptionKey,
62
+ });
63
+ });
64
+ }
65
+ /**
66
+ * Authenticate with passkey in the current browsing context and return the encryption key.
67
+ */
68
+ authenticatePasskeyAndRetrieveKey(params) {
69
+ return __awaiter(this, void 0, void 0, function* () {
70
+ const { origin, relyingPartyOrigins, relyingPartyId, relyingPartyName } = this.resolveParams(params);
71
+ const begin = yield this.postJson(`${origin}/passkeys/begin-authentication`, {
72
+ relyingParty: relyingPartyId,
73
+ relyingPartyName,
74
+ relyingPartyOrigins,
75
+ });
76
+ const credential = yield this.getCredential(begin.options.publicKey);
77
+ const assertion = serializeAssertionCredential(credential);
78
+ const finish = yield this.postJson(`${origin}/passkeys/finish-authentication`, {
79
+ sessionId: begin.sessionId,
80
+ relyingParty: relyingPartyId,
81
+ relyingPartyOrigins,
82
+ assertion,
83
+ });
84
+ if (!(finish === null || finish === void 0 ? void 0 : finish.encryptionKey)) {
85
+ throw new Error('Passkey authentication succeeded but no encryption key was returned.');
86
+ }
87
+ return finish.encryptionKey;
88
+ });
89
+ }
90
+ /**
91
+ * Register a passkey without tying it to an encryption key.
92
+ * The encryption key can be stored later using authenticatePasskeyAndWriteKey.
93
+ */
94
+ registerPasskey(params) {
95
+ return __awaiter(this, void 0, void 0, function* () {
96
+ const { origin, relyingPartyOrigins, relyingPartyId, relyingPartyName } = this.resolveParams(params);
97
+ const status = yield this.getStatus(origin, relyingPartyId);
98
+ const normalizedStatus = status === null || status === void 0 ? void 0 : status.toLowerCase();
99
+ if (normalizedStatus && normalizedStatus !== 'not registered' && normalizedStatus !== 'registered') {
100
+ throw new Error(`Passkey already exists with status: ${status}. Cannot register new credential.`);
101
+ }
102
+ const begin = yield this.postJson(`${origin}/passkeys/begin-credential-registration`, {
103
+ relyingParty: relyingPartyId,
104
+ relyingPartyName,
105
+ relyingPartyOrigins,
106
+ });
107
+ const credential = yield this.createCredential(begin.options.publicKey);
108
+ const attestation = serializeAttestationCredential(credential);
109
+ yield this.postJsonVoid(`${origin}/passkeys/finish-credential-registration`, {
110
+ sessionId: begin.sessionId,
111
+ relyingParty: relyingPartyId,
112
+ relyingPartyOrigins,
113
+ attestation,
114
+ });
115
+ });
116
+ }
117
+ /**
118
+ * Authenticate with passkey and store an encryption key.
119
+ * Used after registerPasskey to associate an encryption key with the passkey.
120
+ */
121
+ authenticatePasskeyAndWriteKey(params) {
122
+ return __awaiter(this, void 0, void 0, function* () {
123
+ const { encryptionKey } = params;
124
+ const { origin, relyingPartyOrigins, relyingPartyId, relyingPartyName } = this.resolveParams(params);
125
+ const begin = yield this.postJson(`${origin}/passkeys/begin-login`, {
126
+ relyingParty: relyingPartyId,
127
+ relyingPartyName,
128
+ relyingPartyOrigins,
129
+ });
130
+ const credential = yield this.getCredential(begin.options.publicKey);
131
+ const assertion = serializeAssertionCredential(credential);
132
+ yield this.postJsonVoid(`${origin}/passkeys/finish-login/write`, {
133
+ sessionId: begin.sessionId,
134
+ relyingParty: relyingPartyId,
135
+ relyingPartyOrigins,
136
+ assertion,
137
+ encryptionKey,
138
+ });
139
+ });
140
+ }
141
+ /**
142
+ * Utility used by combined backup flow to produce reusable payloads.
143
+ */
144
+ generateBackupSharePayload(cipherText, encryptionKey) {
145
+ return {
146
+ cipherText,
147
+ encryptionKey,
148
+ };
149
+ }
150
+ createCredential(options) {
151
+ return __awaiter(this, void 0, void 0, function* () {
152
+ const publicKey = decodeCreationOptions(options);
153
+ const credential = (yield navigator.credentials.create({
154
+ publicKey,
155
+ }));
156
+ if (!credential) {
157
+ throw new Error('Passkey registration was cancelled or failed to create credential.');
158
+ }
159
+ return credential;
160
+ });
161
+ }
162
+ loginAndStoreKey(params) {
163
+ return __awaiter(this, void 0, void 0, function* () {
164
+ const { customDomain, relyingPartyId, relyingPartyOrigins, encryptionKey } = params;
165
+ const origin = this.normalizeDomain(customDomain);
166
+ const begin = yield this.postJson(`${origin}/passkeys/begin-login`, {
167
+ relyingParty: relyingPartyId,
168
+ relyingPartyOrigins,
169
+ });
170
+ const credential = yield this.getCredential(begin.options.publicKey);
171
+ const assertion = serializeAssertionCredential(credential);
172
+ yield this.postJsonVoid(`${origin}/passkeys/finish-login/write`, {
173
+ sessionId: begin.sessionId,
174
+ relyingParty: relyingPartyId,
175
+ relyingPartyOrigins,
176
+ assertion,
177
+ encryptionKey,
178
+ });
179
+ });
180
+ }
181
+ getStatus(origin, relyingPartyId) {
182
+ return __awaiter(this, void 0, void 0, function* () {
183
+ try {
184
+ const query = new URLSearchParams({ relyingParty: relyingPartyId });
185
+ const url = `${origin}/passkeys/status?${query.toString()}`;
186
+ const response = yield this.fetchImpl(url, {
187
+ method: 'GET',
188
+ headers: yield this.buildHeaders(),
189
+ });
190
+ if (!response.ok) {
191
+ return undefined;
192
+ }
193
+ const raw = yield response.text();
194
+ if (!raw) {
195
+ return undefined;
196
+ }
197
+ const data = JSON.parse(raw);
198
+ return data.status;
199
+ }
200
+ catch (error) {
201
+ console.warn('[Portal] Failed to fetch passkey status', error);
202
+ return undefined;
203
+ }
204
+ });
205
+ }
206
+ getCredential(options) {
207
+ return __awaiter(this, void 0, void 0, function* () {
208
+ const publicKey = decodeRequestOptions(options);
209
+ const credential = (yield navigator.credentials.get({
210
+ publicKey,
211
+ }));
212
+ if (!credential) {
213
+ throw new Error('Passkey authentication was cancelled or failed to resolve credential.');
214
+ }
215
+ return credential;
216
+ });
217
+ }
218
+ normalizeDomain(customDomain) {
219
+ const domain = customDomain !== null && customDomain !== void 0 ? customDomain : this.defaultDomain;
220
+ if (!domain) {
221
+ throw new Error('Passkey domain is not configured.');
222
+ }
223
+ if (domain.startsWith('http://') || domain.startsWith('https://')) {
224
+ return domain;
225
+ }
226
+ return `https://${domain}`;
227
+ }
228
+ buildRelyingPartyOrigins(origin) {
229
+ var _a;
230
+ const set = new Set([origin]);
231
+ if (typeof window !== 'undefined' && ((_a = window.location) === null || _a === void 0 ? void 0 : _a.origin)) {
232
+ set.add(window.location.origin);
233
+ }
234
+ return Array.from(set);
235
+ }
236
+ resolveParams(options) {
237
+ const origin = this.normalizeDomain(options.customDomain);
238
+ const relyingPartyOrigins = this.buildRelyingPartyOrigins(origin);
239
+ return {
240
+ origin,
241
+ relyingPartyOrigins,
242
+ relyingPartyId: options.relyingPartyId,
243
+ relyingPartyName: options.relyingPartyName,
244
+ };
245
+ }
246
+ postJson(url, body) {
247
+ return __awaiter(this, void 0, void 0, function* () {
248
+ const response = yield this.fetchImpl(url, {
249
+ method: 'POST',
250
+ headers: yield this.buildHeaders(),
251
+ body: JSON.stringify(body),
252
+ });
253
+ if (!response.ok) {
254
+ const text = yield safeReadText(response);
255
+ throw new Error(`Passkey request failed (${response.status} ${response.statusText}): ${text}`);
256
+ }
257
+ return response.json();
258
+ });
259
+ }
260
+ postJsonVoid(url, body) {
261
+ return __awaiter(this, void 0, void 0, function* () {
262
+ const response = yield this.fetchImpl(url, {
263
+ method: 'POST',
264
+ headers: yield this.buildHeaders(),
265
+ body: JSON.stringify(body),
266
+ });
267
+ if (!response.ok) {
268
+ const text = yield safeReadText(response);
269
+ throw new Error(`Passkey request failed (${response.status} ${response.statusText}): ${text}`);
270
+ }
271
+ });
272
+ }
273
+ buildHeaders() {
274
+ return __awaiter(this, void 0, void 0, function* () {
275
+ const jwt = yield this.getJwt();
276
+ return {
277
+ 'Content-Type': 'application/json',
278
+ Authorization: `Bearer ${jwt}`,
279
+ };
280
+ });
281
+ }
282
+ }
283
+ const decodeCreationOptions = (options) => {
284
+ const { excludeCredentials } = options, rest = __rest(options, ["excludeCredentials"]);
285
+ const publicKey = Object.assign(Object.assign({}, rest), { challenge: base64UrlToArrayBuffer(options.challenge), user: Object.assign(Object.assign({}, options.user), { id: base64UrlToArrayBuffer(options.user.id) }) });
286
+ if (excludeCredentials) {
287
+ publicKey.excludeCredentials = excludeCredentials.map((credential) => ({
288
+ type: credential.type,
289
+ id: base64UrlToArrayBuffer(credential.id),
290
+ transports: credential.transports,
291
+ }));
292
+ }
293
+ return publicKey;
294
+ };
295
+ const decodeRequestOptions = (options) => {
296
+ const { allowCredentials } = options, rest = __rest(options, ["allowCredentials"]);
297
+ const publicKey = Object.assign(Object.assign({}, rest), { challenge: base64UrlToArrayBuffer(options.challenge) });
298
+ if (allowCredentials) {
299
+ publicKey.allowCredentials = allowCredentials.map((credential) => ({
300
+ type: credential.type,
301
+ id: base64UrlToArrayBuffer(credential.id),
302
+ transports: credential.transports,
303
+ }));
304
+ }
305
+ return publicKey;
306
+ };
307
+ const serializeAttestationCredential = (credential) => {
308
+ var _a;
309
+ const { response } = credential;
310
+ const attestationResponse = response;
311
+ const payload = {
312
+ id: credential.id,
313
+ rawId: arrayBufferToBase64Url(credential.rawId),
314
+ type: credential.type,
315
+ response: {
316
+ clientDataJSON: arrayBufferToBase64Url(attestationResponse.clientDataJSON),
317
+ attestationObject: attestationResponse.attestationObject
318
+ ? arrayBufferToBase64Url(attestationResponse.attestationObject)
319
+ : undefined,
320
+ transports: (_a = attestationResponse.getTransports) === null || _a === void 0 ? void 0 : _a.call(attestationResponse),
321
+ },
322
+ clientExtensionResults: credential.getClientExtensionResults(),
323
+ };
324
+ return JSON.stringify(payload);
325
+ };
326
+ const serializeAssertionCredential = (credential) => {
327
+ const { response } = credential;
328
+ const assertionResponse = response;
329
+ const payload = {
330
+ id: credential.id,
331
+ rawId: arrayBufferToBase64Url(credential.rawId),
332
+ type: credential.type,
333
+ response: {
334
+ clientDataJSON: arrayBufferToBase64Url(assertionResponse.clientDataJSON),
335
+ authenticatorData: arrayBufferToBase64Url(assertionResponse.authenticatorData),
336
+ signature: arrayBufferToBase64Url(assertionResponse.signature),
337
+ userHandle: assertionResponse.userHandle
338
+ ? arrayBufferToBase64Url(assertionResponse.userHandle)
339
+ : null,
340
+ },
341
+ clientExtensionResults: credential.getClientExtensionResults(),
342
+ };
343
+ return JSON.stringify(payload);
344
+ };
345
+ const base64UrlToArrayBuffer = (value) => {
346
+ const normalized = value.replace(/-/g, '+').replace(/_/g, '/');
347
+ const padded = normalized.padEnd(normalized.length + ((4 - (normalized.length % 4)) % 4), '=');
348
+ const binary = typeof atob === 'function'
349
+ ? atob(padded)
350
+ : decodeWithBufferFallback(padded);
351
+ const bytes = new Uint8Array(binary.length);
352
+ for (let i = 0; i < binary.length; i++) {
353
+ bytes[i] = binary.charCodeAt(i);
354
+ }
355
+ return bytes.buffer;
356
+ };
357
+ const arrayBufferToBase64Url = (buffer) => {
358
+ const bytes = new Uint8Array(buffer);
359
+ let binary = '';
360
+ for (let i = 0; i < bytes.byteLength; i++) {
361
+ binary += String.fromCharCode(bytes[i]);
362
+ }
363
+ const base64 = typeof btoa === 'function'
364
+ ? btoa(binary)
365
+ : encodeWithBufferFallback(binary);
366
+ return base64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
367
+ };
368
+ const safeReadText = (response) => __awaiter(void 0, void 0, void 0, function* () {
369
+ try {
370
+ return yield response.text();
371
+ }
372
+ catch (error) {
373
+ return error.message;
374
+ }
375
+ });
376
+ export default PasskeyService;
377
+ const decodeWithBufferFallback = (value) => {
378
+ const bufferCtor = globalThis.Buffer;
379
+ if (bufferCtor) {
380
+ return bufferCtor.from(value, 'base64').toString('binary');
381
+ }
382
+ throw new Error('Base64 decoding is not supported in this environment.');
383
+ };
384
+ const encodeWithBufferFallback = (binary) => {
385
+ const bufferCtor = globalThis.Buffer;
386
+ if (bufferCtor) {
387
+ return bufferCtor.from(binary, 'binary').toString('base64');
388
+ }
389
+ throw new Error('Base64 encoding is not supported in this environment.');
390
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -257,7 +257,7 @@ class Provider {
257
257
  * @param args The arguments of the request being made
258
258
  * @returns Promise<any>
259
259
  */
260
- request({ chainId: requestChainId, method, params, }) {
260
+ request({ chainId: requestChainId, method, params, sponsorGas, }) {
261
261
  return __awaiter(this, void 0, void 0, function* () {
262
262
  const isSignerMethod = signerMethods.includes(method);
263
263
  const chainId = this.getCAIP2ChainId(requestChainId);
@@ -285,6 +285,7 @@ class Provider {
285
285
  chainId,
286
286
  method,
287
287
  params,
288
+ sponsorGas,
288
289
  });
289
290
  if (transactionHash) {
290
291
  this.emit('portal_signatureReceived', {
@@ -400,7 +401,7 @@ class Provider {
400
401
  * @param args The arguments of the request being made
401
402
  * @returns Promise<any>
402
403
  */
403
- handleSigningRequest({ chainId, method, params, }) {
404
+ handleSigningRequest({ chainId, method, params, sponsorGas, }) {
404
405
  return __awaiter(this, void 0, void 0, function* () {
405
406
  const isApproved = passiveSignerMethods.includes(method)
406
407
  ? true
@@ -432,6 +433,7 @@ class Provider {
432
433
  method,
433
434
  params: this.buildParams(method, params),
434
435
  rpcUrl: this.portal.getRpcUrl(chainId),
436
+ sponsorGas,
435
437
  });
436
438
  return result;
437
439
  }
@@ -445,6 +447,7 @@ class Provider {
445
447
  method,
446
448
  params: this.buildParams(method, params),
447
449
  rpcUrl: this.portal.getRpcUrl(chainId),
450
+ sponsorGas,
448
451
  });
449
452
  return result;
450
453
  }