@explorins/pers-signer 1.0.12 → 1.0.16

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.
@@ -48,7 +48,17 @@ class FetchHttpClient {
48
48
  setTimeout(() => controller.abort(), options.timeout);
49
49
  }
50
50
  const response = await fetch(url, fetchOptions);
51
- const data = await response.json();
51
+ // Check if response is actually JSON before parsing
52
+ const responseText = await response.text();
53
+ let data;
54
+ try {
55
+ data = JSON.parse(responseText);
56
+ }
57
+ catch (parseError) {
58
+ console.error('[HttpClient] JSON parse error:', parseError);
59
+ console.error('[HttpClient] Failed to parse response text:', responseText);
60
+ throw new Error(`Failed to parse JSON response: ${parseError instanceof Error ? parseError.message : 'Unknown error'}`);
61
+ }
52
62
  return {
53
63
  data,
54
64
  status: response.status,
@@ -158,74 +168,31 @@ function getServiceConfig() {
158
168
 
159
169
  /**
160
170
  * Wallet management service
161
- * Handles wallet listing and management operations
171
+ * Updated for v1 API endpoints per migration reference
162
172
  */
163
173
  class WalletService {
164
174
  /**
165
175
  * List all wallets for authenticated user
166
- * @param authToken - Authentication token
167
- * @returns Promise resolving to wallet list
176
+ * @param signerToken - Signer JWT token
177
+ * @returns Promise resolving to wallet data
168
178
  */
169
- static async listWallets(authToken) {
179
+ static async listWallets(signerToken) {
170
180
  const httpClient = getHttpClient();
171
181
  const config = getServiceConfig();
172
182
  try {
173
- const response = await httpClient.post(`${config.apiUrl}/wallets/list`, {
174
- authToken,
175
- appId: config.appId,
176
- });
177
- console.log('[WalletService] Raw API response:', response.data);
178
- return response.data;
183
+ const requestBody = {};
184
+ const response = await httpClient.post(`${config.apiUrl}/wallets/list`, requestBody, { Authorization: `Bearer ${signerToken}` });
185
+ const backendResponse = response.data;
186
+ if (backendResponse.success && backendResponse.data) {
187
+ return backendResponse.data;
188
+ }
189
+ throw new Error('Failed to list wallets: Invalid response');
179
190
  }
180
191
  catch (error) {
181
192
  console.error('[WalletService] Error listing wallets:', error);
182
193
  throw error;
183
194
  }
184
195
  }
185
- /**
186
- * Get wallet by ID (when backend supports it)
187
- * @param authToken - Authentication token
188
- * @param walletId - Wallet identifier
189
- * @returns Promise resolving to wallet details
190
- */
191
- static async getWallet(authToken, walletId) {
192
- const httpClient = getHttpClient();
193
- const config = getServiceConfig();
194
- try {
195
- const response = await httpClient.post(`${config.apiUrl}/wallets/get`, {
196
- authToken,
197
- walletId,
198
- appId: config.appId,
199
- });
200
- return response.data;
201
- }
202
- catch (error) {
203
- console.error(`[WalletService] Error getting wallet ${walletId}:`, error);
204
- throw error;
205
- }
206
- }
207
- /**
208
- * Create new wallet (when backend supports it)
209
- * @param authToken - Authentication token
210
- * @param walletConfig - Wallet configuration
211
- * @returns Promise resolving to wallet creation result
212
- */
213
- static async createWallet(authToken, walletConfig) {
214
- const httpClient = getHttpClient();
215
- const config = getServiceConfig();
216
- try {
217
- const response = await httpClient.post(`${config.apiUrl}/wallets/create`, {
218
- authToken,
219
- ...walletConfig,
220
- appId: config.appId,
221
- });
222
- return response.data;
223
- }
224
- catch (error) {
225
- console.error('[WalletService] Error creating wallet:', error);
226
- throw error;
227
- }
228
- }
229
196
  }
230
197
 
231
198
  /**
@@ -304,10 +271,10 @@ class PersService {
304
271
  * @returns Promise with tenant public information
305
272
  */
306
273
  static async getTenantById(tenantId, authToken) {
307
- // Check memory cache first
274
+ // Check memory cache first with expiration
308
275
  const cached = this.tenantCache.get(tenantId);
309
- if (cached) {
310
- return cached;
276
+ if (cached && Date.now() < cached.expiresAt) {
277
+ return cached.tenant;
311
278
  }
312
279
  try {
313
280
  const headers = {
@@ -345,10 +312,16 @@ class PersService {
345
312
  }
346
313
  }
347
314
  const tenantData = await response.json();
348
- // Cache the tenant data in memory only
349
- this.tenantCache.set(tenantId, tenantData);
315
+ // Cache the tenant data with expiration
316
+ const now = Date.now();
317
+ this.tenantCache.set(tenantId, {
318
+ tenant: tenantData,
319
+ cachedAt: now,
320
+ expiresAt: now + this.TENANT_CACHE_TTL
321
+ });
350
322
  // Update current project key (check multiple possible property names)
351
323
  this.currentProjectKey = tenantData.projectKey || tenantData.projectApiKey || tenantData.apiKey;
324
+ this.currentTenantId = tenantId;
352
325
  return tenantData;
353
326
  }
354
327
  catch (error) {
@@ -368,14 +341,35 @@ class PersService {
368
341
  const projectKey = tenantData.projectApiKey;
369
342
  if (projectKey) {
370
343
  this.currentProjectKey = projectKey;
344
+ this.currentTenantId = tenantId;
371
345
  }
372
346
  return tenantData;
373
347
  }
348
+ /**
349
+ * Ensure tenant is initialized and current, with automatic revalidation
350
+ * @param tenantId - The tenant ID to ensure is initialized
351
+ * @param authToken - Optional auth token for authentication
352
+ * @returns Promise with tenant information
353
+ */
354
+ static async ensureTenantInitialized(tenantId, authToken) {
355
+ // Check if we already have the right tenant initialized and it's still valid
356
+ const cached = this.tenantCache.get(tenantId);
357
+ if (this.currentTenantId === tenantId && cached && Date.now() < cached.expiresAt) {
358
+ return cached.tenant;
359
+ }
360
+ // Initialize or refresh tenant data
361
+ return await this.initializeTenant(tenantId, authToken);
362
+ }
374
363
  /**
375
364
  * Get the current project key (either from tenant or fallback)
365
+ * @param tenantId - Optional tenant ID to ensure is initialized
376
366
  * @returns The project key to use for API calls
377
367
  */
378
- static getProjectKey() {
368
+ static async getProjectKey(tenantId) {
369
+ // If tenantId provided and different from current, initialize it
370
+ if (tenantId && this.currentTenantId !== tenantId) {
371
+ await this.ensureTenantInitialized(tenantId);
372
+ }
379
373
  if (this.currentProjectKey) {
380
374
  return this.currentProjectKey;
381
375
  }
@@ -391,17 +385,19 @@ class PersService {
391
385
  static clearTenantCache() {
392
386
  this.tenantCache.clear();
393
387
  this.currentProjectKey = null;
388
+ this.currentTenantId = null;
394
389
  }
395
390
  /**
396
391
  * Authenticates a user with the PERS backend using their auth token
397
392
  *
398
393
  * @param authToken - The authentication token received from DFNS after login/registration
394
+ * @param tenantId - Optional tenant ID for automatic initialization
399
395
  * @returns A promise that resolves to the authentication response
400
396
  * @throws If the request fails
401
397
  */
402
- static async authenticateUser(authToken) {
398
+ static async authenticateUser(authToken, tenantId) {
403
399
  try {
404
- const projectKey = this.getProjectKey();
400
+ const projectKey = await this.getProjectKey(tenantId);
405
401
  const headers = {
406
402
  'accept': 'application/json',
407
403
  'x-project-key': projectKey,
@@ -451,30 +447,6 @@ class PersService {
451
447
  };
452
448
  }
453
449
  }
454
- /**
455
- * Prepares a transaction by calling the backend endpoint
456
- *
457
- * @param form - The transaction details
458
- * @param persAccessToken - The PERS access token for authentication (Bearer)
459
- * @returns A promise that resolves to the transaction preparation response
460
- * @throws If the request fails
461
- */
462
- static async prepareTransaction(form, persAccessToken) {
463
- // ✅ UPDATED: /transaction/auth/prepare-signing → /transactions/prepare
464
- const res = await fetch(`${getPersApiUrl(this.useStaging)}/transactions`, {
465
- method: "POST",
466
- headers: {
467
- "Content-Type": "application/json",
468
- 'x-project-key': this.getProjectKey(),
469
- Authorization: `Bearer ${persAccessToken}`,
470
- },
471
- body: JSON.stringify(form),
472
- });
473
- if (!res.ok)
474
- throw new Error("Failed to prepare transaction");
475
- const response = await res.json();
476
- return response;
477
- }
478
450
  /**
479
451
  * Submits a transaction by calling the backend endpoint
480
452
  *
@@ -482,208 +454,86 @@ class PersService {
482
454
  * @param signedTransactionOrSignature - The signed transaction data or EIP-712 signature
483
455
  * @param persAccessToken - The PERS access token for authentication (Bearer)
484
456
  * @param submissionType - The transaction format type
457
+ * @param tenantId - Optional tenant ID for automatic initialization
485
458
  * @returns A promise that resolves to the transaction submission response
486
459
  * @throws If the request fails
487
460
  */
488
- static async submitTransaction(transactionId, signedTransactionOrSignature, persAccessToken, submissionType) {
489
- // Map TransactionFormat to the backend's expected submission type
490
- const dto = {
491
- transactionId,
492
- type: submissionType,
493
- ...(submissionType === TRANSACTION_FORMATS.EIP_712
494
- ? { signature: signedTransactionOrSignature }
495
- : { signedTransaction: signedTransactionOrSignature })
496
- };
497
- // ✅ UPDATED: /transaction/auth/submit/${transactionId} → /transactions/${transactionId}/submit
498
- const res = await fetch(`${getPersApiUrl(this.useStaging)}/transactions/submit`, {
499
- method: "POST",
500
- headers: {
501
- "Content-Type": "application/json",
502
- 'x-project-key': this.getProjectKey(),
503
- Authorization: `Bearer ${persAccessToken}`,
504
- },
505
- body: JSON.stringify(dto),
506
- });
507
- if (!res.ok)
508
- throw new Error("Failed to submit transaction");
509
- return await res.json();
510
- }
511
- /**
512
- * Fetches a prepared transaction for signing by transactionId
513
- * @param transactionId - The transaction ID to fetch
514
- * @param persAccessToken - The PERS access token for authentication (Bearer)
515
- * @returns The prepared transaction data
516
- * @throws If the request fails
517
- */
518
- static async fetchPreparedTransaction(transactionId, persAccessToken) {
519
- // ✅ UPDATED: /transaction/auth/prepare-signing/${transactionId} → /transactions/${transactionId}/prepare
520
- const res = await fetch(`${getPersApiUrl(this.useStaging)}/transactions/${transactionId}/prepare`, {
521
- headers: {
522
- "Content-Type": "application/json",
523
- "x-project-key": this.getProjectKey(),
524
- Authorization: `Bearer ${persAccessToken}`,
525
- },
526
- });
527
- const response = await res.json();
528
- if (!res.ok) {
529
- // Throw structured error with the backend response
530
- throw {
531
- status: res.status,
532
- message: response.message || "Failed to fetch prepared transaction",
533
- error: response
534
- };
535
- }
536
- return response;
537
- }
538
- /**
539
- * Claims a reward from the PERS blockchain system
540
- * @param rewardId - The ID of the reward to claim
541
- * @param pointsCost - The points cost for the reward
542
- * @param persAccessToken - The PERS access token for authentication (Bearer)
543
- * @returns The reward claim response including reward image URL
544
- * @throws If the request fails
545
- */
546
- static async claimReward(rewardId, pointsCost, persAccessToken) {
547
- const res = await fetch(`${getPersApiUrl(this.useStaging)}/rewards/auth/claim`, {
548
- method: "POST",
549
- headers: {
550
- "Content-Type": "application/json",
551
- 'x-project-key': this.getProjectKey(),
552
- Authorization: `Bearer ${persAccessToken}`,
553
- },
554
- body: JSON.stringify({
555
- rewardId,
556
- pointsCost
557
- }),
558
- });
559
- if (!res.ok) {
560
- const error = await res.text();
561
- throw new Error(`Failed to claim reward: ${error}`);
562
- }
563
- return await res.json();
564
- }
565
- /**
566
- * Get all active campaigns
567
- * @returns Promise with list of active campaigns
568
- */
569
- static async getActiveCampaigns() {
570
- try {
571
- const response = await fetch(`${getPersApiUrl(this.useStaging)}/campaign`, {
572
- method: 'GET',
573
- headers: {
574
- 'accept': 'application/json',
575
- 'x-project-key': this.getProjectKey()
576
- }
577
- });
578
- if (!response.ok) {
579
- const errorData = await response.json().catch(() => ({}));
580
- throw {
581
- status: response.status,
582
- message: errorData.message || 'Failed to fetch active campaigns',
583
- error: errorData
584
- };
585
- }
586
- const data = await response.json();
587
- return data;
588
- }
589
- catch (error) {
590
- throw error;
591
- }
592
- }
593
- /**
594
- * Claims a campaign for a user
595
- * @param campaignId - The ID of the campaign to claim
596
- * @param persAccessToken - The PERS access token for authentication (Bearer)
597
- * @returns Promise with the campaign claim response
598
- * @throws If the request fails
599
- */
600
- static async claimCampaign(campaignId, persAccessToken) {
461
+ static async submitTransaction(transactionId, signedTransactionOrSignature, persAccessToken, submissionType, tenantId) {
601
462
  try {
602
- const response = await fetch(`${getPersApiUrl(this.useStaging)}/campaign/auth/claim`, {
603
- method: 'POST',
463
+ // Map TransactionFormat to the backend's expected submission type
464
+ const dto = {
465
+ transactionId,
466
+ type: submissionType,
467
+ ...(submissionType === TRANSACTION_FORMATS.EIP_712
468
+ ? { signature: signedTransactionOrSignature }
469
+ : { signedTransaction: signedTransactionOrSignature })
470
+ };
471
+ // ✅ UPDATED: /transaction/auth/submit/${transactionId} → /transactions/${transactionId}/submit
472
+ const res = await fetch(`${getPersApiUrl(this.useStaging)}/transactions/submit`, {
473
+ method: "POST",
604
474
  headers: {
605
- 'accept': 'application/json',
606
- 'x-project-key': this.getProjectKey(),
607
- 'Authorization': `Bearer ${persAccessToken}`,
608
- 'Content-Type': 'application/json'
475
+ "Content-Type": "application/json",
476
+ 'x-project-key': await this.getProjectKey(tenantId),
477
+ Authorization: `Bearer ${persAccessToken}`,
609
478
  },
610
- body: JSON.stringify({
611
- campaignId
612
- })
479
+ body: JSON.stringify(dto),
613
480
  });
614
- if (!response.ok) {
615
- const errorData = await response.json().catch(() => ({}));
481
+ const response = await res.json();
482
+ if (!res.ok) {
483
+ // Throw structured error with the backend response
616
484
  throw {
617
- status: response.status,
618
- message: errorData.message || 'Failed to claim campaign',
619
- error: errorData
485
+ status: res.status,
486
+ message: response.message || "Failed to submit transaction",
487
+ error: response
620
488
  };
621
489
  }
622
- const data = await response.json();
623
- return data;
490
+ return response;
624
491
  }
625
492
  catch (error) {
626
- throw error;
493
+ // Rethrow a structured error
494
+ throw {
495
+ status: error.status || 500,
496
+ message: error.message || 'Failed to submit transaction to PERS backend',
497
+ error
498
+ };
627
499
  }
628
500
  }
629
501
  /**
630
- * Gets all campaign claims for the authenticated user
502
+ * Fetches a prepared transaction for signing by transactionId
503
+ * @param transactionId - The transaction ID to fetch
631
504
  * @param persAccessToken - The PERS access token for authentication (Bearer)
632
- * @returns Promise with list of user's campaign claims
633
- * @throws If the request fails
634
- */
635
- static async getUserCampaignClaims(persAccessToken) {
636
- try {
637
- const response = await fetch(`${getPersApiUrl(this.useStaging)}/campaign/auth/claim`, {
638
- method: 'GET',
639
- headers: {
640
- 'accept': 'application/json',
641
- 'x-project-key': this.getProjectKey(),
642
- 'Authorization': `Bearer ${persAccessToken}`
643
- }
644
- });
645
- if (!response.ok) {
646
- const errorData = await response.json().catch(() => ({}));
647
- throw {
648
- status: response.status,
649
- message: errorData.message || 'Failed to fetch user campaign claims',
650
- error: errorData
651
- };
652
- }
653
- const data = await response.json();
654
- return data;
655
- }
656
- catch (error) {
657
- throw error;
658
- }
659
- }
660
- /**
661
- * Gets all available rewards for redemption
662
- * @returns Promise with list of available rewards that can be exchanged for points
505
+ * @param tenantId - Optional tenant ID for automatic initialization
506
+ * @returns The prepared transaction data
663
507
  * @throws If the request fails
664
508
  */
665
- static async getAvailableRedemptions() {
509
+ static async fetchPreparedTransaction(transactionId, persAccessToken, tenantId) {
666
510
  try {
667
- const response = await fetch(`${getPersApiUrl(this.useStaging)}/redemption`, {
668
- method: 'GET',
511
+ // UPDATED: /transaction/auth/prepare-signing/${transactionId} → /transactions/${transactionId}/prepare
512
+ const res = await fetch(`${getPersApiUrl(this.useStaging)}/transactions/${transactionId}/prepare`, {
669
513
  headers: {
670
- 'accept': 'application/json',
671
- 'x-project-key': this.getProjectKey()
672
- }
514
+ "Content-Type": "application/json",
515
+ "x-project-key": await this.getProjectKey(tenantId),
516
+ Authorization: `Bearer ${persAccessToken}`,
517
+ },
673
518
  });
674
- if (!response.ok) {
675
- const errorData = await response.json().catch(() => ({}));
519
+ const response = await res.json();
520
+ if (!res.ok) {
521
+ // Throw structured error with the backend response
676
522
  throw {
677
- status: response.status,
678
- message: errorData.message || 'Failed to fetch available redemptions',
679
- error: errorData
523
+ status: res.status,
524
+ message: response.message || "Failed to fetch prepared transaction",
525
+ error: response
680
526
  };
681
527
  }
682
- const data = await response.json();
683
- return data;
528
+ return response;
684
529
  }
685
530
  catch (error) {
686
- throw error;
531
+ // Rethrow a structured error
532
+ throw {
533
+ status: error.status || 500,
534
+ message: error.message || 'Failed to fetch prepared transaction from PERS backend',
535
+ error
536
+ };
687
537
  }
688
538
  }
689
539
  /**
@@ -701,7 +551,7 @@ class PersService {
701
551
  * @throws If transaction data is not available
702
552
  */
703
553
  static getTransactionDataForSigning(transactionResponse) {
704
- if (!this.isTransactionReadyForSigning(transactionResponse)) {
554
+ if (!transactionResponse.signingData) {
705
555
  throw new Error('Transaction data is not ready for signing');
706
556
  }
707
557
  return transactionResponse.signingData;
@@ -710,8 +560,53 @@ class PersService {
710
560
  PersService.config = DEFAULT_PERS_CONFIG;
711
561
  PersService.tenantCache = new Map();
712
562
  PersService.currentProjectKey = null;
563
+ PersService.currentTenantId = null;
713
564
  PersService.useStaging = false;
565
+ PersService.TENANT_CACHE_TTL = 24 * 60 * 60 * 1000; // 24 hours - tenant configs are essentially static
714
566
 
567
+ // Adapter function to convert backend signature response to legacy format
568
+ const adaptSignatureResponse = (newResponse) => {
569
+ if (!newResponse.signature) {
570
+ console.error('[KeyWallet] No signature in response:', newResponse);
571
+ throw new Error('Signature missing from response');
572
+ }
573
+ // Handle structured signature format (typically from EIP-712 signing)
574
+ if (typeof newResponse.signature === 'object' && 'r' in newResponse.signature) {
575
+ const { r, s, recid } = newResponse.signature;
576
+ return {
577
+ status: 'Signed',
578
+ signature: {
579
+ r,
580
+ s,
581
+ recid // Use recid directly from backend
582
+ }
583
+ };
584
+ }
585
+ // Handle string signature format (potentially from hash signing)
586
+ if (typeof newResponse.signature === 'string') {
587
+ // Parse string signature into components
588
+ // Assuming it's a hex string that can be split into r, s, v components
589
+ const signature = newResponse.signature;
590
+ if (signature.length === 132) { // 0x + 32 bytes r + 32 bytes s + 1 byte v
591
+ const r = '0x' + signature.slice(2, 66);
592
+ const s = '0x' + signature.slice(66, 130);
593
+ const v = parseInt(signature.slice(130, 132), 16);
594
+ const recid = v >= 27 ? v - 27 : v; // Convert v to recid
595
+ return {
596
+ status: 'Signed',
597
+ signature: {
598
+ r,
599
+ s,
600
+ recid
601
+ }
602
+ };
603
+ }
604
+ console.error('[KeyWallet] Invalid string signature length:', signature.length);
605
+ throw new Error(`Invalid string signature format - expected 132 characters, got ${signature.length}`);
606
+ }
607
+ console.error('[KeyWallet] Invalid signature format - expected structured object or string:', typeof newResponse.signature);
608
+ throw new Error('Invalid signature format in response - expected structured object or string from backend');
609
+ };
715
610
  let DfnsError$1 = class DfnsError extends Error {
716
611
  constructor(code, message, details) {
717
612
  super(message);
@@ -730,14 +625,16 @@ const assertSigned = (res) => {
730
625
  };
731
626
  const combineSignature = (res) => {
732
627
  if (!res.signature) {
628
+ console.error('[KeyWallet] No signature in response for combining:', res);
733
629
  throw new DfnsError$1(-1, "signature missing", res);
734
630
  }
735
631
  const { r, s, recid } = res.signature;
736
- return Signature.from({
632
+ const ethersSignature = Signature.from({
737
633
  r,
738
634
  s,
739
635
  v: recid ? 0x1c : 0x1b, // Assuming 0x1c for chain_id > 0, 0x1b otherwise. DFNS usually provides recid.
740
- }).serialized;
636
+ });
637
+ return ethersSignature.serialized;
741
638
  };
742
639
  class KeyWallet extends AbstractSigner {
743
640
  constructor(options, provider) {
@@ -763,9 +660,11 @@ class KeyWallet extends AbstractSigner {
763
660
  return this.address;
764
661
  }
765
662
  async signHash(hash) {
766
- const res = await this.signingService.signHash(this.authToken, this.metadata.id, hash);
663
+ const rawRes = await this.signingService.signHash(this.authToken, this.metadata.id, hash);
664
+ const res = adaptSignatureResponse(rawRes);
767
665
  assertSigned(res);
768
- return combineSignature(res);
666
+ const combinedSignature = combineSignature(res);
667
+ return combinedSignature;
769
668
  }
770
669
  async signTransaction(tx) {
771
670
  // Resolve any addresses
@@ -802,9 +701,11 @@ class KeyWallet extends AbstractSigner {
802
701
  async signTypedData(domain, types, value) {
803
702
  try {
804
703
  // Use DFNS native EIP-712 support with correct structure
805
- const res = await this.signingService.signTypedData(this.authToken, this.metadata.id, domain, types, value);
704
+ const rawRes = await this.signingService.signTypedData(this.authToken, this.metadata.id, domain, types, value);
705
+ const res = adaptSignatureResponse(rawRes);
806
706
  assertSigned(res);
807
- return combineSignature(res);
707
+ const combinedSignature = combineSignature(res);
708
+ return combinedSignature;
808
709
  }
809
710
  catch (error) {
810
711
  console.error('[KeyWallet] EIP-712 signing failed:', error);
@@ -934,27 +835,36 @@ class SigningService {
934
835
  const config = getServiceConfig();
935
836
  try {
936
837
  // Step 1: Initialize signing and get challenge
937
- const initResponse = await httpClient.post(`${config.apiUrl}/wallets/signatures/init`, {
938
- authToken,
838
+ const initRequest = {
939
839
  walletId,
940
- request,
941
- appId: config.appId,
942
- });
943
- const { requestBody, challenge } = initResponse.data;
840
+ request
841
+ };
842
+ const initResponse = await httpClient.post(`${config.apiUrl}/wallets/signatures/init`, initRequest, { Authorization: `Bearer ${authToken}` });
843
+ const backendInitResponse = initResponse.data;
844
+ if (!backendInitResponse.success || !backendInitResponse.data) {
845
+ console.error('[SigningService] Init response invalid:', backendInitResponse);
846
+ throw new Error('Signature initialization failed');
847
+ }
848
+ const { requestBody, challenge } = backendInitResponse.data;
944
849
  // Step 2: Sign the challenge using WebAuthn
945
850
  const assertion = await this.webAuthnProvider.sign(challenge);
946
851
  // Step 3: Complete signing with signed challenge
947
- const completeResponse = await httpClient.post(`${config.apiUrl}/wallets/signatures/complete`, {
948
- authToken,
852
+ const completeRequest = {
949
853
  walletId,
950
854
  requestBody,
951
855
  signedChallenge: {
952
- challengeIdentifier: challenge.challengeIdentifier,
856
+ challengeIdentifier: challenge?.challengeIdentifier,
953
857
  firstFactor: assertion,
954
- },
955
- appId: config.appId,
956
- });
957
- return completeResponse.data;
858
+ }
859
+ };
860
+ const completeResponse = await httpClient.post(`${config.apiUrl}/wallets/signatures/complete`, completeRequest, { Authorization: `Bearer ${authToken}` });
861
+ const backendCompleteResponse = completeResponse.data;
862
+ if (!backendCompleteResponse.success || !backendCompleteResponse.data) {
863
+ console.error('[SigningService] Complete response invalid:', backendCompleteResponse);
864
+ throw new Error('Signature completion failed');
865
+ }
866
+ // Return the complete signature data from backend response
867
+ return backendCompleteResponse.data;
958
868
  }
959
869
  catch (error) {
960
870
  console.error(`[SigningService] Signing failed for wallet ${walletId}:`, error);
@@ -1151,8 +1061,8 @@ class TransactionValidator {
1151
1061
  if (!params.authTokens) {
1152
1062
  throw TransactionErrorHandler.createError(TransactionSigningErrorCode.INVALID_TOKENS, 'Authentication tokens are required', params.transactionId);
1153
1063
  }
1154
- if (!params.authTokens.backendAuthToken || !params.authTokens.persAccessToken) {
1155
- throw TransactionErrorHandler.createError(TransactionSigningErrorCode.INVALID_TOKENS, 'Both backend and PERS authentication tokens are required', params.transactionId);
1064
+ if (!params.authTokens.signerAuthToken || !params.authTokens.persAccessToken) {
1065
+ throw TransactionErrorHandler.createError(TransactionSigningErrorCode.INVALID_TOKENS, 'Both signer and PERS authentication tokens are required', params.transactionId);
1156
1066
  }
1157
1067
  if (!params.ethersProviderUrl || typeof params.ethersProviderUrl !== 'string') {
1158
1068
  throw TransactionErrorHandler.createError(TransactionSigningErrorCode.INVALID_TOKENS, 'Ethers provider URL is required', params.transactionId);
@@ -1164,81 +1074,18 @@ class TransactionValidator {
1164
1074
  * @returns true if valid, throws error if invalid
1165
1075
  */
1166
1076
  static validateAuthTokens(authTokens) {
1167
- if (!authTokens.persAccessToken || !authTokens.backendAuthToken) {
1168
- throw TransactionErrorHandler.createError(TransactionSigningErrorCode.INVALID_TOKENS, 'Both PERS access token and backend auth token are required');
1077
+ if (!authTokens.persAccessToken || !authTokens.signerAuthToken) {
1078
+ throw TransactionErrorHandler.createError(TransactionSigningErrorCode.INVALID_TOKENS, 'Both PERS access token and signer auth token are required');
1169
1079
  }
1170
1080
  return true;
1171
1081
  }
1172
1082
  }
1173
1083
 
1174
- /**
1175
- * Handles transaction submission, success flows, and redirect logic
1176
- */
1177
- class TransactionSubmissionHandler {
1178
- /**
1179
- * Create redirect URL with transaction parameters
1180
- * @param returnUrl - Base return URL
1181
- * @param transactionHash - Transaction hash to include
1182
- * @param metadata - Optional metadata to include as parameters
1183
- * @returns Promise resolving to the complete redirect URL
1184
- */
1185
- static async createRedirectUrl(returnUrl, transactionHash, metadata) {
1186
- const additionalParams = {
1187
- txHash: transactionHash,
1188
- success: 'true'
1189
- };
1190
- // Add metadata if available
1191
- if (metadata) {
1192
- Object.entries(metadata).forEach(([key, value]) => {
1193
- // metadata is already constrained to string values by TransactionMetadata interface
1194
- additionalParams[key] = value;
1195
- });
1196
- }
1197
- // Dynamic import to avoid build issues
1198
- const { createUrlWithSearchParams } = await Promise.resolve().then(function () { return searchParams; });
1199
- return createUrlWithSearchParams(returnUrl, ['transactionId', 'returnUrl', 'jwt', 'token'], additionalParams);
1200
- }
1201
- /**
1202
- * Submit transaction and handle success flow
1203
- * @param preparedTransaction - The prepared transaction
1204
- * @param signature - The transaction signature
1205
- * @param signingData - The signing data used
1206
- * @param authTokens - Authentication tokens
1207
- * @param transactionId - Transaction ID for tracking
1208
- * @param returnUrl - Optional return URL for redirect
1209
- * @param metadata - Optional metadata for redirect parameters
1210
- * @returns Promise resolving to submission result
1211
- */
1212
- static async handleTransactionSubmission(preparedTransaction, signature, signingData, authTokens, returnUrl, metadata) {
1213
- // Submit signed transaction
1214
- const submitResult = await PersService.submitTransaction(preparedTransaction.transaction.id, signature, authTokens.persAccessToken, signingData.format);
1215
- // Get transaction hash for return URL or response
1216
- const transactionHash = submitResult.transaction?.transactionHash || '';
1217
- // Handle success - check for return URL
1218
- if (returnUrl) {
1219
- const redirectUrl = await this.createRedirectUrl(returnUrl, transactionHash, metadata);
1220
- return { submitResult, shouldRedirect: true, redirectUrl };
1221
- }
1222
- else {
1223
- return { submitResult, shouldRedirect: false };
1224
- }
1225
- }
1226
- }
1227
-
1228
1084
  /**
1229
1085
  * Utility class for coordinating WebAuthn operations to prevent conflicts
1230
1086
  * Manages global state flags to ensure only one WebAuthn operation runs at a time
1231
1087
  */
1232
1088
  class WebAuthnCoordinator {
1233
- /**
1234
- * Clear the landing authentication flag
1235
- * Used when starting a new transaction signing flow
1236
- */
1237
- static clearLandingAuthentication() {
1238
- if (typeof window !== 'undefined') {
1239
- window.landingAuthenticationInProgress = false;
1240
- }
1241
- }
1242
1089
  /**
1243
1090
  * Check if a WebAuthn operation is currently in progress
1244
1091
  * @returns True if an operation is in progress, false otherwise
@@ -1271,16 +1118,16 @@ const SIGNABLE_STATUSES = [TransactionStatus.PENDING_SIGNATURE, TransactionStatu
1271
1118
  * Uses constructor-based dependency injection for WebAuthn provider
1272
1119
  */
1273
1120
  class TransactionSigningService {
1274
- constructor(webAuthnProvider) {
1275
- this.webAuthnProvider = webAuthnProvider;
1121
+ constructor(config) {
1122
+ this.webAuthnProvider = config.webAuthnProvider;
1276
1123
  }
1277
1124
  /**
1278
1125
  * Prepare transaction for signing - fetch and validate
1279
1126
  */
1280
- async prepareTransaction(transactionId, authTokens) {
1127
+ async prepareTransaction(transactionId, authTokens, tenantId) {
1281
1128
  let preparedTransaction;
1282
1129
  try {
1283
- preparedTransaction = await PersService.fetchPreparedTransaction(transactionId, authTokens.persAccessToken);
1130
+ preparedTransaction = await PersService.fetchPreparedTransaction(transactionId, authTokens.persAccessToken, tenantId);
1284
1131
  }
1285
1132
  catch (err) {
1286
1133
  // Use TransactionErrorHandler to process PERS API errors
@@ -1301,7 +1148,7 @@ class TransactionSigningService {
1301
1148
  // Authenticate with PERS using backend signer token to set up signing account
1302
1149
  let updatedPersAccessToken = authTokens.persAccessToken;
1303
1150
  try {
1304
- const persSignerAuth = await PersService.authenticateUser(authTokens.backendAuthToken);
1151
+ const persSignerAuth = await PersService.authenticateUser(authTokens.signerAuthToken, tenantId);
1305
1152
  // Update PERS access token with the new one that has signing account linked
1306
1153
  const newPersAccessToken = persSignerAuth.accessToken;
1307
1154
  if (newPersAccessToken) {
@@ -1315,7 +1162,7 @@ class TransactionSigningService {
1315
1162
  }
1316
1163
  // If transaction is 'created' status but doesn't have signingData, fetch again with wallet-enabled token
1317
1164
  if (transactionStatus === TransactionStatus.CREATED && !PersService.isTransactionReadyForSigning(preparedTransaction)) {
1318
- preparedTransaction = await PersService.fetchPreparedTransaction(transactionId, updatedPersAccessToken);
1165
+ preparedTransaction = await PersService.fetchPreparedTransaction(transactionId, updatedPersAccessToken, tenantId);
1319
1166
  }
1320
1167
  if (!PersService.isTransactionReadyForSigning(preparedTransaction)) {
1321
1168
  throw TransactionErrorHandler.createError(TransactionSigningErrorCode.TRANSACTION_NOT_READY, 'Transaction is not ready for signing', transactionId);
@@ -1330,19 +1177,21 @@ class TransactionSigningService {
1330
1177
  */
1331
1178
  async prepareWallet(authTokens, ethersProviderUrl) {
1332
1179
  // Wallet validation will be handled through API responses - no localStorage needed
1333
- let walletData;
1180
+ let walletListResult;
1334
1181
  try {
1335
- walletData = await WalletService.listWallets(authTokens.backendAuthToken);
1336
- console.log('[TransactionSigningService] Wallet list response:', walletData);
1182
+ // Use new WalletService API with signer token
1183
+ walletListResult = await WalletService.listWallets(authTokens.signerAuthToken);
1337
1184
  }
1338
1185
  catch (error) {
1339
1186
  console.error('[TransactionSigningService] Wallet list API failed:', error);
1340
1187
  throw TransactionErrorHandler.createError(TransactionSigningErrorCode.WALLET_NOT_AVAILABLE, 'Failed to retrieve wallet information. Please refresh the page and try again.', undefined, error);
1341
1188
  }
1342
- // Check both possible response structures for compatibility
1343
- const wallets = walletData?.wallets || walletData?.items || [];
1189
+ // Handle multiple response formats for compatibility
1190
+ const wallets = walletListResult?.items ||
1191
+ walletListResult?.data?.wallets ||
1192
+ walletListResult?.wallets || [];
1344
1193
  if (!wallets?.length) {
1345
- console.error('[TransactionSigningService] No wallets found in response:', walletData);
1194
+ console.error('[TransactionSigningService] No wallets found in response:', walletListResult);
1346
1195
  throw TransactionErrorHandler.createError(TransactionSigningErrorCode.WALLET_NOT_AVAILABLE, 'No wallet found for transaction signing. Please refresh the page and complete account setup.');
1347
1196
  }
1348
1197
  // Create SigningService with injected WebAuthn provider
@@ -1350,7 +1199,7 @@ class TransactionSigningService {
1350
1199
  // Create wallet instance with provider - use unknown type for flexibility
1351
1200
  const provider = new JsonRpcProvider(ethersProviderUrl);
1352
1201
  const wallet = new KeyWallet({
1353
- authToken: authTokens.backendAuthToken,
1202
+ authToken: authTokens.signerAuthToken,
1354
1203
  wallet: wallets[0],
1355
1204
  signingService: signingService,
1356
1205
  }).connect(provider);
@@ -1359,7 +1208,9 @@ class TransactionSigningService {
1359
1208
  /**
1360
1209
  * Execute the transaction signing with WebAuthn coordination
1361
1210
  */
1362
- async executeTransactionSigning(wallet, preparedTransaction) {
1211
+ async executeTransactionSigning(wallet,
1212
+ // preparedTransaction: TransactionRequestResponseDTO,
1213
+ signingData) {
1363
1214
  // Check for concurrent WebAuthn operations
1364
1215
  if (WebAuthnCoordinator.checkConcurrentOperations()) {
1365
1216
  throw TransactionErrorHandler.createError(TransactionSigningErrorCode.WEBAUTHN_OPERATION_IN_PROGRESS, 'Another WebAuthn operation is in progress. Please wait and try again.');
@@ -1367,7 +1218,7 @@ class TransactionSigningService {
1367
1218
  // Set WebAuthn operation flag for transaction signing
1368
1219
  WebAuthnCoordinator.setOperationInProgress(true);
1369
1220
  try {
1370
- const signingData = PersService.getTransactionDataForSigning(preparedTransaction);
1221
+ // const signingData = PersService.getTransactionDataForSigning(preparedTransaction);
1371
1222
  const signature = await wallet.signPersTransaction(signingData);
1372
1223
  return signature;
1373
1224
  }
@@ -1386,6 +1237,13 @@ class TransactionSigningService {
1386
1237
  WebAuthnCoordinator.setOperationInProgress(false);
1387
1238
  }
1388
1239
  }
1240
+ async getPersSigningData(data) {
1241
+ // Step 1: Prepare transaction for signing
1242
+ const { preparedTransaction } = await this.prepareTransaction(data.transactionId, data.authTokens, data.tenantId);
1243
+ // const signingData = PersService.getTransactionDataForSigning(preparedTransaction);
1244
+ const signingData = PersService.getTransactionDataForSigning(preparedTransaction);
1245
+ return signingData;
1246
+ }
1389
1247
  /**
1390
1248
  * Main transaction signing orchestration method
1391
1249
  * Handles the complete flow from preparation to submission
@@ -1393,31 +1251,23 @@ class TransactionSigningService {
1393
1251
  * @returns Promise resolving to transaction signing result
1394
1252
  * @throws TransactionSigningError for validation and operation failures
1395
1253
  */
1396
- async signTransaction(params) {
1254
+ async signTransaction(params, signingData) {
1397
1255
  // Validate input parameters first using TransactionValidator
1398
1256
  TransactionValidator.validateSigningParams(params);
1399
- const { transactionId, authTokens, ethersProviderUrl, returnUrl, metadata } = params;
1257
+ const { transactionId, authTokens, ethersProviderUrl } = params;
1400
1258
  console.info(`[TransactionSigningService] Starting signature process for ${transactionId}`);
1401
1259
  try {
1402
- // Clear previous landing authentication state
1403
- WebAuthnCoordinator.clearLandingAuthentication();
1404
- // Step 1: Prepare transaction for signing
1405
- const { preparedTransaction, updatedTokens } = await this.prepareTransaction(transactionId, authTokens);
1406
1260
  // Step 2: Prepare wallet for signing
1407
- const wallet = await this.prepareWallet(updatedTokens, ethersProviderUrl);
1261
+ const wallet = await this.prepareWallet(authTokens, ethersProviderUrl);
1408
1262
  // Step 3: Execute transaction signing
1409
- const signature = await this.executeTransactionSigning(wallet, preparedTransaction);
1410
- const signingData = PersService.getTransactionDataForSigning(preparedTransaction);
1411
- // Step 4: Submit transaction and handle success using TransactionSubmissionHandler
1412
- const { submitResult, shouldRedirect, redirectUrl } = await TransactionSubmissionHandler.handleTransactionSubmission(preparedTransaction, signature, signingData, updatedTokens, returnUrl, metadata);
1413
- console.info(`[TransactionSigningService] Completed successfully: ${transactionId}`);
1263
+ const signature = await this.executeTransactionSigning(wallet, signingData);
1264
+ console.info(`[TransactionSigningService] Completed signing successfully: ${transactionId}`);
1414
1265
  return {
1415
1266
  success: true,
1416
1267
  transactionId,
1417
- transactionHash: submitResult.transaction?.transactionHash ?? undefined,
1268
+ signingData,
1269
+ // transactionHash: submitResult.transaction?.transactionHash ?? undefined,
1418
1270
  signature,
1419
- shouldRedirect,
1420
- redirectUrl
1421
1271
  };
1422
1272
  }
1423
1273
  catch (error) {
@@ -1443,720 +1293,728 @@ class TransactionSigningService {
1443
1293
  }
1444
1294
  }
1445
1295
 
1296
+ /**
1297
+ * Handles transaction submission, success flows, and redirect logic
1298
+ */
1299
+ class TransactionSubmissionHandler {
1300
+ /**
1301
+ * Create redirect URL with transaction parameters
1302
+ * @param returnUrl - Base return URL
1303
+ * @param transactionHash - Transaction hash to include
1304
+ * @param metadata - Optional metadata to include as parameters
1305
+ * @returns Promise resolving to the complete redirect URL
1306
+ */
1307
+ static async createRedirectUrl(returnUrl, transactionHash) {
1308
+ const additionalParams = {
1309
+ txHash: transactionHash,
1310
+ success: 'true'
1311
+ };
1312
+ // Build URL with required params
1313
+ const paramKeys = ['transactionId', 'returnUrl', 'jwt', 'token'];
1314
+ const url = new URL(returnUrl, window.location.origin);
1315
+ // Add required params if present in additionalParams
1316
+ paramKeys.forEach(key => {
1317
+ if (additionalParams[key]) {
1318
+ url.searchParams.set(key, additionalParams[key]);
1319
+ }
1320
+ });
1321
+ // Add all other params
1322
+ Object.entries(additionalParams).forEach(([key, value]) => {
1323
+ if (!paramKeys.includes(key)) {
1324
+ url.searchParams.set(key, value);
1325
+ }
1326
+ });
1327
+ return url.toString();
1328
+ }
1329
+ }
1330
+
1446
1331
  /**
1447
1332
  * Authentication service for user login and registration
1448
1333
  * Uses constructor-based dependency injection for WebAuthn provider
1334
+ * Updated for new v1 API endpoints
1449
1335
  */
1450
1336
  class AuthenticationService {
1451
- constructor(webAuthnProvider) {
1452
- this.webAuthnProvider = webAuthnProvider;
1337
+ constructor(config) {
1338
+ this.signerToken = null;
1339
+ this.config = config;
1340
+ this.webAuthnProvider = config.webAuthnProvider;
1453
1341
  }
1454
1342
  /**
1455
- * Authenticate user with username
1456
- * @param username - User identifier
1457
- * @returns Promise resolving to authentication token
1343
+ * Login with PERS token to get signer JWT
1344
+ * @param persToken - PERS JWT from PERS authentication
1345
+ * @returns Promise resolving to login response or provider challenge data
1458
1346
  */
1459
- async login(username) {
1347
+ async loginWithPersToken(persToken) {
1460
1348
  const httpClient = getHttpClient();
1461
1349
  const config = getServiceConfig();
1462
1350
  try {
1463
- const response = await httpClient.post(`${config.apiUrl}/login`, {
1464
- username,
1465
- appId: config.appId,
1466
- });
1467
- // Check if the response contains an error
1468
- if (response.data.status && response.data.status >= 400) {
1469
- throw new Error(response.data.message || 'Login failed');
1470
- }
1471
- if (!response.data.token) {
1472
- throw new Error(response.data.message || 'No token received from login');
1351
+ const requestBody = {
1352
+ authToken: persToken
1353
+ };
1354
+ const response = await httpClient.post(`${config.apiUrl}/auth/login`, requestBody
1355
+ // Note: Login endpoint doesn't require Authorization header (public endpoint)
1356
+ );
1357
+ const backendResponse = response.data;
1358
+ if (backendResponse && backendResponse.success) {
1359
+ // Check if it's JWT response or wrapped provider response
1360
+ if ('access_token' in backendResponse) {
1361
+ // JWT response - set token and return
1362
+ this.signerToken = backendResponse.access_token;
1363
+ return backendResponse;
1364
+ }
1365
+ else {
1366
+ // Wrapped provider response - return the data
1367
+ return backendResponse.data;
1368
+ }
1473
1369
  }
1474
- console.info(`[WebAuthn] User login successful: ${username}`);
1475
- return response.data.token;
1370
+ console.error('[AuthenticationService] Invalid response - no success or success=false:', backendResponse);
1371
+ throw new Error('Login failed: Invalid response format');
1476
1372
  }
1477
1373
  catch (error) {
1478
- console.error(`[AuthenticationService] Login failed for ${username}:`, error);
1374
+ // console.error(`[AuthenticationService] PERS token login failed:`, error);
1479
1375
  throw error;
1480
1376
  }
1481
1377
  }
1482
1378
  /**
1483
- * Register new user with WebAuthn credential
1484
- * @param username - User identifier
1485
- * @returns Promise resolving to registration result
1379
+ * Verify signer token validity
1380
+ * @param token - Signer JWT to verify
1381
+ * @returns Promise resolving to verification result
1486
1382
  */
1487
- async register(username) {
1383
+ async verifyToken(token) {
1488
1384
  const httpClient = getHttpClient();
1489
1385
  const config = getServiceConfig();
1490
1386
  try {
1491
- // Step 1: Initialize registration and get challenge
1492
- const initResponse = await httpClient.post(`${config.apiUrl}/register/init`, {
1493
- username,
1494
- appId: config.appId,
1495
- });
1496
- const challenge = initResponse.data;
1497
- // Check if the registration init failed
1498
- if (challenge.status && challenge.status >= 400) {
1499
- throw new Error(challenge.message || 'Registration initialization failed');
1500
- }
1501
- // Step 2: Create WebAuthn credential using the challenge
1502
- const attestation = await this.webAuthnProvider.create(challenge);
1503
- console.info(`[WebAuthn] Credential created successfully for user: ${username}`);
1504
- // Step 3: Complete registration with signed challenge
1505
- const completeResponse = await httpClient.post(`${config.apiUrl}/register/complete`, {
1506
- signedChallenge: { firstFactorCredential: attestation },
1507
- temporaryAuthenticationToken: challenge.temporaryAuthenticationToken,
1508
- appId: config.appId,
1509
- });
1510
- const result = completeResponse.data;
1511
- // Check if the registration completion failed
1512
- if (result.status && result.status >= 400) {
1513
- throw new Error(result.message || 'Registration completion failed');
1387
+ const requestBody = {
1388
+ token
1389
+ };
1390
+ const response = await httpClient.post(`${config.apiUrl}/auth/verify`, requestBody, { Authorization: `Bearer ${token}` });
1391
+ const verifyData = response.data;
1392
+ if (!verifyData.valid) {
1393
+ throw new Error('Token verification failed');
1514
1394
  }
1515
- console.info(`[AuthenticationService] Registration successful for: ${username}`);
1516
- return result;
1395
+ return verifyData;
1517
1396
  }
1518
1397
  catch (error) {
1519
- console.error(`[AuthenticationService] Registration failed for ${username}:`, error);
1398
+ console.error(`[AuthenticationService] Token verification failed:`, error);
1520
1399
  throw error;
1521
1400
  }
1522
1401
  }
1523
1402
  /**
1524
- * Validate authentication token (when backend supports it)
1525
- * @param authToken - Token to validate
1526
- * @returns Promise resolving to validation result
1403
+ * Initialize user registration
1404
+ * @param persToken - PERS JWT token (registration is public)
1405
+ * @returns Promise resolving to registration challenge
1527
1406
  */
1528
- async validateToken(authToken) {
1407
+ async initializeRegistration(persToken) {
1529
1408
  const httpClient = getHttpClient();
1530
1409
  const config = getServiceConfig();
1531
1410
  try {
1532
- const response = await httpClient.post(`${config.apiUrl}/validate-token`, {
1533
- authToken,
1534
- appId: config.appId,
1535
- });
1536
- return response.data.valid === true;
1411
+ const requestBody = {
1412
+ authToken: persToken
1413
+ };
1414
+ const response = await httpClient.post(`${config.apiUrl}/auth/register/init`, requestBody);
1415
+ const backendResponse = response.data;
1416
+ if (backendResponse.success && backendResponse.data) {
1417
+ return backendResponse.data;
1418
+ }
1419
+ throw new Error('Registration initialization failed: No data returned');
1537
1420
  }
1538
1421
  catch (error) {
1539
- console.error('[AuthenticationService] Error validating token:', error);
1540
- return false;
1422
+ // console.error(`[AuthenticationService] Registration initialization failed:`, error);
1423
+ throw error;
1541
1424
  }
1542
1425
  }
1543
1426
  /**
1544
- * Get user information (when backend supports it)
1545
- * @param authToken - Authentication token
1546
- * @returns Promise resolving to user info or null
1427
+ * Get current signer token
1428
+ * @returns The current signer JWT token
1429
+ */
1430
+ getSignerToken() {
1431
+ return this.signerToken;
1432
+ }
1433
+ /**
1434
+ * Set signer token (for external token management)
1435
+ * @param token - Signer JWT token
1436
+ */
1437
+ setSignerToken(token) {
1438
+ this.signerToken = token;
1439
+ }
1440
+ /**
1441
+ * Complete registration with WebAuthn challenge data (v1 API format)
1442
+ * @param tmpAuthToken - Temporary auth token from init registration (temporaryAuthenticationToken)
1443
+ * @param signedChallenge - WebAuthn credential response (will be restructured for backend)
1444
+ * @param persToken - PERS JWT token (authToken)
1445
+ * @returns Promise resolving to registration result
1547
1446
  */
1548
- async getUser(authToken) {
1447
+ async completeRegistrationWithChallenge(tmpAuthToken, signedChallenge, persToken) {
1549
1448
  const httpClient = getHttpClient();
1449
+ const config = getServiceConfig();
1550
1450
  try {
1551
- const response = await httpClient.get(`${getServiceConfig().apiUrl}/user`, {
1552
- 'Authorization': `Bearer ${authToken}`
1553
- });
1554
- return response.data;
1555
- }
1451
+ const requestBody = {
1452
+ temporaryAuthenticationToken: tmpAuthToken,
1453
+ signedChallenge: {
1454
+ firstFactorCredential: {
1455
+ credentialKind: signedChallenge.credentialKind || "Fido2",
1456
+ credentialInfo: {
1457
+ credId: signedChallenge.credentialInfo?.credId || "",
1458
+ clientData: signedChallenge.credentialInfo?.clientData || "",
1459
+ attestationData: signedChallenge.credentialInfo?.attestationData || ""
1460
+ }
1461
+ }
1462
+ },
1463
+ authToken: persToken
1464
+ };
1465
+ const response = await httpClient.post(`${config.apiUrl}/auth/register/complete`, requestBody);
1466
+ const backendResponse = response.data;
1467
+ if (backendResponse && backendResponse.success) {
1468
+ // Check if it's JWT response (has access_token at root level) or wrapped provider response
1469
+ if ('access_token' in backendResponse) {
1470
+ // JWT response - set token and return
1471
+ this.signerToken = backendResponse.access_token;
1472
+ return backendResponse;
1473
+ }
1474
+ else if ('data' in backendResponse) {
1475
+ // Wrapped provider response - registration is not complete yet, needs more steps
1476
+ return backendResponse.data;
1477
+ }
1478
+ else {
1479
+ // Wrapped provider response - return the data
1480
+ return backendResponse.data;
1481
+ }
1482
+ }
1483
+ console.error('[AuthenticationService] Registration with challenge failed - invalid response:', backendResponse);
1484
+ throw new Error('Registration completion with challenge failed');
1485
+ }
1556
1486
  catch (error) {
1557
- console.error('[AuthenticationService] Error getting user:', error);
1558
- return null;
1487
+ console.error(`[AuthenticationService] Registration completion with challenge failed:`, error);
1488
+ throw error;
1559
1489
  }
1560
1490
  }
1561
- }
1562
-
1563
- /**
1564
- * PERS Blockchain Signer SDK - Simple Orchestrator
1565
- *
1566
- * Lightweight SDK that orchestrates existing services.
1567
- * Uses environment configuration and existing infrastructure.
1568
- */
1569
- /**
1570
- * Main PERS Signer SDK class
1571
- *
1572
- * Simple orchestrator that uses existing services and environment configuration.
1573
- * No complex initialization needed - services are already configured.
1574
- */
1575
- class PersSignerSDK {
1576
- constructor(config) {
1577
- this.config = config;
1578
- this.webAuthnProvider = config.webAuthnProvider;
1579
- // Set up the configuration provider with stable defaults from constants
1580
- const serviceConfig = {
1581
- apiUrl: config.apiUrl || SIGNER_CONFIG.DEFAULT_SIGNER_API_URL,
1582
- relyingParty: {
1583
- id: typeof window !== 'undefined' ? window.location.hostname : 'localhost',
1584
- name: config.relyingPartyName || SIGNER_CONFIG.DEFAULT_RELYING_PARTY_NAME,
1585
- origin: typeof window !== 'undefined' ? window.location.origin : undefined,
1586
- },
1587
- };
1588
- setConfigProvider(new WebConfigProvider(serviceConfig));
1589
- // Initialize services with the WebAuthn provider
1590
- this.authService = new AuthenticationService(this.webAuthnProvider);
1591
- this.signingService = new SigningService(this.webAuthnProvider);
1592
- this.transactionSigningService = new TransactionSigningService(this.webAuthnProvider);
1593
- }
1594
1491
  /**
1595
- * Re-export TransactionSigningService with WebAuthn provider already injected
1596
- * This provides the same interface as the original service but with automatic provider injection
1492
+ * Combined authentication flow - handles both login and registration
1493
+ * @param identifier - User identifier (email/userId)
1494
+ * @param persAccessToken - PERS JWT token for authentication
1495
+ * @param webAuthnProvider - WebAuthn provider for credential creation
1496
+ * @param relyingPartyConfig - Configuration for WebAuthn relying party
1497
+ * @returns Promise resolving to authenticated user with signer token
1597
1498
  */
1598
- get TransactionSigningService() {
1599
- return this.transactionSigningService;
1600
- }
1601
- // ========================================================================
1602
- // HIGH-LEVEL METHODS (Same as Web Project)
1603
- // ========================================================================
1604
- /**
1605
- * Complete user onboarding flow - same as web project
1606
- * Uses existing AuthenticationService and PersService
1607
- */
1608
- async authenticateUser(userInfo) {
1609
- const identifier = userInfo.identifier;
1499
+ async combinedAuthentication(identifier, persAccessToken) {
1610
1500
  try {
1611
- // Step 1: Try login first (existing user)
1612
- let signerAuthToken;
1501
+ // Step 1: Try to login with PERS token first (this will get signer JWT if user exists)
1502
+ let signerToken;
1613
1503
  try {
1614
- signerAuthToken = await this.authService.login(identifier);
1504
+ const loginResult = await this.loginWithPersToken(persAccessToken);
1505
+ // Extract token from login result
1506
+ if (loginResult && typeof loginResult === 'object' && 'access_token' in loginResult) {
1507
+ signerToken = loginResult.access_token;
1508
+ }
1509
+ else {
1510
+ throw new Error('Invalid login response format');
1511
+ }
1615
1512
  }
1616
1513
  catch (loginError) {
1617
- // User doesn't exist - register new user
1618
- await this.authService.register(identifier);
1619
- signerAuthToken = await this.authService.login(identifier);
1620
- }
1621
- // Step 2: Authenticate with PERS backend
1622
- let persAccessToken = '';
1623
- try {
1624
- const persResponse = await PersService.authenticateUser(signerAuthToken);
1625
- persAccessToken = persResponse?.accessToken || '';
1626
- }
1627
- catch (persError) {
1628
- console.warn(`PERS authentication failed:`, persError);
1629
- // Continue without PERS but provide empty token
1630
- persAccessToken = '';
1514
+ // Step 2: User doesn't exist - register with v1 API
1515
+ // 2a. Initialize registration
1516
+ const initResult = await this.initializeRegistration(persAccessToken);
1517
+ let signedChallenge = null;
1518
+ let tmpAuthToken = null;
1519
+ // 2b. Create WebAuthn credential using the challenge (if available)
1520
+ if (initResult && typeof initResult === 'object') {
1521
+ // Extract challenge and tmpAuthToken from init result with better type safety
1522
+ const initData = initResult;
1523
+ const challenge = initData.challenge;
1524
+ tmpAuthToken = initData.temporaryAuthenticationToken || initData.tmpAuthToken || null;
1525
+ if (challenge && initData.user) {
1526
+ // Construct proper WebAuthn credential creation options
1527
+ const credentialCreationOptions = {
1528
+ challenge: challenge,
1529
+ rp: {
1530
+ name: this.config.relyingPartyName || 'PERS Signer',
1531
+ id: typeof window !== 'undefined' ? window.location.hostname : 'localhost'
1532
+ },
1533
+ user: {
1534
+ id: initData.user.id,
1535
+ name: initData.user.name,
1536
+ displayName: initData.user.displayName
1537
+ },
1538
+ pubKeyCredParams: initData.pubKeyCredParams || [
1539
+ { alg: -7, type: "public-key" },
1540
+ { alg: -257, type: "public-key" }
1541
+ ],
1542
+ authenticatorSelection: initData.authenticatorSelection || {},
1543
+ attestation: initData.attestation || "direct",
1544
+ excludeCredentials: initData.excludeCredentials || []
1545
+ };
1546
+ signedChallenge = await this.webAuthnProvider.create(credentialCreationOptions);
1547
+ }
1548
+ else {
1549
+ console.warn(`[PersSignerSDK] Missing challenge or user data in init result`);
1550
+ }
1551
+ }
1552
+ // 2c. Complete registration with proper challenge data - call AuthenticationService directly
1553
+ const completeResult = await this.completeRegistrationWithChallenge(tmpAuthToken, signedChallenge, persAccessToken);
1554
+ // Extract token from registration result
1555
+ if (completeResult && typeof completeResult === 'object') {
1556
+ // Check if this is a complete JWT response with access_token
1557
+ if ('access_token' in completeResult) {
1558
+ signerToken = completeResult.access_token;
1559
+ }
1560
+ else {
1561
+ // This is a wrapped provider response - registration is not complete
1562
+ // We have user created and wallet created, but need to get JWT token
1563
+ // For now, throw an error indicating we need additional backend integration steps
1564
+ throw new Error('Registration created user and wallet but JWT token not provided. Backend integration incomplete.');
1565
+ }
1566
+ }
1567
+ else {
1568
+ throw new Error('Registration completed but no response received');
1569
+ }
1631
1570
  }
1632
1571
  return {
1633
1572
  identifier,
1634
- signerAuthToken,
1573
+ signerAuthToken: signerToken,
1635
1574
  persAccessToken
1636
1575
  };
1637
1576
  }
1638
1577
  catch (error) {
1639
- throw new Error(`User authentication failed: ${error}`);
1578
+ console.error(`[PersSignerSDK] Combined authentication failed for ${identifier}:`, error);
1579
+ throw new Error(`Combined authentication failed: ${error}`);
1640
1580
  }
1641
1581
  }
1582
+ }
1583
+
1584
+ /**
1585
+ * JWT Utility Functions
1586
+ *
1587
+ * Browser-compatible JWT handling without external dependencies
1588
+ */
1589
+ /**
1590
+ * Extract and validate JWT token payload
1591
+ * Uses a browser-compatible approach without external dependencies
1592
+ */
1593
+ function extractJWTFromToken(jwtToken) {
1594
+ if (!jwtToken) {
1595
+ return { payload: null, isExpired: false };
1596
+ }
1597
+ try {
1598
+ // Use a more compatible JWT decoding approach
1599
+ // Split JWT into parts
1600
+ const parts = jwtToken.split('.');
1601
+ if (parts.length !== 3) {
1602
+ throw new Error('Invalid JWT format');
1603
+ }
1604
+ // Decode the payload (middle part)
1605
+ const payload = JSON.parse(atob(parts[1]));
1606
+ // Check expiration
1607
+ const isExpired = payload.exp ? Date.now() >= payload.exp * 1000 : false;
1608
+ return { payload, isExpired };
1609
+ }
1610
+ catch (error) {
1611
+ console.warn('JWT decode failed:', error);
1612
+ return { payload: null, isExpired: false };
1613
+ }
1614
+ }
1615
+
1616
+ /**
1617
+ * In-memory User Cache
1618
+ *
1619
+ * Provides secure, storage-free user authentication caching
1620
+ * Uses memory-only storage with TTL expiration for security
1621
+ */
1622
+ /**
1623
+ * In-memory user cache with TTL expiration
1624
+ * Security-first approach - no persistent storage
1625
+ */
1626
+ class UserCache {
1642
1627
  /**
1643
- * Complete PERS transaction signing flow - exactly like web project
1644
- * Uses existing TransactionSigningService with injected WebAuthn provider
1628
+ * Get cached user by identifier
1629
+ * @param identifier - User identifier (email/userId)
1630
+ * @returns Cached user or null if not found/expired
1645
1631
  */
1646
- async signPersTransaction(user, transactionId) {
1647
- if (!user.signerAuthToken) {
1648
- throw new Error('Signer authentication token required');
1649
- }
1650
- if (!user.persAccessToken) {
1651
- throw new Error('PERS access token required for transaction signing');
1652
- }
1653
- try {
1654
- // Use the existing high-level service with injected WebAuthn provider
1655
- const result = await this.transactionSigningService.signTransaction({
1656
- transactionId,
1657
- authTokens: {
1658
- backendAuthToken: user.signerAuthToken,
1659
- persAccessToken: user.persAccessToken
1660
- },
1661
- ethersProviderUrl: this.config.ethersProviderUrl || 'https://sepolia.infura.io/v3/2781b4b5242343d5b0954c98f287b29e'
1662
- });
1663
- return {
1664
- success: result.success,
1665
- transactionHash: result.transactionHash,
1666
- error: result.error
1667
- };
1632
+ static get(identifier) {
1633
+ const cached = this.cache.get(identifier);
1634
+ if (!cached) {
1635
+ return null;
1668
1636
  }
1669
- catch (error) {
1670
- return {
1671
- success: false,
1672
- error: `Transaction signing failed: ${error}`
1673
- };
1637
+ // Check if expired
1638
+ if (Date.now() > cached.expiresAt) {
1639
+ this.cache.delete(identifier);
1640
+ return null;
1674
1641
  }
1642
+ return cached.user;
1675
1643
  }
1676
- // ========================================================================
1677
- // GRANULAR METHODS (For Custom Integrations)
1678
- // ========================================================================
1679
1644
  /**
1680
- * Register new user - uses existing AuthenticationService
1645
+ * Cache user with TTL
1646
+ * @param identifier - User identifier
1647
+ * @param user - Authenticated user data
1648
+ * @param ttlMs - Time to live in milliseconds (default: 5 minutes)
1681
1649
  */
1682
- async registerUser(identifier) {
1683
- try {
1684
- await this.authService.register(identifier);
1685
- const authToken = await this.authService.login(identifier);
1686
- return { authToken };
1687
- }
1688
- catch (error) {
1689
- throw new Error(`User registration failed: ${error}`);
1690
- }
1650
+ static set(identifier, user, ttlMs = this.DEFAULT_TTL_MS) {
1651
+ this.cache.set(identifier, {
1652
+ user,
1653
+ expiresAt: Date.now() + ttlMs
1654
+ });
1691
1655
  }
1692
1656
  /**
1693
- * Login existing user - uses existing AuthenticationService
1657
+ * Remove user from cache
1658
+ * @param identifier - User identifier
1694
1659
  */
1695
- async loginUser(identifier) {
1696
- try {
1697
- return await this.authService.login(identifier);
1698
- }
1699
- catch (error) {
1700
- throw new Error(`User login failed: ${error}`);
1701
- }
1660
+ static delete(identifier) {
1661
+ return this.cache.delete(identifier);
1702
1662
  }
1703
1663
  /**
1704
- * Add wallet to PERS user - uses existing PersService
1664
+ * Clear all cached users
1705
1665
  */
1706
- async addWalletToPersUser(signerAuthToken) {
1707
- try {
1708
- const persResponse = await PersService.authenticateUser(signerAuthToken);
1709
- if (!persResponse?.accessToken) {
1710
- throw new Error('Failed to get PERS access token');
1711
- }
1712
- return persResponse.accessToken;
1713
- }
1714
- catch (error) {
1715
- throw new Error(`Failed to add wallet to PERS user: ${error}`);
1716
- }
1666
+ static clear() {
1667
+ this.cache.clear();
1717
1668
  }
1718
1669
  /**
1719
- * Retrieve transaction data from PERS - uses existing PersService
1670
+ * Get cache size (for debugging)
1720
1671
  */
1721
- async retrieveTransactionData(transactionId, persAccessToken) {
1722
- try {
1723
- return await PersService.fetchPreparedTransaction(transactionId, persAccessToken);
1724
- }
1725
- catch (error) {
1726
- // Preserve original error structure for proper error handling
1727
- throw error;
1728
- }
1672
+ static size() {
1673
+ return this.cache.size;
1729
1674
  }
1730
1675
  /**
1731
- * Sign transaction data - uses existing SigningService
1732
- * Follows same patterns as KeyWallet.signPersTransaction
1676
+ * Clean up expired entries
1733
1677
  */
1734
- async signTransactionData(signerAuthToken, signingData) {
1735
- try {
1736
- const wallets = await WalletService.listWallets(signerAuthToken);
1737
- if (!wallets.items || wallets.items.length === 0) {
1738
- throw new Error('No wallet found for user');
1739
- }
1740
- const walletId = wallets.items[0].id;
1741
- // Sign based on transaction format - same pattern as KeyWallet
1742
- switch (signingData.format) {
1743
- case TRANSACTION_FORMATS.EIP_712: {
1744
- const eip712Data = signingData;
1745
- const typedData = eip712Data.typedData;
1746
- // Sign using same pattern as KeyWallet
1747
- const signResponse = await this.signingService.signTypedData(signerAuthToken, walletId, typedData.domain, { Transaction: typedData.types.Transaction }, typedData.message);
1748
- return `${signResponse.signature?.r}${signResponse.signature?.s}`;
1749
- }
1750
- default: {
1751
- // For legacy and other formats, use raw signing data
1752
- const signResponse = await this.signingService.signHash(signerAuthToken, walletId, JSON.stringify(signingData));
1753
- return `${signResponse.signature?.r}${signResponse.signature?.s}`;
1754
- }
1678
+ static cleanup() {
1679
+ const now = Date.now();
1680
+ let cleaned = 0;
1681
+ for (const [identifier, cached] of this.cache.entries()) {
1682
+ if (now > cached.expiresAt) {
1683
+ this.cache.delete(identifier);
1684
+ cleaned++;
1755
1685
  }
1756
1686
  }
1757
- catch (error) {
1758
- throw new Error(`Transaction signing failed: ${error}`);
1759
- }
1687
+ return cleaned;
1760
1688
  }
1689
+ }
1690
+ UserCache.cache = new Map();
1691
+ UserCache.DEFAULT_TTL_MS = 300000; // 5 minutes
1692
+
1693
+ /**
1694
+ * PERS Blockchain Signer SDK
1695
+ *
1696
+ * A lightweight blockchain transaction signing SDK with WebAuthn authentication.
1697
+ * Provides 5 focused methods for complete transaction lifecycle management:
1698
+ *
1699
+ * 1. loginUser(jwtToken) - Authenticate user with 5-minute caching
1700
+ * 2. signTransaction(signingData, jwtToken) - Sign transactions with auto-login
1701
+ * 3. submitTransaction(signedTx, jwtToken) - Submit signed transactions to blockchain
1702
+ * 4. signPersTransaction(jwtToken) - Legacy one-liner for backward compatibility
1703
+ * 5. signAndSubmitPersTransaction(jwtToken) - Complete sign + submit flow
1704
+ *
1705
+ * @example
1706
+ * ```typescript
1707
+ * import { createPersSignerSDK } from '@explorins/pers-signer';
1708
+ *
1709
+ * const sdk = createPersSignerSDK({
1710
+ * webAuthnProvider: myWebAuthnProvider,
1711
+ * ethersProviderUrl: 'https://ethereum-rpc.com'
1712
+ * });
1713
+ *
1714
+ * // Quick sign and submit
1715
+ * const result = await sdk.signAndSubmitPersTransaction(jwtToken);
1716
+ * if (result.success) {
1717
+ * console.log('Transaction submitted:', result.transactionHash);
1718
+ * }
1719
+ * ```
1720
+ */
1721
+ /**
1722
+ * PERS Blockchain Signer SDK Class
1723
+ *
1724
+ * Main SDK class providing blockchain transaction signing capabilities with WebAuthn authentication.
1725
+ * Implements a clean 5-method API for complete transaction lifecycle management.
1726
+ *
1727
+ * Features:
1728
+ * - WebAuthn-based secure authentication
1729
+ * - 5-minute user session caching
1730
+ * - Automatic transaction data fetching
1731
+ * - Blockchain transaction signing and submission
1732
+ * - Multi-tenant support
1733
+ *
1734
+ * @class PersSignerSDK
1735
+ */
1736
+ class PersSignerSDK {
1761
1737
  /**
1762
- * Submit signed transaction to PERS - uses existing PersService
1738
+ * Initialize the PERS Signer SDK
1739
+ *
1740
+ * @param {PersSignerConfig} config - SDK configuration object
1741
+ * @throws {Error} If required configuration is missing
1763
1742
  */
1764
- async submitTransaction(transactionId, signature, persAccessToken, transactionFormat = TRANSACTION_FORMATS.EIP_712) {
1765
- try {
1766
- const submitResult = await PersService.submitTransaction(transactionId, signature, persAccessToken, transactionFormat);
1767
- return {
1768
- transactionHash: submitResult.transaction?.transactionHash || undefined,
1769
- success: true
1770
- };
1771
- }
1772
- catch (error) {
1773
- throw new Error(`Transaction submission failed: ${error}`);
1774
- }
1743
+ constructor(config) {
1744
+ this.config = config;
1745
+ setConfigProvider(new WebConfigProvider({
1746
+ apiUrl: config.apiUrl || SIGNER_CONFIG.DEFAULT_SIGNER_API_URL,
1747
+ relyingParty: {
1748
+ id: typeof window !== 'undefined' ? window.location.hostname : 'localhost',
1749
+ name: config.relyingPartyName || SIGNER_CONFIG.DEFAULT_RELYING_PARTY_NAME,
1750
+ origin: typeof window !== 'undefined' ? window.location.origin : undefined,
1751
+ },
1752
+ }));
1753
+ this.authenticationService = new AuthenticationService(this.config);
1754
+ this.transactionSigningService = new TransactionSigningService(config);
1775
1755
  }
1776
1756
  /**
1777
- * Check if user exists - uses existing AuthenticationService
1757
+ * Authenticate user and cache session for 5 minutes
1758
+ *
1759
+ * Validates JWT token, authenticates with both signer and PERS backends,
1760
+ * and caches the authenticated user session to avoid repeated authentication.
1761
+ *
1762
+ * @param {string} jwtToken - JWT token containing user identifier and tenant info
1763
+ * @returns {Promise<AuthenticatedUser>} Authenticated user with access tokens
1764
+ * @throws {Error} If JWT is invalid, expired, or authentication fails
1765
+ *
1766
+ * @example
1767
+ * ```typescript
1768
+ * try {
1769
+ * const user = await sdk.loginUser(jwtToken);
1770
+ * console.log('Authenticated:', user.identifier);
1771
+ * } catch (error) {
1772
+ * console.error('Authentication failed:', error.message);
1773
+ * }
1774
+ * ```
1778
1775
  */
1779
- async checkUserExists(identifier) {
1780
- try {
1781
- await this.authService.login(identifier);
1782
- return true;
1776
+ async loginUser(jwtToken) {
1777
+ if (!jwtToken || typeof jwtToken !== 'string') {
1778
+ throw new Error('JWT token is required and must be a string');
1783
1779
  }
1784
- catch {
1785
- return false;
1780
+ const { payload, isExpired } = extractJWTFromToken(jwtToken);
1781
+ if (!payload || isExpired) {
1782
+ throw new Error('Invalid or expired JWT token');
1786
1783
  }
1787
- }
1788
- /**
1789
- * Get user wallets - uses existing WalletService
1790
- */
1791
- async getUserWallets(signerAuthToken) {
1792
- try {
1793
- return await WalletService.listWallets(signerAuthToken);
1784
+ const identifier = payload.identifierEmail || payload.email || payload.userId;
1785
+ const tenantId = payload.tenantId || this.config.tenantId || '';
1786
+ if (!identifier) {
1787
+ throw new Error('JWT token missing user identifier (identifierEmail, email, or userId)');
1794
1788
  }
1795
- catch (error) {
1796
- throw new Error(`Failed to get user wallets: ${error}`);
1789
+ // Check cache first
1790
+ const cachedUser = UserCache.get(identifier);
1791
+ if (cachedUser && cachedUser.tenantId === tenantId && Date.now() < cachedUser.expiresAt) {
1792
+ return cachedUser;
1797
1793
  }
1798
- }
1799
- /**
1800
- * Get transaction status - uses existing PersService
1801
- */
1802
- async getTransactionStatus(transactionId, persAccessToken) {
1803
1794
  try {
1804
- const transaction = await PersService.fetchPreparedTransaction(transactionId, persAccessToken);
1805
- return {
1806
- status: transaction.transactionStatus,
1807
- transactionHash: transaction.transaction?.transactionHash || undefined
1795
+ // Authenticate and cache
1796
+ const authResult = await this.authenticationService.combinedAuthentication(identifier, jwtToken);
1797
+ const user = {
1798
+ identifier: authResult.identifier,
1799
+ signerAuthToken: authResult.signerAuthToken,
1800
+ persAccessToken: authResult.persAccessToken,
1801
+ tenantId,
1802
+ expiresAt: Date.now() + 300000 // 5 minutes
1808
1803
  };
1804
+ UserCache.set(identifier, user);
1805
+ return user;
1809
1806
  }
1810
1807
  catch (error) {
1811
- throw new Error(`Failed to get transaction status: ${error}`);
1808
+ throw new Error(`Authentication failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
1812
1809
  }
1813
1810
  }
1814
1811
  /**
1815
- * Initialize tenant - uses existing PersService
1816
- */
1817
- async initializeTenant(tenantId) {
1818
- await PersService.initializeTenant(tenantId);
1819
- }
1820
- // ========================================================================
1821
- // JWT AUTHENTICATION METHODS (For Legacy PERS Flow)
1822
- // ========================================================================
1823
- /**
1824
- * Extract and validate JWT token from URL search parameters
1825
- * Uses a simpler approach compatible with browser environments
1812
+ * Sign a PERS transaction (legacy compatibility method)
1813
+ *
1814
+ * Automatically handles user authentication, transaction data fetching,
1815
+ * and transaction signing in a single call. This is the legacy method
1816
+ * maintained for backward compatibility.
1817
+ *
1818
+ * @param {string} jwtToken - JWT token containing transaction ID and user info
1819
+ * @returns {Promise<TransactionSigningResult>} Signing result with signature and metadata
1820
+ * @throws {Error} If authentication fails, transaction ID missing, or signing fails
1821
+ *
1822
+ * @example
1823
+ * ```typescript
1824
+ * try {
1825
+ * const result = await sdk.signPersTransaction(jwtToken);
1826
+ * if (result.success) {
1827
+ * console.log('Transaction signed:', result.signature);
1828
+ * }
1829
+ * } catch (error) {
1830
+ * console.error('Signing failed:', error.message);
1831
+ * }
1832
+ * ```
1826
1833
  */
1827
- extractJWTFromURL(searchParams) {
1828
- const jwtToken = searchParams.get('jwt') || searchParams.get('token');
1829
- if (!jwtToken) {
1830
- return { payload: null, isExpired: false };
1831
- }
1834
+ async signPersTransaction(jwtToken) {
1835
+ if (!jwtToken || typeof jwtToken !== 'string') {
1836
+ throw new Error('JWT token is required and must be a string');
1837
+ }
1838
+ const user = await this.loginUser(jwtToken);
1839
+ const { payload } = extractJWTFromToken(jwtToken);
1840
+ if (!payload?.transactionId) {
1841
+ throw new Error('JWT token missing transactionId in payload');
1842
+ }
1843
+ const authTokens = {
1844
+ signerAuthToken: user.signerAuthToken,
1845
+ persAccessToken: user.persAccessToken
1846
+ };
1832
1847
  try {
1833
- // Use a more compatible JWT decoding approach
1834
- // Split JWT into parts
1835
- const parts = jwtToken.split('.');
1836
- if (parts.length !== 3) {
1837
- throw new Error('Invalid JWT format');
1848
+ const persSigningData = await this.transactionSigningService.getPersSigningData({
1849
+ transactionId: payload.transactionId,
1850
+ authTokens,
1851
+ tenantId: user.tenantId
1852
+ });
1853
+ const result = await this.signTransaction(persSigningData, jwtToken);
1854
+ if (!result.success) {
1855
+ throw new Error(result.error || 'Transaction signing failed');
1838
1856
  }
1839
- // Decode the payload (middle part)
1840
- const payload = JSON.parse(atob(parts[1]));
1841
- // Check expiration
1842
- const isExpired = payload.exp ? Date.now() >= payload.exp * 1000 : false;
1843
- return { payload, isExpired };
1857
+ return result;
1844
1858
  }
1845
1859
  catch (error) {
1846
- console.warn('JWT decode failed:', error);
1847
- return { payload: null, isExpired: false };
1860
+ throw new Error(`PERS transaction signing failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
1848
1861
  }
1849
1862
  }
1850
1863
  /**
1851
- * Initialize tenant from JWT payload using existing PersService
1864
+ * Sign a transaction with provided signing data
1865
+ *
1866
+ * Low-level method to sign transactions when you already have the signing data.
1867
+ * Automatically handles user authentication and applies the blockchain signature.
1868
+ *
1869
+ * @param {CounterfactualWalletTransactionResponse | LegacyTransaction} signingData - Transaction data to sign
1870
+ * @param {string} jwtToken - JWT token containing transaction ID and user info
1871
+ * @returns {Promise<TransactionSigningResult>} Signing result with signature and metadata
1872
+ * @throws {Error} If authentication fails, transaction ID missing, or signing fails
1873
+ *
1874
+ * @example
1875
+ * ```typescript
1876
+ * const signingData = await getTransactionData(transactionId);
1877
+ * const result = await sdk.signTransaction(signingData, jwtToken);
1878
+ * console.log('Signed transaction:', result.signature);
1879
+ * ```
1852
1880
  */
1853
- async initializeTenantFromJWT(payload) {
1854
- if (!payload.tenantId) {
1855
- return 'default';
1881
+ async signTransaction(signingData, jwtToken) {
1882
+ if (!signingData) {
1883
+ throw new Error('Signing data is required');
1884
+ }
1885
+ if (!jwtToken || typeof jwtToken !== 'string') {
1886
+ throw new Error('JWT token is required and must be a string');
1887
+ }
1888
+ const user = await this.loginUser(jwtToken);
1889
+ const { payload } = extractJWTFromToken(jwtToken);
1890
+ if (!payload?.transactionId) {
1891
+ throw new Error('JWT token missing transactionId in payload');
1856
1892
  }
1893
+ const authTokens = {
1894
+ signerAuthToken: user.signerAuthToken,
1895
+ persAccessToken: user.persAccessToken
1896
+ };
1857
1897
  try {
1858
- // Use existing PersService.initializeTenant method
1859
- const tenantData = await PersService.initializeTenant(payload.tenantId);
1860
- // Extract project key from tenant data
1861
- const projectKey = tenantData.projectKey ||
1862
- tenantData.projectApiKey ||
1863
- tenantData.apiKey ||
1864
- payload.tenantId;
1865
- return projectKey;
1898
+ const result = await this.transactionSigningService.signTransaction({
1899
+ transactionId: payload.transactionId,
1900
+ tenantId: user.tenantId,
1901
+ authTokens,
1902
+ ethersProviderUrl: this.config.ethersProviderUrl || ''
1903
+ }, signingData);
1904
+ return result;
1866
1905
  }
1867
1906
  catch (error) {
1868
- console.warn('Tenant initialization failed:', error);
1869
- return payload.tenantId;
1907
+ throw new Error(`Transaction signing failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
1870
1908
  }
1871
1909
  }
1872
1910
  /**
1873
- * Authenticate user with PERS API using existing PersService pattern
1874
- */
1875
- /**
1876
- * Authenticate user with PERS API using existing PersService
1911
+ * Complete transaction flow: sign and submit in one call
1912
+ *
1913
+ * Convenience method that combines signing and submission into a single operation.
1914
+ * This is the recommended method for most use cases as it handles the complete
1915
+ * transaction lifecycle automatically.
1916
+ *
1917
+ * @param {string} jwtToken - JWT token containing transaction ID and user info
1918
+ * @returns {Promise<SubmissionResult>} Submission result with transaction hash and status
1919
+ * @throws {Error} If authentication, signing, or submission fails
1920
+ *
1921
+ * @example
1922
+ * ```typescript
1923
+ * try {
1924
+ * const result = await sdk.signAndSubmitPersTransaction(jwtToken);
1925
+ * if (result.success) {
1926
+ * console.log('Transaction completed:', result.transactionHash);
1927
+ * if (result.shouldRedirect) {
1928
+ * window.location.href = result.redirectUrl;
1929
+ * }
1930
+ * }
1931
+ * } catch (error) {
1932
+ * console.error('Transaction failed:', error.message);
1933
+ * }
1934
+ * ```
1877
1935
  */
1878
- async authenticatePersUser(jwtToken, projectKey) {
1936
+ async signAndSubmitPersTransaction(jwtToken) {
1937
+ if (!jwtToken || typeof jwtToken !== 'string') {
1938
+ throw new Error('JWT token is required and must be a string');
1939
+ }
1879
1940
  try {
1880
- // Configure PersService with the project key from tenant initialization
1881
- PersService.configure({ projectKey });
1882
- // Use the existing PersService method which handles all the complexity
1883
- const result = await PersService.authenticateUser(jwtToken);
1884
- // Convert to expected PersAuthResult format
1885
- return {
1886
- user: {
1887
- email: result.user?.email || undefined,
1888
- id: result.user?.id || undefined
1889
- },
1890
- accessToken: result.accessToken
1891
- };
1941
+ const signedTx = await this.signPersTransaction(jwtToken);
1942
+ const submittedTx = await this.submitTransaction(signedTx, jwtToken);
1943
+ return submittedTx;
1892
1944
  }
1893
1945
  catch (error) {
1894
- throw new Error(`PERS authentication failed: ${error}`);
1946
+ throw new Error(`Sign and submit transaction failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
1895
1947
  }
1896
1948
  }
1897
1949
  /**
1898
- * Combined PERS + DFNS authentication flow
1950
+ * Submit a signed transaction to the blockchain
1951
+ *
1952
+ * Takes a signed transaction result and submits it to the blockchain network.
1953
+ * Returns detailed submission results including transaction hash and any
1954
+ * redirect information for UI flows.
1955
+ *
1956
+ * @param {TransactionSigningResult} signingResult - Result from a successful transaction signing
1957
+ * @param {string} jwtToken - JWT token containing tenant and user info
1958
+ * @returns {Promise<SubmissionResult>} Submission result with transaction hash and status
1959
+ * @throws {Error} If signing result is invalid, JWT is missing tenantId, or submission fails
1960
+ *
1961
+ * @example
1962
+ * ```typescript
1963
+ * const signedTx = await sdk.signPersTransaction(jwtToken);
1964
+ * const result = await sdk.submitTransaction(signedTx, jwtToken);
1965
+ * console.log('Transaction submitted:', result.transactionHash);
1966
+ * ```
1899
1967
  */
1900
- async combinedAuthentication(identifier, persAccessToken) {
1968
+ async submitTransaction(signingResult, jwtToken) {
1969
+ if (!signingResult) {
1970
+ throw new Error('Signing result is required');
1971
+ }
1972
+ if (!jwtToken || typeof jwtToken !== 'string') {
1973
+ throw new Error('JWT token is required and must be a string');
1974
+ }
1975
+ if (!signingResult.success || !signingResult.signature || !signingResult.signingData) {
1976
+ throw new Error('Invalid signing result: must be successful with signature and signing data');
1977
+ }
1978
+ const { payload } = extractJWTFromToken(jwtToken);
1979
+ if (!payload?.tenantId) {
1980
+ throw new Error('JWT token missing tenantId in payload');
1981
+ }
1982
+ const user = await this.loginUser(jwtToken);
1901
1983
  try {
1902
- // Authenticate with DFNS - try login first, register if user doesn't exist
1903
- let signerAuthToken;
1904
- try {
1905
- signerAuthToken = await this.loginUser(identifier);
1906
- }
1907
- catch (loginError) {
1908
- console.log(`[PersSignerSDK] User not found, registering new user: ${identifier}`);
1909
- // User doesn't exist - register new user (registerUser already includes login)
1910
- const registrationResult = await this.registerUser(identifier);
1911
- signerAuthToken = registrationResult.authToken;
1912
- }
1984
+ const submitResult = await PersService.submitTransaction(signingResult.transactionId, signingResult.signature, user.persAccessToken, signingResult.signingData.format, payload.tenantId);
1985
+ // Transform response to SubmissionResult
1913
1986
  return {
1914
- identifier,
1915
- signerAuthToken,
1916
- persAccessToken
1987
+ success: true,
1988
+ transactionHash: submitResult.transaction.transactionHash,
1989
+ shouldRedirect: false, // Can be configured based on business requirements
1990
+ redirectUrl: undefined, // Can be set based on business logic
1991
+ error: undefined,
1992
+ submitResult: submitResult
1917
1993
  };
1918
1994
  }
1919
1995
  catch (error) {
1920
- throw new Error(`Combined authentication failed: ${error}`);
1996
+ throw new Error(`Transaction submission failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
1921
1997
  }
1922
1998
  }
1923
1999
  /**
1924
- * Complete JWT-based authentication flow (legacy PERS flow)
2000
+ * Clear user authentication cache
2001
+ *
2002
+ * Removes all cached user sessions, forcing fresh authentication
2003
+ * on the next method call. Useful for logout scenarios or when
2004
+ * switching between different user contexts.
2005
+ *
2006
+ * @example
2007
+ * ```typescript
2008
+ * // Clear cache on user logout
2009
+ * sdk.clearCache();
2010
+ * console.log('User cache cleared');
2011
+ * ```
1925
2012
  */
1926
- async authenticateWithJWT(searchParams) {
1927
- // 1. Extract JWT from URL
1928
- const { payload, isExpired } = this.extractJWTFromURL(searchParams);
1929
- if (!payload) {
1930
- // Return null instead of throwing error to allow graceful handling
1931
- return null;
1932
- }
1933
- if (isExpired) {
1934
- return {
1935
- user: {
1936
- identifier: payload.email || payload.userId || 'unknown',
1937
- signerAuthToken: '',
1938
- persAccessToken: ''
1939
- },
1940
- isExpired: true
1941
- };
1942
- }
1943
- // 2. Initialize tenant and get project key
1944
- const projectKey = await this.initializeTenantFromJWT(payload);
1945
- // 3. Get JWT token from URL
1946
- const jwtToken = searchParams.get('jwt') || searchParams.get('token');
1947
- if (!jwtToken) {
1948
- throw new Error('JWT token not found in URL parameters');
1949
- }
1950
- // 4. Authenticate with PERS
1951
- const persResult = await this.authenticatePersUser(jwtToken, projectKey);
1952
- // 5. Get identifier for DFNS authentication
1953
- const identifier = persResult.user.email || persResult.user.id;
1954
- if (!identifier) {
1955
- throw new Error('No identifier found in PERS response');
1956
- }
1957
- // 6. Combined authentication
1958
- const user = await this.combinedAuthentication(identifier, persResult.accessToken);
1959
- return {
1960
- user,
1961
- isExpired: false
1962
- };
2013
+ clearCache() {
2014
+ UserCache.clear();
1963
2015
  }
1964
2016
  }
1965
2017
 
1966
- /**
1967
- * Utility functions for handling URL search parameters in a consistent way across applications
1968
- */
1969
- /**
1970
- * Creates a URL search string that preserves all current parameters
1971
- * @param paramsToExclude Array of parameter names to exclude from the result
1972
- * @param paramsToAdd Object with additional parameters to add or override
1973
- * @param baseParams Optional URLSearchParams object to use instead of window.location.search
1974
- * @returns A search string with '?' prefix if there are parameters, or empty string if no parameters
1975
- */
1976
- function createSearchString(paramsToExclude = [], paramsToAdd = {}, baseParams) {
1977
- // Get base search params from the provided value or window location
1978
- const currentParams = new URLSearchParams(baseParams instanceof URLSearchParams
1979
- ? baseParams
1980
- : baseParams !== undefined
1981
- ? baseParams
1982
- : (typeof window !== 'undefined' ? window.location.search : ''));
1983
- // Remove excluded parameters
1984
- paramsToExclude.forEach(param => {
1985
- if (currentParams.has(param)) {
1986
- currentParams.delete(param);
1987
- }
1988
- });
1989
- // Add or update new parameters
1990
- Object.entries(paramsToAdd).forEach(([key, value]) => {
1991
- if (value !== undefined && value !== null) {
1992
- currentParams.set(key, value);
1993
- }
1994
- });
1995
- const searchString = currentParams.toString();
1996
- return searchString ? `?${searchString}` : '';
1997
- }
1998
- /**
1999
- * Creates a URL path with search parameters
2000
- * @param basePath The base path without search parameters
2001
- * @param paramsToExclude Array of parameter names to exclude from the result
2002
- * @param paramsToAdd Object with additional parameters to add or override
2003
- * @param baseParams Optional URLSearchParams object to use instead of window.location.search
2004
- * @returns A full path with search parameters
2005
- */
2006
- function createUrlWithSearchParams(basePath, paramsToExclude = [], paramsToAdd = {}, baseParams) {
2007
- const searchString = createSearchString(paramsToExclude, paramsToAdd, baseParams);
2008
- return `${basePath}${searchString}`;
2009
- }
2010
- /**
2011
- * Combines the current search parameters with new ones
2012
- * @param searchParams The current URLSearchParams object
2013
- * @param additionalParams Object with parameters to add or update
2014
- * @returns A new URLSearchParams object
2015
- */
2016
- function mergeSearchParams(searchParams, additionalParams = {}) {
2017
- const merged = new URLSearchParams(searchParams.toString());
2018
- Object.entries(additionalParams).forEach(([key, value]) => {
2019
- if (value !== undefined && value !== null) {
2020
- merged.set(key, value);
2021
- }
2022
- });
2023
- return merged;
2024
- }
2025
- /**
2026
- * Gets a specific search parameter value
2027
- * @param key The parameter name to get
2028
- * @param search Optional search string to parse (defaults to window.location.search)
2029
- * @returns The parameter value or null if not found
2030
- */
2031
- function getSearchParam(key, search) {
2032
- const params = new URLSearchParams(search || (typeof window !== 'undefined' ? window.location.search : ''));
2033
- return params.get(key);
2034
- }
2035
- /**
2036
- * Gets all search parameters as a URLSearchParams object
2037
- * @param search Optional search string to parse (defaults to window.location.search)
2038
- * @returns A URLSearchParams object
2039
- */
2040
- function getAllSearchParams(search) {
2041
- return new URLSearchParams(search || (typeof window !== 'undefined' ? window.location.search : ''));
2042
- }
2043
- /**
2044
- * Removes a specific search parameter
2045
- * @param key The parameter name to remove
2046
- * @param search Optional search string to parse (defaults to window.location.search)
2047
- * @returns A new search string without the specified parameter
2048
- */
2049
- function removeSearchParam(key, search) {
2050
- const params = new URLSearchParams(search || (typeof window !== 'undefined' ? window.location.search : ''));
2051
- params.delete(key);
2052
- const searchString = params.toString();
2053
- return searchString ? `?${searchString}` : '';
2054
- }
2055
- /**
2056
- * Updates or adds a specific search parameter
2057
- * @param key The parameter name to update
2058
- * @param value The new value for the parameter
2059
- * @param search Optional search string to parse (defaults to window.location.search)
2060
- * @returns A new search string with the updated parameter
2061
- */
2062
- function updateSearchParam(key, value, search) {
2063
- const params = new URLSearchParams(search || (typeof window !== 'undefined' ? window.location.search : ''));
2064
- params.set(key, value);
2065
- const searchString = params.toString();
2066
- return searchString ? `?${searchString}` : '';
2067
- }
2068
-
2069
- var searchParams = /*#__PURE__*/Object.freeze({
2070
- __proto__: null,
2071
- createSearchString: createSearchString,
2072
- createUrlWithSearchParams: createUrlWithSearchParams,
2073
- getAllSearchParams: getAllSearchParams,
2074
- getSearchParam: getSearchParam,
2075
- mergeSearchParams: mergeSearchParams,
2076
- removeSearchParam: removeSearchParam,
2077
- updateSearchParam: updateSearchParam
2078
- });
2079
-
2080
- /**
2081
- * Utility for debugging search parameter handling
2082
- */
2083
- /**
2084
- * Logs the current search parameters with a custom message
2085
- * This is useful for debugging search parameter preservation across navigation
2086
- *
2087
- * @param message - A descriptive message to identify where the logging happens
2088
- * @param additionalData - Optional additional data to log
2089
- */
2090
- function logSearchParams(message, additionalData) {
2091
- if (process.env.NODE_ENV !== 'production') {
2092
- // Check if window exists (for SSR compatibility)
2093
- if (typeof window === 'undefined') {
2094
- return;
2095
- }
2096
- const searchParams = new URLSearchParams(window.location.search);
2097
- searchParams.forEach((value, key) => {
2098
- });
2099
- console.group(`🔍 Search Params: ${message}`);
2100
- console.groupEnd();
2101
- }
2102
- }
2103
- /**
2104
- * Creates a search parameter tracking hook that can be used in React components
2105
- * Logs search parameter changes when the component mounts/updates
2106
- *
2107
- * @param componentName - The name of the component (for logging)
2108
- */
2109
- function createSearchParamLogger(componentName) {
2110
- return () => {
2111
- logSearchParams(`${componentName} rendered`);
2112
- };
2113
- }
2114
-
2115
- /**
2116
- * Generates a RFC4122 version 4 compliant UUID
2117
- * @returns A randomly generated UUID
2118
- */
2119
- function generateUUID() {
2120
- return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
2121
- const r = Math.random() * 16 | 0;
2122
- const v = c === 'x' ? r : (r & 0x3 | 0x8);
2123
- return v.toString(16);
2124
- });
2125
- }
2126
- /**
2127
- * Create a semi-anonymous username for users
2128
- * @returns A username in the format "user_[random string]"
2129
- */
2130
- function generateAnonymousUsername() {
2131
- const randomPart = Math.random().toString(36).substring(2, 10);
2132
- return `user_${randomPart}`;
2133
- }
2134
- /**
2135
- * Create a guest username for the application with a random prefix
2136
- * @returns A guest email in the format "randomstring_guest@explorins.com"
2137
- */
2138
- function generateGuestUsername() {
2139
- const randomPart = Math.random().toString(36).substring(2, 8); // 6 character random string
2140
- return `${randomPart}_guest@explorins.com`;
2141
- }
2142
-
2143
- /**
2144
- * Utility functions for tenant-specific operations
2145
- */
2146
- /**
2147
- * Initialize tenant-specific analytics
2148
- * @param tenantId The tenant ID
2149
- * @param getTenantConfig Function to retrieve tenant configuration
2150
- */
2151
- const initTenantAnalytics = (tenantId, getTenantConfig) => {
2152
- const tenant = getTenantConfig(tenantId);
2153
- if (!tenant) {
2154
- console.warn(`Tenant ${tenantId} not found`);
2155
- return;
2156
- }
2157
- if (tenant.analytics && tenant.analytics.enabled && tenant.analytics.trackingId) ;
2158
- };
2159
-
2160
2018
  /**
2161
2019
  * Browser-specific WebAuthn provider using DFNS browser SDK
2162
2020
  */
@@ -2210,20 +2068,6 @@ async function getBrowserWebAuthnProvider() {
2210
2068
  * Includes only browser-compatible dependencies
2211
2069
  */
2212
2070
  // Re-export all shared functionality
2213
- /**
2214
- * Create a new AuthenticationService instance with browser WebAuthn provider
2215
- */
2216
- async function createAuthenticationService() {
2217
- const webAuthnProvider = await getBrowserWebAuthnProvider();
2218
- return new AuthenticationService(webAuthnProvider);
2219
- }
2220
- /**
2221
- * Create a new SigningService instance with browser WebAuthn provider
2222
- */
2223
- async function createSigningService() {
2224
- const webAuthnProvider = await getBrowserWebAuthnProvider();
2225
- return new SigningService(webAuthnProvider);
2226
- }
2227
2071
  /**
2228
2072
  * Create a new PersSignerSDK instance with browser WebAuthn provider
2229
2073
  * @param config - SDK configuration (ethersProviderUrl is required)
@@ -11451,5 +11295,5 @@ var index = /*#__PURE__*/_mergeNamespaces({
11451
11295
  __proto__: null
11452
11296
  }, [sdkBrowser]);
11453
11297
 
11454
- export { AuthenticationService, FetchHttpClient, KeyWallet, PersService, PersSignerSDK, ReactNativeConfigProvider, SIGNABLE_STATUSES, SigningService, StaticConfigProvider, TransactionErrorHandler, TransactionSigningErrorCode, TransactionSigningService, TransactionSubmissionHandler, TransactionValidator, WalletService, WebAuthnCoordinator, WebConfigProvider, createAuthenticationService, createPersSignerSDK, createSearchParamLogger, createSearchString, createSigningService, createUrlWithSearchParams, generateAnonymousUsername, generateGuestUsername, generateUUID, getAllSearchParams, getBrowserWebAuthnProvider, getConfigProvider, getHttpClient, getSearchParam, getServiceConfig, getBrowserWebAuthnProvider as getWebAuthnProvider, initTenantAnalytics, logSearchParams, mergeSearchParams, removeSearchParam, setConfigProvider, setHttpClient, updateSearchParam };
11298
+ export { AuthenticationService, FetchHttpClient, KeyWallet, PersService, PersSignerSDK, ReactNativeConfigProvider, SIGNABLE_STATUSES, SigningService, StaticConfigProvider, TransactionErrorHandler, TransactionSigningErrorCode, TransactionSigningService, TransactionSubmissionHandler, TransactionValidator, WalletService, WebAuthnCoordinator, WebConfigProvider, createPersSignerSDK, getBrowserWebAuthnProvider, getConfigProvider, getHttpClient, getServiceConfig, getBrowserWebAuthnProvider as getWebAuthnProvider, setConfigProvider, setHttpClient };
11455
11299
  //# sourceMappingURL=browser.esm.js.map