@explorins/pers-sdk 1.2.2 → 1.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/dist/auth-admin/api/auth-admin-api.d.ts +3 -2
  2. package/dist/auth-admin/api/auth-admin-api.d.ts.map +1 -1
  3. package/dist/auth-admin.cjs +7 -2
  4. package/dist/auth-admin.cjs.map +1 -1
  5. package/dist/auth-admin.js +7 -2
  6. package/dist/auth-admin.js.map +1 -1
  7. package/dist/business/api/business-api.d.ts +17 -32
  8. package/dist/business/api/business-api.d.ts.map +1 -1
  9. package/dist/business.cjs +26 -50
  10. package/dist/business.cjs.map +1 -1
  11. package/dist/business.js +26 -50
  12. package/dist/business.js.map +1 -1
  13. package/dist/campaign/api/campaign-api.d.ts +47 -30
  14. package/dist/campaign/api/campaign-api.d.ts.map +1 -1
  15. package/dist/campaign/index.d.ts +5 -5
  16. package/dist/campaign/services/campaign-service.d.ts +6 -6
  17. package/dist/campaign/services/campaign-service.d.ts.map +1 -1
  18. package/dist/campaign.cjs +62 -41
  19. package/dist/campaign.cjs.map +1 -1
  20. package/dist/campaign.js +62 -41
  21. package/dist/campaign.js.map +1 -1
  22. package/dist/index.cjs +719 -438
  23. package/dist/index.cjs.map +1 -1
  24. package/dist/index.js +718 -439
  25. package/dist/index.js.map +1 -1
  26. package/dist/package.json +1 -1
  27. package/dist/redemption/api/redemption-api.d.ts +58 -14
  28. package/dist/redemption/api/redemption-api.d.ts.map +1 -1
  29. package/dist/redemption/index.d.ts +2 -2
  30. package/dist/redemption/models/index.d.ts +1 -1
  31. package/dist/redemption/models/index.d.ts.map +1 -1
  32. package/dist/redemption/services/redemption-service.d.ts +3 -3
  33. package/dist/redemption/services/redemption-service.d.ts.map +1 -1
  34. package/dist/redemption.cjs +89 -15
  35. package/dist/redemption.cjs.map +1 -1
  36. package/dist/redemption.js +89 -15
  37. package/dist/redemption.js.map +1 -1
  38. package/dist/shared/interfaces/pers-shared-lib.interfaces.d.ts +3 -3
  39. package/dist/shared/interfaces/pers-shared-lib.interfaces.d.ts.map +1 -1
  40. package/dist/transaction/api/transaction-api.d.ts +23 -19
  41. package/dist/transaction/api/transaction-api.d.ts.map +1 -1
  42. package/dist/transaction/index.d.ts +3 -7
  43. package/dist/transaction/index.d.ts.map +1 -1
  44. package/dist/transaction/models/index.d.ts +0 -1
  45. package/dist/transaction/models/index.d.ts.map +1 -1
  46. package/dist/transaction/services/transaction-service.d.ts +5 -7
  47. package/dist/transaction/services/transaction-service.d.ts.map +1 -1
  48. package/dist/transaction.cjs +85 -50
  49. package/dist/transaction.cjs.map +1 -1
  50. package/dist/transaction.js +85 -50
  51. package/dist/transaction.js.map +1 -1
  52. package/dist/web3/application/index.d.ts +6 -0
  53. package/dist/web3/application/index.d.ts.map +1 -0
  54. package/dist/web3/application/web3-application.service.d.ts +53 -0
  55. package/dist/web3/application/web3-application.service.d.ts.map +1 -0
  56. package/dist/web3/domain/models/index.d.ts +58 -0
  57. package/dist/web3/domain/models/index.d.ts.map +1 -0
  58. package/dist/web3/domain/services/contract-domain.service.d.ts +20 -0
  59. package/dist/web3/domain/services/contract-domain.service.d.ts.map +1 -0
  60. package/dist/web3/domain/services/index.d.ts +8 -0
  61. package/dist/web3/domain/services/index.d.ts.map +1 -0
  62. package/dist/web3/domain/services/metadata-domain.service.d.ts +12 -0
  63. package/dist/web3/domain/services/metadata-domain.service.d.ts.map +1 -0
  64. package/dist/web3/domain/services/token-domain.service.d.ts +48 -0
  65. package/dist/web3/domain/services/token-domain.service.d.ts.map +1 -0
  66. package/dist/web3/index.d.ts +10 -11
  67. package/dist/web3/index.d.ts.map +1 -1
  68. package/dist/web3/infrastructure/api/index.d.ts +6 -0
  69. package/dist/web3/infrastructure/api/index.d.ts.map +1 -0
  70. package/dist/web3/infrastructure/api/ipfs-api.d.ts +15 -0
  71. package/dist/web3/infrastructure/api/ipfs-api.d.ts.map +1 -0
  72. package/dist/web3/{api → infrastructure/api}/web3-api.d.ts +6 -2
  73. package/dist/web3/infrastructure/api/web3-api.d.ts.map +1 -0
  74. package/dist/web3/infrastructure/index.d.ts +2 -0
  75. package/dist/web3/infrastructure/index.d.ts.map +1 -0
  76. package/dist/web3.cjs +509 -336
  77. package/dist/web3.cjs.map +1 -1
  78. package/dist/web3.js +507 -336
  79. package/dist/web3.js.map +1 -1
  80. package/package.json +1 -1
  81. package/dist/web3/api/web3-api.d.ts.map +0 -1
  82. package/dist/web3/models/index.d.ts +0 -92
  83. package/dist/web3/models/index.d.ts.map +0 -1
  84. package/dist/web3/services/web3-service.d.ts +0 -21
  85. package/dist/web3/services/web3-service.d.ts.map +0 -1
package/dist/index.cjs CHANGED
@@ -1,5 +1,6 @@
1
1
  'use strict';
2
2
 
3
+ var persShared = require('@explorins/pers-shared');
3
4
  var jwtDecode = require('jwt-decode');
4
5
  var Web3 = require('web3');
5
6
  var ethers = require('ethers');
@@ -503,13 +504,15 @@ function createPersSDK(httpClient, config) {
503
504
  /**
504
505
  * Platform-Agnostic Business API Client
505
506
  *
506
- * Updated to match the new RESTful /businesses endpoints.
507
- * Uses @explorins/pers-shared DTOs for full type safety and consistency with backend.
507
+ * Updated to match the actual RESTful endpoints:
508
+ * - /businesses for business operations
509
+ * - /business-types for business type operations (separate controller)
508
510
  */
509
511
  class BusinessApi {
510
512
  constructor(apiClient) {
511
513
  this.apiClient = apiClient;
512
514
  this.basePath = '/businesses';
515
+ this.businessTypesPath = '/business-types'; // ✅ FIX: Separate controller
513
516
  }
514
517
  // ==========================================
515
518
  // 🌐 BUSINESS TYPES MANAGEMENT
@@ -517,38 +520,38 @@ class BusinessApi {
517
520
  /**
518
521
  * Get all business types (project key required)
519
522
  *
520
- * Endpoint: GET /businesses/types
523
+ * Endpoint: GET /business-types
521
524
  * Auth: @ApiSecurity('projectKey')
522
525
  */
523
526
  async getAllBusinessTypes() {
524
- return this.apiClient.get(`${this.basePath}/types`);
527
+ return this.apiClient.get(this.businessTypesPath); // ✅ FIX: Correct path
525
528
  }
526
529
  /**
527
530
  * ADMIN: Create business type
528
531
  *
529
- * Endpoint: POST /businesses/types
532
+ * Endpoint: POST /business-types
530
533
  * Auth: @TenantAdmin()
531
534
  */
532
535
  async createBusinessType(dto) {
533
- return this.apiClient.post(`${this.basePath}/types`, dto);
536
+ return this.apiClient.post(this.businessTypesPath, dto); // ✅ FIX: Correct path
534
537
  }
535
538
  /**
536
539
  * ADMIN: Update business type
537
540
  *
538
- * Endpoint: PUT /businesses/types
541
+ * Endpoint: PUT /business-types
539
542
  * Auth: @TenantAdmin()
540
543
  */
541
544
  async updateBusinessType(dto) {
542
- return this.apiClient.put(`${this.basePath}/types`, dto);
545
+ return this.apiClient.put(this.businessTypesPath, dto); // ✅ FIX: Correct path
543
546
  }
544
547
  /**
545
548
  * ADMIN: Delete business type
546
549
  *
547
- * Endpoint: DELETE /businesses/types/{id}
550
+ * Endpoint: DELETE /business-types/{id}
548
551
  * Auth: @TenantAdmin()
549
552
  */
550
553
  async deleteBusinessType(id) {
551
- return this.apiClient.delete(`${this.basePath}/types/${id}`);
554
+ return this.apiClient.delete(`${this.businessTypesPath}/${id}`); // ✅ FIX: Correct path
552
555
  }
553
556
  // ==========================================
554
557
  // 🏢 BUSINESS MANAGEMENT
@@ -563,20 +566,13 @@ class BusinessApi {
563
566
  return this.apiClient.get(`${this.basePath}/me`);
564
567
  }
565
568
  /**
566
- * Get all active businesses (project key required)
567
- *
568
- * Endpoint: GET /businesses
569
- * Auth: @ApiSecurity('projectKey')
570
- * Note: Regular users automatically get active businesses only
571
- */
572
- async getActiveBusinesses() {
573
- return this.apiClient.get(`${this.basePath}`);
574
- }
575
- /**
576
- * Get all businesses with filtering (admin users can access inactive)
569
+ * Get all businesses with role-based filtering
577
570
  *
578
571
  * Endpoint: GET /businesses?active={boolean}&sanitize={mode}
579
572
  * Auth: @ApiSecurity('projectKey') (enhanced with role-based filtering)
573
+ * Note:
574
+ * - Project API Key users: Active businesses only (automatically filtered)
575
+ * - Admin JWT users: Full access with all query parameters
580
576
  */
581
577
  async getAllBusinesses(options) {
582
578
  const params = new URLSearchParams();
@@ -591,23 +587,16 @@ class BusinessApi {
591
587
  return this.apiClient.get(url);
592
588
  }
593
589
  /**
594
- * ADMIN: Get all businesses (admin endpoint with full access)
590
+ * Get all active businesses (convenience method)
595
591
  *
596
- * Endpoint: GET /businesses/admin
597
- * Auth: @TenantAdmin()
592
+ * Endpoint: GET /businesses
593
+ * Auth: @ApiSecurity('projectKey')
598
594
  */
599
- async getAllBusinessesAdmin(options) {
600
- const params = new URLSearchParams();
601
- if (options?.active !== undefined) {
602
- params.append('active', String(options.active));
603
- }
604
- if (options?.sanitize) {
605
- params.append('sanitize', options.sanitize);
606
- }
607
- const queryString = params.toString();
608
- const url = queryString ? `${this.basePath}?${queryString}` : this.basePath;
609
- return this.apiClient.get(url);
595
+ async getActiveBusinesses() {
596
+ return this.apiClient.get(this.basePath);
610
597
  }
598
+ // ✅ REMOVED: getAllBusinessesAdmin() - No separate /admin endpoint exists
599
+ // The unified endpoint handles role-based access automatically
611
600
  /**
612
601
  * Get business by ID
613
602
  *
@@ -637,18 +626,15 @@ class BusinessApi {
637
626
  * Returns: BusinessApiKeyDTO | BusinessTokenBalancesDTO
638
627
  */
639
628
  async createBusiness(dto) {
640
- return this.apiClient.post(`${this.basePath}`, dto);
629
+ return this.apiClient.post(this.basePath, dto);
641
630
  }
642
631
  /**
643
632
  * ADMIN: Create business by display name (convenience method)
644
- *
645
- * This is a convenience wrapper that creates the proper DTO structure
646
633
  */
647
634
  async createBusinessByDisplayName(displayName) {
648
635
  const dto = {
649
636
  displayName,
650
637
  // Add other required fields based on BusinessCreateRequestDTO structure
651
- // You may need to check the DTO definition for required fields
652
638
  };
653
639
  return this.createBusiness(dto);
654
640
  }
@@ -673,21 +659,12 @@ class BusinessApi {
673
659
  /**
674
660
  * ADMIN: Toggle business active status
675
661
  *
676
- * Endpoint: PUT /businesses/{id}/activate
662
+ * Endpoint: PUT /businesses/{id}/status
677
663
  * Auth: @TenantAdmin()
678
664
  */
679
665
  async toggleBusinessActive(id, isActive) {
680
666
  const dto = { isActive };
681
- return this.apiClient.put(`${this.basePath}/${id}/status`, dto);
682
- }
683
- /**
684
- * ADMIN: Generate new business API key
685
- *
686
- * Endpoint: PUT /businesses/{id}/api-key
687
- * Auth: @TenantAdmin()
688
- */
689
- async generateNewBusinessApiKey(id) {
690
- return this.apiClient.put(`${this.basePath}/${id}/api-key`, {});
667
+ return this.apiClient.put(`${this.basePath}/${id}/status`, dto); // ✅ FIX: Correct endpoint
691
668
  }
692
669
  }
693
670
 
@@ -823,6 +800,15 @@ class TransactionApi {
823
800
  async getTransactionById(transactionId) {
824
801
  return this.apiClient.get(`${this.basePath}/${transactionId}`);
825
802
  }
803
+ /**
804
+ * Unique method to create a transaction
805
+ * @param request
806
+ * @returns
807
+ */
808
+ async createTransaction(request) {
809
+ return this.apiClient.post(`${this.basePath}`, request);
810
+ // return this.apiClient.post<TransactionDTO>(`${this.basePath}/system`, request);
811
+ }
826
812
  // ==========================================
827
813
  // AUTHENTICATED USER OPERATIONS
828
814
  // ==========================================
@@ -831,9 +817,9 @@ class TransactionApi {
831
817
  *
832
818
  * UPDATED: /transaction/auth/transaction → /transactions/user
833
819
  */
834
- async createAuthTransaction(request) {
835
- return this.apiClient.post(`${this.basePath}/user`, request);
836
- }
820
+ /* async createAuthTransaction(request: TransactionRequestDTO): Promise<TransactionRequestResponseDTO> {
821
+ return this.apiClient.post<TransactionRequestResponseDTO>(`${this.basePath}`, request);
822
+ } */
837
823
  /**
838
824
  * AUTH: Get user's sent transactions
839
825
  *
@@ -878,14 +864,6 @@ class TransactionApi {
878
864
  return this.apiClient.get(`${this.basePath}/me/sent?${params.toString()}`);
879
865
  }
880
866
  }
881
- /**
882
- * AUTH: Prepare client signed transaction
883
- *
884
- * UPDATED: /transaction/auth/prepare-signing → /transactions/prepare
885
- */
886
- async prepareClientSignedTransaction(request) {
887
- return this.apiClient.post(`${this.basePath}/prepare`, request);
888
- }
889
867
  /**
890
868
  * AUTH: Prepare existing transaction for client-side signing
891
869
  *
@@ -899,8 +877,8 @@ class TransactionApi {
899
877
  *
900
878
  * NEW ENDPOINT: POST /transactions/{id}/submit
901
879
  */
902
- async submitSignedTransaction(transactionId, signedData) {
903
- return this.apiClient.post(`${this.basePath}/${transactionId}/submit`, signedData);
880
+ async submitSignedTransaction(signedTxData) {
881
+ return this.apiClient.post(`${this.basePath}/submit`, signedTxData);
904
882
  }
905
883
  /**
906
884
  * AUTH: Burn user tokens
@@ -908,14 +886,15 @@ class TransactionApi {
908
886
  * UPDATED: Uses new user transaction endpoint with burn-specific parameters
909
887
  * Note: This might need backend confirmation on burn functionality implementation
910
888
  */
911
- async burnUserTokens(request) {
912
- // Map burn request to TransactionRequestDTO format for new endpoint
913
- const transactionRequest = {
914
- ...request,
915
- // Add any specific burn transaction parameters here
916
- };
917
- return this.apiClient.post(`${this.basePath}/user`, transactionRequest);
918
- }
889
+ /* async burnUserTokens(request: UserBurnTokenRequestDTO): Promise<TransactionRequestResponseDTO> {
890
+ // Map burn request to TransactionRequestDTO format for new endpoint
891
+ const transactionRequest: TransactionRequestDTO = {
892
+ ...request,
893
+ // Add any specific burn transaction parameters here
894
+ } as any;
895
+
896
+ return this.apiClient.post<TransactionRequestResponseDTO>(`${this.basePath}`, transactionRequest);
897
+ } */
919
898
  // ==========================================
920
899
  // BUSINESS OPERATIONS
921
900
  // ==========================================
@@ -924,9 +903,9 @@ class TransactionApi {
924
903
  *
925
904
  * UPDATED: /transaction/business/transaction → /transactions/business
926
905
  */
927
- async createBusinessTransaction(request) {
928
- return this.apiClient.post(`${this.basePath}/business`, request);
929
- }
906
+ /* async createBusinessTransaction(request: TransactionRequestDTO): Promise<TransactionRequestResponseDTO> {
907
+ return this.apiClient.post<TransactionRequestResponseDTO>(`${this.basePath}`, request);
908
+ } */
930
909
  // ==========================================
931
910
  // ADMIN OPERATIONS
932
911
  // ==========================================
@@ -935,8 +914,17 @@ class TransactionApi {
935
914
  *
936
915
  * UPDATED: /transaction/admin/transaction → /transactions/admin
937
916
  */
938
- async createAdminTransaction(request) {
939
- return this.apiClient.post(`${this.basePath}/system`, request);
917
+ /* async createAdminTransaction(request: TransactionRequestDTO): Promise<TransactionRequestResponseDTO> {
918
+ // return this.apiClient.post<TransactionRequestResponseDTO>(`${this.basePath}`, request);
919
+ return this.apiClient.post<TransactionDTO>(`${this.basePath}/system`, request);
920
+ } */
921
+ /**
922
+ * AUTH: Prepare client signed transaction
923
+ *
924
+ * UPDATED: /transaction/auth/prepare-signing → /transactions/prepare
925
+ */
926
+ async prepareClientSignedTransaction(request) {
927
+ return this.apiClient.post(`${this.basePath}`, request);
940
928
  }
941
929
  /**
942
930
  * ADMIN: Get all tenant transactions
@@ -1015,16 +1003,31 @@ class TransactionApi {
1015
1003
  *
1016
1004
  * NEW ENDPOINT: POST /transactions/query-sender
1017
1005
  */
1006
+ /**
1007
+ * Query transactions by sender using unified endpoint
1008
+ */
1018
1009
  async queryTransactionsBySender(accountSelector) {
1019
- return this.apiClient.post(`${this.basePath}/query-sender`, accountSelector);
1010
+ // Build query parameters safely
1011
+ const queryParams = {};
1012
+ if (accountSelector.accountId) {
1013
+ queryParams['participantId'] = accountSelector.accountId;
1014
+ }
1015
+ queryParams['role'] = persShared.TransactionRole.SENDER.toString();
1016
+ const params = new URLSearchParams(queryParams);
1017
+ return this.apiClient.get(`${this.basePath}?${params.toString()}`).then(response => response); // Extract items from paginated response
1020
1018
  }
1021
1019
  /**
1022
- * ADMIN: Query transactions by recipient
1023
- *
1024
- * NEW ENDPOINT: POST /transactions/query-recipient
1020
+ * Query transactions by recipient using unified endpoint
1025
1021
  */
1026
1022
  async queryTransactionsByRecipient(accountSelector) {
1027
- return this.apiClient.post(`${this.basePath}/query-recipient`, accountSelector);
1023
+ // Build query parameters safely
1024
+ const queryParams = {};
1025
+ if (accountSelector.accountId) {
1026
+ queryParams['participantId'] = accountSelector.accountId;
1027
+ }
1028
+ queryParams['role'] = persShared.TransactionRole.RECIPIENT.toString();
1029
+ const params = new URLSearchParams(queryParams);
1030
+ return this.apiClient.get(`${this.basePath}?${params.toString()}`).then(response => response); // Extract items from paginated response
1028
1031
  }
1029
1032
  /**
1030
1033
  * ADMIN: Get transaction analytics
@@ -1075,8 +1078,14 @@ class TransactionService {
1075
1078
  /**
1076
1079
  * AUTH: Create authenticated transaction
1077
1080
  */
1078
- async createAuthTransaction(request) {
1079
- return this.transactionApi.createAuthTransaction(request);
1081
+ /* async createAuthTransaction(request: TransactionRequestDTO): Promise<TransactionRequestResponseDTO> {
1082
+ return this.transactionApi.createAuthTransaction(request);
1083
+ } */
1084
+ async createTransaction(request) {
1085
+ return this.transactionApi.createTransaction(request);
1086
+ }
1087
+ async submitSignedTransaction(signedTxData) {
1088
+ return this.transactionApi.submitSignedTransaction(signedTxData);
1080
1089
  }
1081
1090
  /**
1082
1091
  * AUTH: Get user transaction history by type
@@ -1087,33 +1096,33 @@ class TransactionService {
1087
1096
  /**
1088
1097
  * AUTH: Prepare client signed transaction
1089
1098
  */
1090
- async prepareClientSignedTransaction(request) {
1091
- return this.transactionApi.prepareClientSignedTransaction(request);
1092
- }
1099
+ /* async prepareClientSignedTransaction(request: TransactionRequestDTO): Promise<TransactionRequestResponseDTO> {
1100
+ return this.transactionApi.prepareClientSignedTransaction(request);
1101
+ } */
1093
1102
  /**
1094
1103
  * AUTH: Burn user tokens
1095
1104
  */
1096
- async burnUserTokens(request) {
1097
- return this.transactionApi.burnUserTokens(request);
1098
- }
1105
+ /* async burnUserTokens(request: UserBurnTokenRequestDTO): Promise<TransactionRequestResponseDTO> {
1106
+ return this.transactionApi.burnUserTokens(request);
1107
+ } */
1099
1108
  // ==========================================
1100
1109
  // BUSINESS OPERATIONS
1101
1110
  // ==========================================
1102
1111
  /**
1103
1112
  * BUSINESS: Create business transaction
1104
1113
  */
1105
- async createBusinessTransaction(request) {
1106
- return this.transactionApi.createBusinessTransaction(request);
1107
- }
1114
+ /* async createBusinessTransaction(request: TransactionRequestDTO): Promise<TransactionRequestResponseDTO> {
1115
+ return this.transactionApi.createBusinessTransaction(request);
1116
+ } */
1108
1117
  // ==========================================
1109
1118
  // ADMIN OPERATIONS
1110
1119
  // ==========================================
1111
1120
  /**
1112
1121
  * ADMIN: Create admin transaction
1113
1122
  */
1114
- async createAdminTransaction(request) {
1115
- return this.transactionApi.createAdminTransaction(request);
1116
- }
1123
+ /* async createAdminTransaction(request: TransactionRequestDTO): Promise<TransactionRequestResponseDTO> {
1124
+ return this.transactionApi.createAdminTransaction(request);
1125
+ } */
1117
1126
  /**
1118
1127
  * ADMIN: Get all tenant transactions
1119
1128
  */
@@ -1167,15 +1176,16 @@ function createTransactionSDK(apiClient) {
1167
1176
  // Direct access to service methods (primary interface)
1168
1177
  // Public methods
1169
1178
  getTransactionById: (transactionId) => transactionService.getTransactionById(transactionId),
1179
+ createTransaction: (request) => transactionService.createTransaction(request),
1170
1180
  // Auth methods
1171
- createAuthTransaction: (request) => transactionService.createAuthTransaction(request),
1181
+ // createAuthTransaction: (request: TransactionRequestDTO) => transactionService.createAuthTransaction(request),
1172
1182
  getUserTransactionHistory: (type) => transactionService.getUserTransactionHistory(type),
1173
- prepareClientSignedTransaction: (request) => transactionService.prepareClientSignedTransaction(request),
1174
- burnUserTokens: (request) => transactionService.burnUserTokens(request),
1183
+ //prepareClientSignedTransaction: (request: TransactionRequestDTO) => transactionService.prepareClientSignedTransaction(request),
1184
+ // burnUserTokens: (request: UserBurnTokenRequestDTO) => transactionService.burnUserTokens(request),
1175
1185
  // Business methods
1176
- createBusinessTransaction: (request) => transactionService.createBusinessTransaction(request),
1186
+ // createBusinessTransaction: (request: TransactionRequestDTO) => transactionService.createBusinessTransaction(request),
1177
1187
  // Admin methods
1178
- createAdminTransaction: (request) => transactionService.createAdminTransaction(request),
1188
+ // createAdminTransaction: (request: TransactionRequestDTO) => transactionService.createAdminTransaction(request),
1179
1189
  getTenantTransactions: () => transactionService.getTenantTransactions(),
1180
1190
  getPaginatedTransactions: (params) => transactionService.getPaginatedTransactions(params),
1181
1191
  exportTransactionsCSV: () => transactionService.exportTransactionsCSV(),
@@ -1277,8 +1287,11 @@ class AuthAdminApi {
1277
1287
  * Note: JWT handling and auth bypass headers may need special implementation
1278
1288
  */
1279
1289
  async loginTenantAdmin(jwt) {
1280
- // TODO: Implement proper JWT and bypass header handling when PersApiClient supports it
1281
- return this.apiClient.post(`${this.basePath}/token`, {});
1290
+ const body = {
1291
+ authToken: jwt,
1292
+ authType: persShared.AccountOwnerType.TENANT
1293
+ };
1294
+ return this.apiClient.post(`${this.basePath}/token`, body);
1282
1295
  }
1283
1296
  /**
1284
1297
  * ADMIN: Refresh access token
@@ -1587,81 +1600,102 @@ class CampaignApi {
1587
1600
  * NEW: POST /campaign-claims/user
1588
1601
  */
1589
1602
  async claimCampaign(request) {
1590
- return this.apiClient.post('/campaign-claims/user', request);
1603
+ return this.apiClient.post('/campaign-claims', request);
1591
1604
  }
1592
1605
  /**
1593
1606
  * USER: Get claims for logged user
1594
1607
  * NEW: GET /campaign-claims/users/me
1595
1608
  */
1596
1609
  async getClaimsForLoggedUser() {
1597
- return this.apiClient.get('/campaign-claims/users/me');
1610
+ return this.apiClient.get('/campaign-claims/me');
1598
1611
  }
1599
1612
  /**
1600
- * BUSINESS: Claim campaign reward for customer
1601
- * NEW: POST /campaign-claims/business
1602
- */
1603
- async businessClaimCampaign(request) {
1604
- return this.apiClient.post('/campaign-claims/business', request);
1613
+ * ADMIN: Get all campaign claims
1614
+ * Updated to use unified endpoint
1615
+ */
1616
+ async getCampaignClaims() {
1617
+ // Admin context - no parameters needed for all claims
1618
+ return this.apiClient.get('/campaign-claims');
1605
1619
  }
1606
1620
  /**
1607
- * SYSTEM: Process automated claim
1608
- * NEW: POST /campaign-claims/system
1621
+ * ADMIN: Get campaign claims by campaign ID
1622
+ * Updated to use query parameters
1609
1623
  */
1610
- async systemClaimCampaign(request) {
1611
- return this.apiClient.post('/campaign-claims/system', request);
1624
+ async getCampaignClaimsByCampaignId(campaignId) {
1625
+ return this.apiClient.get(`/campaign-claims?campaignId=${campaignId}`);
1612
1626
  }
1613
1627
  /**
1614
- * ADMIN: Manual claim processing
1615
- * NEW: POST /campaign-claims/admin
1628
+ * ADMIN: Get campaign claims by user ID
1629
+ * Updated to use query parameters
1616
1630
  */
1617
- async adminClaimCampaign(request) {
1618
- return this.apiClient.post('/campaign-claims/admin', request);
1631
+ async getCampaignClaimsByUserId(userId) {
1632
+ return this.apiClient.get(`/campaign-claims?userId=${userId}`);
1619
1633
  }
1620
1634
  /**
1621
- * ADMIN: Get all campaign claims
1622
- * NEW: GET /campaign-claims/admin
1635
+ * ADMIN: Get campaign claims by business ID
1636
+ * Updated to use query parameters
1623
1637
  */
1624
- async getCampaignClaims() {
1625
- return this.apiClient.get('/campaign-claims/admin');
1638
+ async getCampaignClaimsByBusinessId(businessId) {
1639
+ return this.apiClient.get(`/campaign-claims?businessId=${businessId}`);
1626
1640
  }
1627
1641
  /**
1628
- * ADMIN: Get campaign claims by campaign ID
1629
- * NEW: GET /campaign-claims/admin/{campaignId}
1642
+ * ADMIN: Get campaign claims by user ID for specific campaign
1643
+ * Combined filtering using query parameters
1630
1644
  */
1631
- async getCampaignClaimsByCampaignId(campaignId) {
1632
- return this.apiClient.get(`/campaign-claims/admin/${campaignId}`);
1645
+ async getCampaignClaimsByUserAndCampaign(userId, campaignId) {
1646
+ return this.apiClient.get(`/campaign-claims?userId=${userId}&campaignId=${campaignId}`);
1633
1647
  }
1634
1648
  /**
1635
- * ADMIN: Get campaign claims by user ID
1636
- * NEW: GET /campaign-claims/admin/users/{userId}
1649
+ * ADMIN: Get campaign claims by business ID for specific campaign
1650
+ * Combined filtering using query parameters
1637
1651
  */
1638
- async getCampaignClaimsByUserId(userId) {
1639
- return this.apiClient.get(`/campaign-claims/admin/users/${userId}`);
1652
+ async getCampaignClaimsByBusinessAndCampaign(businessId, campaignId) {
1653
+ return this.apiClient.get(`/campaign-claims?businessId=${businessId}&campaignId=${campaignId}`);
1640
1654
  }
1641
1655
  /**
1642
- * ADMIN: Get campaign claims by business ID
1643
- * NEW: GET /campaign-claims/admin/businesses/{businessId}
1656
+ * USER: Get user's own claims (all campaigns)
1657
+ * Use convenience endpoint
1644
1658
  */
1645
- async getCampaignClaimsByBusinessId(businessId) {
1646
- return this.apiClient.get(`/campaign-claims/admin/businesses/${businessId}`);
1659
+ async getUserClaims() {
1660
+ return this.apiClient.get('/campaign-claims/me');
1647
1661
  }
1648
1662
  /**
1649
1663
  * USER: Get user's claims for specific campaign
1650
- * NEW: GET /campaign-claims/campaigns/{campaignId}/users/me
1664
+ * Use convenience endpoint with query parameter
1651
1665
  */
1652
1666
  async getUserClaimsForCampaign(campaignId) {
1653
- return this.apiClient.get(`/campaign-claims/campaigns/${campaignId}/users/me`);
1667
+ return this.apiClient.get(`/campaign-claims/me?campaignId=${campaignId}`);
1668
+ }
1669
+ /**
1670
+ * BUSINESS: Get business claims (all campaigns)
1671
+ * Uses unified endpoint with business context
1672
+ */
1673
+ async getBusinessClaims() {
1674
+ return this.apiClient.get('/campaign-claims');
1675
+ }
1676
+ /**
1677
+ * BUSINESS: Get business claims for specific campaign
1678
+ * Uses unified endpoint with business context and campaign filter
1679
+ */
1680
+ async getBusinessClaimsForCampaign(campaignId) {
1681
+ return this.apiClient.get(`/campaign-claims?campaignId=${campaignId}`);
1682
+ }
1683
+ /**
1684
+ * Helper: Build query string from parameters
1685
+ */
1686
+ buildQueryString(params) {
1687
+ const validParams = Object.entries(params)
1688
+ .filter(([_, value]) => value !== undefined)
1689
+ .map(([key, value]) => `${key}=${encodeURIComponent(value)}`)
1690
+ .join('&');
1691
+ return validParams ? `?${validParams}` : '';
1654
1692
  }
1655
- // ==========================================
1656
- // BACKWARD COMPATIBILITY (DEPRECATED)
1657
- // ==========================================
1658
1693
  /**
1659
- * @deprecated Use getCampaigns() instead
1660
- * LEGACY: Get campaigns with active filter
1694
+ * Flexible admin claims query with multiple filters
1661
1695
  */
1662
- async getCampaignsLegacy(active) {
1663
- console.warn('CampaignApi.getCampaignsLegacy() is deprecated. Use getCampaigns() instead.');
1664
- return this.getCampaigns(active !== undefined ? { active } : undefined);
1696
+ async getAdminClaims(filters) {
1697
+ const queryString = this.buildQueryString(filters || {});
1698
+ return this.apiClient.get(`/campaign-claims${queryString}`);
1665
1699
  }
1666
1700
  }
1667
1701
 
@@ -2172,20 +2206,21 @@ function createPaymentSDK(apiClient) {
2172
2206
  /**
2173
2207
  * Platform-Agnostic Redemption API Client (UPDATED - RESTful Design)
2174
2208
  *
2175
- * Updated to work with the new RESTful /redemptions endpoints.
2209
+ * Updated to work with the new RESTful /redemptions and /redemption-redeems endpoints.
2176
2210
  * Handles redemption operations using the PERS backend with intelligent access detection.
2177
2211
  * Uses @explorins/pers-shared DTOs for consistency with backend.
2178
2212
  *
2179
- * Migration Update: Updated all endpoints from /redemption to /redemptions
2213
+ * Migration Update: Updated all endpoints for unified controller pattern
2180
2214
  * - Removed role revelation from URLs (no more /admin, /auth paths)
2181
2215
  * - Added intelligent access detection for unified endpoints
2182
- * - Updated toggle endpoint to follow /status pattern
2183
- * - Enhanced redemption process with path-based IDs
2216
+ * - Added new /redemption-redeems endpoints for redeem processing
2217
+ * - Enhanced redemption process with role-based access control
2184
2218
  */
2185
2219
  class RedemptionApi {
2186
2220
  constructor(apiClient) {
2187
2221
  this.apiClient = apiClient;
2188
2222
  this.basePath = '/redemptions';
2223
+ this.redeemsPath = '/redemption-redeems';
2189
2224
  }
2190
2225
  // ==========================================
2191
2226
  // PUBLIC OPERATIONS (Project Key)
@@ -2220,7 +2255,7 @@ class RedemptionApi {
2220
2255
  * Updated: /redemption/type → /redemptions/types
2221
2256
  */
2222
2257
  async getRedemptionTypes() {
2223
- return this.apiClient.get(`${this.basePath}/types`);
2258
+ return this.apiClient.get(`/redemption-types`);
2224
2259
  }
2225
2260
  /**
2226
2261
  * PUBLIC: Get redemption by ID
@@ -2233,33 +2268,106 @@ class RedemptionApi {
2233
2268
  /**
2234
2269
  * PUBLIC: Get available supply for redemption
2235
2270
  *
2236
- * Updated: /redemption/:id/available-supply → /redemptions/:id/available-supply
2271
+ * Updated: /redemption/:id/available-supply → /redemptions/:id/supply
2237
2272
  */
2238
2273
  async getRedemptionAvailableSupply(id) {
2239
- return this.apiClient.get(`${this.basePath}/${id}/available-supply`);
2274
+ return this.apiClient.get(`${this.basePath}/${id}/supply`);
2240
2275
  }
2241
2276
  // ==========================================
2242
- // USER OPERATIONS (JWT + Project Key)
2277
+ // REDEMPTION EXECUTION (NEW UNIFIED ENDPOINT)
2243
2278
  // ==========================================
2244
2279
  /**
2245
- * USER: Redeem a redemption
2280
+ * Execute redemption (unified endpoint)
2246
2281
  *
2247
- * Updated: /redemption/auth/redeem /redemptions/:id/redeem
2248
- * Enhanced: Path-based redemption ID for better RESTful design
2282
+ * NEW: POST /redemption-redeems - Role-based processing
2283
+ * - USER: Direct user redemption processing
2284
+ * - ADMIN: Can process redemptions for any account type
2285
+ * - BUSINESS: Process redemptions for customers
2249
2286
  */
2250
2287
  async redeemRedemption(redemptionId) {
2251
2288
  const body = {
2252
2289
  redemptionId: redemptionId,
2253
2290
  };
2254
- return this.apiClient.post(`${this.basePath}/${redemptionId}/redeem`, body);
2291
+ return this.apiClient.post(this.redeemsPath, body);
2292
+ }
2293
+ // ==========================================
2294
+ // REDEMPTION REDEEMS QUERIES (NEW ENDPOINTS)
2295
+ // ==========================================
2296
+ /**
2297
+ * Get redemption redeems with filtering (unified endpoint)
2298
+ *
2299
+ * NEW: GET /redemption-redeems with query parameters
2300
+ * Role-based access: Users see only their own, admins can filter by userId/businessId
2301
+ */
2302
+ async getRedemptionRedeems(filters) {
2303
+ let url = this.redeemsPath;
2304
+ const params = new URLSearchParams();
2305
+ if (filters?.redemptionId)
2306
+ params.append('redemptionId', filters.redemptionId);
2307
+ if (filters?.userId)
2308
+ params.append('userId', filters.userId);
2309
+ if (filters?.businessId)
2310
+ params.append('businessId', filters.businessId);
2311
+ const queryString = params.toString();
2312
+ if (queryString) {
2313
+ url += `?${queryString}`;
2314
+ }
2315
+ return this.apiClient.get(url);
2316
+ }
2317
+ /**
2318
+ * Get specific redemption redeem by ID
2319
+ *
2320
+ * NEW: GET /redemption-redeems/:id
2321
+ */
2322
+ async getRedemptionRedeemById(id) {
2323
+ return this.apiClient.get(`${this.redeemsPath}/${id}`);
2324
+ }
2325
+ /**
2326
+ * USER: Get my redemption redeems (convenience endpoint)
2327
+ *
2328
+ * NEW: GET /redemption-redeems/me with optional filtering
2329
+ */
2330
+ async getMyRedemptionRedeems(redemptionId) {
2331
+ let url = `${this.redeemsPath}/me`;
2332
+ if (redemptionId) {
2333
+ url += `?redemptionId=${redemptionId}`;
2334
+ }
2335
+ return this.apiClient.get(url);
2336
+ }
2337
+ /**
2338
+ * ADMIN: Get redemption redeems by user ID
2339
+ *
2340
+ * NEW: GET /redemption-redeems?userId=X
2341
+ */
2342
+ async getRedemptionRedeemsByUserId(userId, redemptionId) {
2343
+ return this.getRedemptionRedeems({ userId, redemptionId });
2344
+ }
2345
+ /**
2346
+ * ADMIN: Get redemption redeems by business ID
2347
+ *
2348
+ * NEW: GET /redemption-redeems?businessId=X
2349
+ */
2350
+ async getRedemptionRedeemsByBusinessId(businessId, redemptionId) {
2351
+ return this.getRedemptionRedeems({ businessId, redemptionId });
2255
2352
  }
2353
+ /**
2354
+ * ADMIN: Get redemption redeems by redemption ID
2355
+ *
2356
+ * NEW: GET /redemption-redeems?redemptionId=X
2357
+ */
2358
+ async getRedemptionRedeemsByRedemptionId(redemptionId) {
2359
+ return this.getRedemptionRedeems({ redemptionId });
2360
+ }
2361
+ // ==========================================
2362
+ // USER OPERATIONS (JWT + Project Key)
2363
+ // ==========================================
2256
2364
  /**
2257
2365
  * USER: Get user redemption history
2258
2366
  *
2259
- * Updated: /redemption/auth/redeem /redemptions/me/history
2367
+ * Updated: Uses new convenience endpoint /redemption-redeems/me
2260
2368
  */
2261
2369
  async getUserRedemptionHistory() {
2262
- return this.apiClient.get(`${this.basePath}/me/history`);
2370
+ return this.getMyRedemptionRedeems();
2263
2371
  }
2264
2372
  /**
2265
2373
  * USER: Get user redemptions (backward compatibility)
@@ -2328,7 +2436,7 @@ class RedemptionApi {
2328
2436
  * Updated: /redemption/admin/type → /redemptions/types
2329
2437
  */
2330
2438
  async createRedemptionType(redemptionType) {
2331
- return this.apiClient.post(`${this.basePath}/types`, redemptionType);
2439
+ return this.apiClient.post(`${this.basePath}/redemption-types`, redemptionType);
2332
2440
  }
2333
2441
  // ==========================================
2334
2442
  // TOKEN UNIT MANAGEMENT (Admin)
@@ -4063,311 +4171,445 @@ function createWeb3ChainSDK(apiClient, providerService) {
4063
4171
  };
4064
4172
  }
4065
4173
 
4066
- class Web3Api {
4067
- constructor(web3ChainService) {
4068
- this.web3ChainService = web3ChainService;
4174
+ /**
4175
+ * TokenDomainService - Domain service for token operations
4176
+ * Implements business logic for token balance, metadata, and collection operations
4177
+ */
4178
+ class TokenDomainService {
4179
+ constructor(web3Api, metadataService, contractService) {
4180
+ this.web3Api = web3Api;
4181
+ this.metadataService = metadataService;
4182
+ this.contractService = contractService;
4069
4183
  }
4070
4184
  async getTokenBalance(request) {
4071
- const web3 = await this.web3ChainService.getWeb3ByChainId(request.chainId);
4072
- const tokenContract = web3Ts.getSmartContractInstance(request.contractAddress, request.abi, web3);
4073
- const balance = await web3Ts.getAddressTokenBalanceByContract(tokenContract, request.accountAddress, request.tokenId);
4074
- return Number(balance);
4075
- }
4076
- async getTokenUri(request) {
4077
- const web3 = await this.web3ChainService.getWeb3ByChainId(request.chainId);
4078
- // ✅ DIRECT: Use web3-ts functions directly
4079
- const tokenContract = web3Ts.getSmartContractInstance(request.contractAddress, request.abi, web3);
4080
- const tokenId = Number(request.tokenId);
4081
- const tokenUri = await web3Ts.getTokenUri(tokenContract, tokenId);
4082
- return String(tokenUri);
4083
- }
4084
- async getTokenOfOwnerByIndex(request) {
4085
- const web3 = await this.web3ChainService.getWeb3ByChainId(request.chainId);
4086
- // ✅ DIRECT: Use web3-ts functions directly
4087
- const tokenContract = web3Ts.getSmartContractInstance(request.contractAddress, request.abi, web3);
4088
- const tokenId = await web3Ts.getTokenOfOwnerByIndex(tokenContract, request.accountAddress, request.tokenIndex);
4089
- return String(tokenId);
4090
- }
4091
- }
4092
-
4093
- class SimpleCache {
4094
- constructor() {
4095
- this.storage = {};
4096
- this.defaultTTL = 10 * 1000; // 10 seconds
4097
- }
4098
- set(key, data, ttl) {
4099
- this.storage[key] = {
4100
- data,
4101
- timestamp: Date.now(),
4102
- ttl: ttl ?? this.defaultTTL
4103
- };
4104
- }
4105
- get(key) {
4106
- const entry = this.storage[key];
4107
- if (!entry) {
4108
- return null;
4109
- }
4110
- const now = Date.now();
4111
- const isExpired = (now - entry.timestamp) > entry.ttl;
4112
- if (isExpired) {
4113
- delete this.storage[key];
4114
- return null;
4115
- }
4116
- return entry.data;
4117
- }
4118
- clear() {
4119
- this.storage = {};
4120
- }
4121
- cleanup() {
4122
- const now = Date.now();
4123
- Object.keys(this.storage).forEach(key => {
4124
- const entry = this.storage[key];
4125
- if ((now - entry.timestamp) > entry.ttl) {
4126
- delete this.storage[key];
4127
- }
4128
- });
4129
- }
4130
- }
4131
-
4132
- class Web3Service {
4133
- constructor(web3Api, web3ChainService) {
4134
- this.web3Api = web3Api;
4135
- this.web3ChainService = web3ChainService;
4136
- //temporary fix, remove when the backend supports custom gateways
4137
- this.defaultIpfsGatewayDomain = 'pers.mypinata.cloud';
4138
- // ✅ CACHE: Simple 10-second cache instance
4139
- this.cache = new SimpleCache();
4140
- this.cleanupInterval = null;
4141
- this.cleanupInterval = setInterval(() => {
4142
- this.cache.cleanup();
4143
- }, 30 * 1000);
4144
- }
4145
- destroy() {
4146
- if (this.cleanupInterval) {
4147
- clearInterval(this.cleanupInterval);
4148
- this.cleanupInterval = null;
4149
- }
4150
- this.cache.clear();
4151
- }
4152
- async getERC20Balance(request) {
4153
- const cacheKey = `erc20_balance_${request.accountAddress}_${request.token.contractAddress}_${request.token.chainId}`;
4154
- // ✅ CACHE CHECK: Try to get from cache first
4155
- const cached = this.cache.get(cacheKey);
4156
- if (cached) {
4157
- console.debug(`💾 [Web3Service] Using cached ERC20 balance for ${request.token.symbol}`);
4158
- return cached;
4159
- }
4160
- console.debug(`🔄 [Web3Service] Fetching fresh ERC20 balance for ${request.token.symbol}`);
4161
- const rawBalance = await this.web3Api.getTokenBalance({
4185
+ const balance = await this.web3Api.getTokenBalance({
4162
4186
  accountAddress: request.accountAddress,
4163
- contractAddress: request.token.contractAddress,
4164
- abi: request.token.abi,
4165
- tokenId: null, // Always null for ERC20
4166
- chainId: request.token.chainId
4187
+ contractAddress: request.contractAddress,
4188
+ abi: request.abi,
4189
+ tokenId: request.tokenId,
4190
+ chainId: request.chainId
4167
4191
  });
4168
- const decimals = request.token.decimals ?? 18;
4169
- const symbol = request.token.symbol ?? 'UNKNOWN';
4170
- const response = {
4171
- rawBalance,
4172
- formattedBalance: this.formatBalance(rawBalance, decimals),
4173
- decimals,
4174
- symbol,
4175
- hasBalance: rawBalance > 0
4192
+ return {
4193
+ tokenId: request.tokenId,
4194
+ balance,
4195
+ hasBalance: balance > 0,
4196
+ metadata: null
4176
4197
  };
4177
- // ✅ CACHE SET: Store result in cache
4178
- this.cache.set(cacheKey, response);
4179
- return response;
4180
- }
4181
- async getERC1155Collection(request) {
4182
- // ✅ CACHE KEY: Create unique cache key for collection request
4183
- const contractAddresses = request.tokens.map(t => t.contractAddress).sort().join(',');
4184
- const cacheKey = `erc1155_collection_${request.accountAddress}_${contractAddresses}`;
4185
- // ✅ CACHE CHECK: Try to get from cache first
4186
- const cached = this.cache.get(cacheKey);
4187
- if (cached) {
4188
- console.debug(`💾 [Web3Service] Using cached ERC1155 collection for ${request.accountAddress}`);
4189
- return cached;
4190
- }
4191
- console.debug(`🔄 [Web3Service] Fetching fresh ERC1155 collection for ${request.accountAddress}`);
4192
- const tokenResults = await Promise.all(request.tokens.map(async (token) => {
4193
- // ✅ FIXED: Handle null metadata properly
4194
- const tokenIds = token.metadata?.map(m => m.tokenMetadataIncrementalId?.toString()).filter((id) => id !== undefined) ?? [];
4195
- // Check balance for each known tokenId
4196
- const balanceResults = await Promise.allSettled(tokenIds.map(async (tokenId) => {
4197
- try {
4198
- const rawBalance = await this.web3Api.getTokenBalance({
4199
- accountAddress: request.accountAddress,
4200
- contractAddress: token.contractAddress,
4201
- abi: token.abi,
4202
- tokenId,
4203
- chainId: token.chainId
4204
- });
4205
- const decimals = token.decimals ?? 0; // ERC1155 usually no decimals
4206
- return {
4207
- tokenId,
4208
- balance: rawBalance,
4209
- formattedBalance: this.formatBalance(rawBalance, decimals),
4210
- hasBalance: rawBalance > 0,
4211
- // ✅ FIXED: Convert null to undefined for findMetadata
4212
- metadata: this.findMetadata(token.metadata ?? undefined, tokenId)
4213
- };
4214
- }
4215
- catch (error) {
4216
- console.warn(`Failed to get balance for token ${token.contractAddress}:${tokenId}`, error);
4217
- return null; // Skip failed tokens
4218
- }
4219
- }));
4220
- // Filter successful results with balance > 0
4221
- const successfulResults = [];
4222
- for (const result of balanceResults) {
4223
- if (result.status === 'fulfilled' && result.value !== null && result.value.hasBalance) {
4224
- successfulResults.push(result.value);
4198
+ }
4199
+ async getTokenWithMetadata(params) {
4200
+ try {
4201
+ const balance = await this.web3Api.getTokenBalance({
4202
+ accountAddress: params.accountAddress,
4203
+ contractAddress: params.contractAddress,
4204
+ abi: params.abi,
4205
+ tokenId: params.tokenId,
4206
+ chainId: params.chainId
4207
+ });
4208
+ let metadata = null;
4209
+ if (balance > 0) {
4210
+ const tokenUri = await this.web3Api.getTokenUri({
4211
+ contractAddress: params.contractAddress,
4212
+ abi: params.abi,
4213
+ tokenId: params.tokenId,
4214
+ chainId: params.chainId
4215
+ });
4216
+ if (tokenUri) {
4217
+ metadata = await this.metadataService.fetchAndProcessMetadata(tokenUri, params.chainId);
4225
4218
  }
4226
4219
  }
4227
4220
  return {
4228
- token,
4229
- results: successfulResults
4221
+ tokenId: params.tokenId,
4222
+ balance,
4223
+ hasBalance: balance > 0,
4224
+ metadata
4230
4225
  };
4231
- }));
4232
- const response = {
4233
- accountAddress: request.accountAddress,
4234
- tokens: tokenResults.filter(t => t.results.length > 0)
4235
- };
4236
- // ✅ CACHE SET: Store complete collection result
4237
- this.cache.set(cacheKey, response);
4238
- return response;
4239
- }
4240
- async getERC721Collection(request) {
4241
- // ✅ CACHE KEY: Create unique cache key for NFT collection
4242
- const contractAddresses = request.nftContracts.map(t => t.contractAddress).sort().join(',');
4243
- const maxNFTs = request.maxNFTsPerContract || 50;
4244
- const cacheKey = `erc721_collection_${request.accountAddress}_${contractAddresses}_${maxNFTs}`;
4245
- // ✅ CACHE CHECK: Try to get from cache first
4246
- const cached = this.cache.get(cacheKey);
4247
- if (cached) {
4248
- console.debug(`💾 [Web3Service] Using cached ERC721 collection for ${request.accountAddress}`);
4249
- return cached;
4250
4226
  }
4251
- console.debug(`🔄 [Web3Service] Fetching fresh ERC721 collection for ${request.accountAddress}`);
4252
- const startTime = Date.now();
4253
- const contractResults = await Promise.all(request.nftContracts.map(async (token) => {
4254
- try {
4255
- const totalBalance = await this.web3Api.getTokenBalance({
4256
- accountAddress: request.accountAddress,
4257
- contractAddress: token.contractAddress,
4258
- abi: token.abi,
4259
- tokenId: null,
4260
- chainId: token.chainId
4227
+ catch (error) {
4228
+ console.error('Error getting token with metadata:', error);
4229
+ return {
4230
+ tokenId: params.tokenId,
4231
+ balance: 0,
4232
+ hasBalance: false,
4233
+ metadata: null
4234
+ };
4235
+ }
4236
+ }
4237
+ async getTokenCollection(params) {
4238
+ try {
4239
+ const contractAnalysis = this.contractService.analyzeContract(params.abi);
4240
+ const tokens = [];
4241
+ if (!contractAnalysis.hasEnumeration && !contractAnalysis.isERC1155) {
4242
+ console.warn('Contract does not support enumeration, cannot retrieve full collection');
4243
+ return {
4244
+ accountAddress: params.accountAddress,
4245
+ contractAddress: params.contractAddress,
4246
+ totalBalance: 0,
4247
+ tokensRetrieved: 0,
4248
+ tokens: [],
4249
+ note: 'Contract does not support enumeration'
4250
+ };
4251
+ }
4252
+ else if (contractAnalysis.isERC1155) {
4253
+ const tokenIdsToProcess = params.tokenIds || [];
4254
+ if (tokenIdsToProcess.length > 0) {
4255
+ for (const tokenId of tokenIdsToProcess) {
4256
+ const tokenBalance = await this.getTokenWithMetadata({
4257
+ accountAddress: params.accountAddress,
4258
+ contractAddress: params.contractAddress,
4259
+ abi: params.abi,
4260
+ tokenId,
4261
+ chainId: params.chainId
4262
+ });
4263
+ tokens.push(tokenBalance);
4264
+ }
4265
+ }
4266
+ console.log('ERC-1155 User balances:', tokens);
4267
+ // ERC-1155: Cannot enumerate without knowing token IDs
4268
+ // Would need to use events or provide specific token IDs
4269
+ console.warn('ERC-1155 collection retrieval requires specific token IDs or event analysis');
4270
+ return {
4271
+ accountAddress: params.accountAddress,
4272
+ contractAddress: params.contractAddress,
4273
+ totalBalance: 0,
4274
+ tokensRetrieved: 0,
4275
+ tokens: tokens,
4276
+ note: 'ERC-1155 collection retrieval requires specific token IDs. Use getTokenWithMetadata() for individual tokens.'
4277
+ };
4278
+ }
4279
+ // Handle different token standards
4280
+ if (contractAnalysis.isERC721) {
4281
+ // ERC-721: Get user's total balance and enumerate through tokens
4282
+ const userBalance = await this.web3Api.getTokenBalance({
4283
+ accountAddress: params.accountAddress,
4284
+ contractAddress: params.contractAddress,
4285
+ abi: params.abi,
4286
+ tokenId: null, // null for ERC-721 total balance
4287
+ chainId: params.chainId
4261
4288
  });
4262
- if (totalBalance === 0) {
4289
+ console.log(`ERC-721 User balance for ${params.accountAddress}:`, userBalance);
4290
+ if (userBalance === 0) {
4263
4291
  return {
4264
- token,
4265
- totalNFTs: 0,
4266
- nfts: [],
4267
- hasMore: false
4292
+ accountAddress: params.accountAddress,
4293
+ contractAddress: params.contractAddress,
4294
+ totalBalance: 0,
4295
+ tokensRetrieved: 0,
4296
+ tokens: []
4268
4297
  };
4269
4298
  }
4270
- const nftsToLoad = Math.min(totalBalance, maxNFTs);
4271
- const nftResults = await Promise.allSettled(Array.from({ length: nftsToLoad }, async (_, index) => {
4299
+ // Enumerate through user's tokens
4300
+ const maxTokens = params.maxTokens || userBalance;
4301
+ const tokensToRetrieve = Math.min(maxTokens, userBalance);
4302
+ for (let i = 0; i < tokensToRetrieve; i++) {
4272
4303
  try {
4273
4304
  const tokenId = await this.web3Api.getTokenOfOwnerByIndex({
4274
- contractAddress: token.contractAddress,
4275
- abi: token.abi,
4276
- accountAddress: request.accountAddress,
4277
- tokenIndex: index,
4278
- chainId: token.chainId
4305
+ contractAddress: params.contractAddress,
4306
+ abi: params.abi,
4307
+ accountAddress: params.accountAddress,
4308
+ tokenIndex: i,
4309
+ chainId: params.chainId
4279
4310
  });
4280
- const tokenUri = await this.web3Api.getTokenUri({
4281
- contractAddress: token.contractAddress,
4282
- abi: token.abi,
4311
+ const tokenWithMetadata = await this.getTokenWithMetadata({
4312
+ accountAddress: params.accountAddress,
4313
+ contractAddress: params.contractAddress,
4314
+ abi: params.abi,
4283
4315
  tokenId,
4284
- chainId: token.chainId
4316
+ chainId: params.chainId
4285
4317
  });
4286
- const metadata = await this.fetchMetadata(tokenUri, token.chainId);
4287
- const nftItem = {
4288
- tokenId,
4289
- name: metadata?.name || `Token #${tokenId}`,
4290
- description: metadata?.description || '',
4291
- imageUrl: await this.resolveIPFSUrl(metadata?.image || '', token.chainId),
4292
- rawBalance: 1,
4293
- formattedBalance: '1',
4294
- hasBalance: true,
4295
- metadata,
4296
- tokenIndex: index
4297
- };
4298
- return nftItem;
4318
+ if (tokenWithMetadata.hasBalance) {
4319
+ tokens.push(tokenWithMetadata);
4320
+ }
4299
4321
  }
4300
4322
  catch (error) {
4301
- console.warn(`Failed to load NFT at index ${index} for ${token.symbol}:`, error);
4302
- return null;
4323
+ console.warn(`Error retrieving ERC-721 token at index ${i}:`, error);
4324
+ continue;
4303
4325
  }
4304
- }));
4305
- // ✅ FIXED: Usar tipo específico NFTItem
4306
- const successfulNFTs = nftResults
4307
- .filter((result) => result.status === 'fulfilled' && result.value !== null)
4308
- .map(result => result.value);
4309
- return {
4310
- token,
4311
- totalNFTs: totalBalance,
4312
- nfts: successfulNFTs,
4313
- hasMore: totalBalance > maxNFTs
4314
- };
4326
+ }
4315
4327
  }
4316
- catch (error) {
4317
- console.error(`Failed to load NFT collection for ${token.symbol}:`, error);
4328
+ else {
4329
+ // Unknown standard
4318
4330
  return {
4319
- token,
4320
- totalNFTs: 0,
4321
- nfts: [],
4322
- hasMore: false
4331
+ accountAddress: params.accountAddress,
4332
+ contractAddress: params.contractAddress,
4333
+ totalBalance: 0,
4334
+ tokensRetrieved: 0,
4335
+ tokens: [],
4336
+ note: 'Unsupported token standard for collection retrieval'
4323
4337
  };
4324
4338
  }
4325
- }));
4326
- const totalNFTs = contractResults.reduce((sum, contract) => sum + contract.nfts.length, 0);
4327
- const loadingTime = Date.now() - startTime;
4328
- const response = {
4329
- accountAddress: request.accountAddress,
4330
- contracts: contractResults,
4331
- summary: {
4332
- totalContracts: request.nftContracts.length,
4333
- totalNFTs,
4334
- loadingTime
4339
+ // Calculate total balance based on retrieved tokens
4340
+ let totalBalance = 0;
4341
+ if (contractAnalysis.isERC721) {
4342
+ // For ERC-721, total balance is the number of unique tokens owned
4343
+ totalBalance = tokens.length;
4344
+ }
4345
+ else {
4346
+ // For other standards, sum up individual token balances
4347
+ totalBalance = tokens.reduce((sum, token) => sum + token.balance, 0);
4348
+ }
4349
+ return {
4350
+ accountAddress: params.accountAddress,
4351
+ contractAddress: params.contractAddress,
4352
+ totalBalance,
4353
+ tokensRetrieved: tokens.length,
4354
+ tokens
4355
+ };
4356
+ }
4357
+ catch (error) {
4358
+ console.error('Error getting token collection:', error);
4359
+ return {
4360
+ accountAddress: params.accountAddress,
4361
+ contractAddress: params.contractAddress,
4362
+ totalBalance: 0,
4363
+ tokensRetrieved: 0,
4364
+ tokens: [],
4365
+ note: 'Error retrieving collection'
4366
+ };
4367
+ }
4368
+ }
4369
+ async getTokenMetadata(params) {
4370
+ try {
4371
+ const tokenUri = await this.web3Api.getTokenUri({
4372
+ contractAddress: params.contractAddress,
4373
+ abi: params.abi,
4374
+ tokenId: params.tokenId,
4375
+ chainId: params.chainId
4376
+ });
4377
+ let metadata = null;
4378
+ if (tokenUri) {
4379
+ metadata = await this.metadataService.fetchAndProcessMetadata(tokenUri, params.chainId);
4335
4380
  }
4381
+ return {
4382
+ tokenId: params.tokenId,
4383
+ tokenUri,
4384
+ metadata
4385
+ };
4386
+ }
4387
+ catch (error) {
4388
+ console.error('Error getting token metadata:', error);
4389
+ return {
4390
+ tokenId: params.tokenId,
4391
+ tokenUri: null,
4392
+ metadata: null
4393
+ };
4394
+ }
4395
+ }
4396
+ }
4397
+
4398
+ /**
4399
+ * MetadataDomainService - Clean IPFS metadata resolution
4400
+ */
4401
+ class MetadataDomainService {
4402
+ constructor(ipfsApi) {
4403
+ this.ipfsApi = ipfsApi;
4404
+ }
4405
+ async fetchAndProcessMetadata(tokenUri, chainId) {
4406
+ return this.ipfsApi.fetchAndProcessMetadata(tokenUri, chainId);
4407
+ }
4408
+ async resolveIPFSUrl(url, chainId) {
4409
+ return this.ipfsApi.resolveIPFSUrl(url, chainId);
4410
+ }
4411
+ }
4412
+
4413
+ /**
4414
+ * ContractDomainService - Clean contract analysis without external dependencies
4415
+ */
4416
+ class ContractDomainService {
4417
+ constructor() { }
4418
+ analyzeContract(abi) {
4419
+ const methods = abi.filter(item => item.type === 'function').map(item => item.name);
4420
+ // ERC-721 detection
4421
+ const hasOwnerOf = methods.includes('ownerOf');
4422
+ const hasTokenURI = methods.includes('tokenURI');
4423
+ const hasTransferFrom = methods.includes('transferFrom');
4424
+ const isERC721 = hasOwnerOf && hasTokenURI && hasTransferFrom;
4425
+ // ERC-1155 detection
4426
+ const hasBalanceOfBatch = methods.includes('balanceOfBatch');
4427
+ const hasSafeBatchTransferFrom = methods.includes('safeBatchTransferFrom');
4428
+ const hasURI = methods.includes('uri');
4429
+ const isERC1155 = hasBalanceOfBatch && hasSafeBatchTransferFrom && hasURI;
4430
+ return {
4431
+ hasEnumeration: methods.includes('tokenByIndex') && methods.includes('totalSupply'),
4432
+ hasOwnerOf,
4433
+ hasBalanceOf: methods.includes('balanceOf'),
4434
+ hasTokenURI,
4435
+ hasTransfer: methods.includes('transfer') || methods.includes('transferFrom'),
4436
+ hasApprove: methods.includes('approve'),
4437
+ isERC721,
4438
+ isERC1155
4336
4439
  };
4337
- // ✅ CACHE SET: Store complete collection response
4338
- this.cache.set(cacheKey, response);
4339
- return response;
4340
4440
  }
4341
- // ==========================================
4342
- // HELPER METHODS
4343
- // ==========================================
4344
- formatBalance(rawBalance, decimals) {
4345
- const balance = rawBalance / Math.pow(10, decimals);
4346
- return balance.toLocaleString('en-US', {
4347
- minimumFractionDigits: 0,
4348
- maximumFractionDigits: decimals > 0 ? 2 : 0
4441
+ supportsEnumeration(abi) {
4442
+ return this.analyzeContract(abi).hasEnumeration;
4443
+ }
4444
+ supportsMethod(abi, methodName) {
4445
+ const methods = abi.filter(item => item.type === 'function').map(item => item.name);
4446
+ return methods.includes(methodName);
4447
+ }
4448
+ }
4449
+
4450
+ /**
4451
+ * Web3ApplicationService - Application layer entrance point
4452
+ * Orchestrates domain services and provides clean public interface
4453
+ * Simplified architecture with concrete classes
4454
+ */
4455
+ class Web3ApplicationService {
4456
+ constructor(web3Api, ipfsApi) {
4457
+ // Type-safe metadata conversion methods for ERC-721/ERC-1155 standards
4458
+ this.metadataMapper = {
4459
+ fromERCStandard: (ercMetadata) => ({
4460
+ name: ercMetadata.name || '',
4461
+ description: ercMetadata.description || '',
4462
+ imageUrl: ercMetadata.image || '',
4463
+ externalUrl: ercMetadata.external_url,
4464
+ animationUrl: ercMetadata.animation_url,
4465
+ animationUrlConverted: undefined, // Will be set by IPFS conversion
4466
+ attributes: ercMetadata.attributes || [],
4467
+ ...ercMetadata
4468
+ }),
4469
+ toERCStandard: (metadata) => ({
4470
+ name: metadata.name,
4471
+ description: metadata.description,
4472
+ image: metadata.imageUrl,
4473
+ animation_url: metadata.animationUrl,
4474
+ external_url: metadata.externalUrl,
4475
+ attributes: metadata.attributes,
4476
+ ...Object.fromEntries(Object.entries(metadata).filter(([key]) => !['name', 'description', 'imageUrl', 'animationUrl', 'externalUrl', 'attributes', 'animationUrlConverted'].includes(key)))
4477
+ })
4478
+ };
4479
+ // Create domain services with injected infrastructure dependencies
4480
+ this.contractDomainService = new ContractDomainService();
4481
+ this.metadataDomainService = new MetadataDomainService(ipfsApi);
4482
+ this.tokenDomainService = new TokenDomainService(web3Api, this.metadataDomainService, this.contractDomainService);
4483
+ }
4484
+ /**
4485
+ * Get balance and metadata for a specific token
4486
+ */
4487
+ async getSpecificTokenBalance(request) {
4488
+ if (!request.tokenId) {
4489
+ return this.tokenDomainService.getTokenBalance({
4490
+ accountAddress: request.accountAddress || '',
4491
+ contractAddress: request.contractAddress,
4492
+ abi: request.abi,
4493
+ tokenId: '',
4494
+ chainId: request.chainId
4495
+ });
4496
+ }
4497
+ return this.tokenDomainService.getTokenWithMetadata({
4498
+ accountAddress: request.accountAddress || '',
4499
+ contractAddress: request.contractAddress,
4500
+ abi: request.abi,
4501
+ tokenId: request.tokenId || '',
4502
+ chainId: request.chainId
4503
+ });
4504
+ }
4505
+ /**
4506
+ * Get metadata for a specific token from on-chain
4507
+ */
4508
+ async getTokenMetadata(request) {
4509
+ const domainResult = await this.tokenDomainService.getTokenMetadata({
4510
+ contractAddress: request.contractAddress,
4511
+ abi: request.abi,
4512
+ tokenId: request.tokenId || '',
4513
+ chainId: request.chainId
4514
+ });
4515
+ return domainResult.metadata;
4516
+ }
4517
+ /**
4518
+ * Retrieve entire collection of tokens with balance and metadata
4519
+ */
4520
+ async getTokenCollection(request) {
4521
+ return this.tokenDomainService.getTokenCollection({
4522
+ accountAddress: request.accountAddress || '',
4523
+ contractAddress: request.contractAddress,
4524
+ abi: request.abi,
4525
+ chainId: request.chainId,
4526
+ maxTokens: request.maxTokens,
4527
+ tokenIds: request.tokenIds
4349
4528
  });
4350
4529
  }
4351
- // ✅ FIXED: Update method signature to handle null properly
4352
- findMetadata(metadata, tokenId) {
4353
- if (!metadata || tokenId === null)
4530
+ /**
4531
+ * Resolve IPFS URLs to HTTPS if needed
4532
+ */
4533
+ async resolveIPFSUrl(url, chainId) {
4534
+ return this.metadataDomainService.resolveIPFSUrl(url, chainId);
4535
+ }
4536
+ /**
4537
+ * Fetch and process metadata from URI with IPFS conversion
4538
+ */
4539
+ async fetchAndProcessMetadata(tokenUri, chainId) {
4540
+ const domainMetadata = await this.metadataDomainService.fetchAndProcessMetadata(tokenUri, chainId);
4541
+ if (!domainMetadata)
4354
4542
  return null;
4355
- return metadata.find(m => m.tokenMetadataIncrementalId?.toString() === tokenId) || null;
4543
+ // Convert from ERC token standard to our clean interface
4544
+ const cleanMetadata = this.metadataMapper.fromERCStandard(domainMetadata);
4545
+ // Add IPFS conversion if needed
4546
+ if (cleanMetadata.animationUrl?.startsWith('ipfs://')) {
4547
+ return {
4548
+ ...cleanMetadata,
4549
+ animationUrlConverted: await this.resolveIPFSUrl(cleanMetadata.animationUrl, chainId)
4550
+ };
4551
+ }
4552
+ return cleanMetadata;
4356
4553
  }
4357
- async fetchMetadata(uri, chainId) {
4554
+ }
4555
+
4556
+ /**
4557
+ * Web3InfrastructureApi - Infrastructure implementation for blockchain operations
4558
+ * Uses @explorins/web3-ts for Web3 interactions
4559
+ */
4560
+ class Web3InfrastructureApi {
4561
+ constructor(web3ChainService) {
4562
+ this.web3ChainService = web3ChainService;
4563
+ }
4564
+ async getTokenBalance(request) {
4358
4565
  try {
4359
- const httpUrl = await this.resolveIPFSUrl(uri, chainId);
4360
- const response = await fetch(httpUrl);
4361
- if (!response.ok) {
4362
- throw new Error(`HTTP ${response.status}: ${response.statusText}`);
4363
- }
4364
- return await response.json();
4566
+ if (request.tokenId !== null)
4567
+ request.tokenId = request.tokenId.toString();
4568
+ const web3 = await this.web3ChainService.getWeb3ByChainId(request.chainId);
4569
+ const contract = web3Ts.getSmartContractInstance(request.contractAddress, request.abi, web3);
4570
+ return await web3Ts.getAccountTokenBalance(contract, request.accountAddress, request.tokenId);
4365
4571
  }
4366
4572
  catch (error) {
4367
- console.warn('Failed to fetch NFT metadata:', error);
4368
- return null;
4573
+ console.error(`Failed to get token balance for ${request.accountAddress} for ${request.contractAddress} and tokenId ${request.tokenId}, return 0 instead:`, error);
4574
+ return 0;
4369
4575
  }
4370
4576
  }
4577
+ async getTokenUri(request) {
4578
+ try {
4579
+ const web3 = await this.web3ChainService.getWeb3ByChainId(request.chainId);
4580
+ const contract = web3Ts.getSmartContractInstance(request.contractAddress, request.abi, web3);
4581
+ const tokenId = Number(request.tokenId);
4582
+ const tokenUri = await web3Ts.getTokenUri(contract, tokenId);
4583
+ return String(tokenUri);
4584
+ }
4585
+ catch (error) {
4586
+ console.error(`Failed to get token URI for tokenId ${request.tokenId}:`, error);
4587
+ throw error;
4588
+ }
4589
+ }
4590
+ async getTokenOfOwnerByIndex(request) {
4591
+ try {
4592
+ const web3 = await this.web3ChainService.getWeb3ByChainId(request.chainId);
4593
+ const tokenContract = web3Ts.getSmartContractInstance(request.contractAddress, request.abi, web3);
4594
+ const tokenId = await web3Ts.getTokenOfOwnerByIndex(tokenContract, request.accountAddress, request.tokenIndex);
4595
+ return String(tokenId);
4596
+ }
4597
+ catch (error) {
4598
+ console.error(`Failed to get token by index ${request.tokenIndex} for ${request.accountAddress}:`, error);
4599
+ throw error;
4600
+ }
4601
+ }
4602
+ }
4603
+
4604
+ /**
4605
+ * IPFSInfrastructureApi - Infrastructure implementation for IPFS operations
4606
+ * Uses Web3ChainService for IPFS gateway resolution
4607
+ */
4608
+ class IPFSInfrastructureApi {
4609
+ constructor(web3ChainService) {
4610
+ this.web3ChainService = web3ChainService;
4611
+ this.defaultIpfsGatewayDomain = 'pers.mypinata.cloud';
4612
+ }
4371
4613
  async getIpfsGatewayDomain(chainId) {
4372
4614
  try {
4373
4615
  const chainData = await this.web3ChainService.getChainDataWithCache(chainId);
@@ -4379,29 +4621,66 @@ class Web3Service {
4379
4621
  }
4380
4622
  }
4381
4623
  async resolveIPFSUrl(url, chainId) {
4382
- if (!url)
4383
- return '';
4384
4624
  if (url.startsWith('ipfs://')) {
4385
- const gatewayDomain = await this.getIpfsGatewayDomain(chainId);
4386
- return `https://${gatewayDomain}/ipfs/${url.slice(7)}`;
4625
+ const gateway = await this.getIpfsGatewayDomain(chainId);
4626
+ return url.replace('ipfs://', `https://${gateway}/ipfs/`);
4387
4627
  }
4388
4628
  return url;
4389
4629
  }
4630
+ async fetchAndProcessMetadata(tokenUri, chainId) {
4631
+ try {
4632
+ const resolvedUri = await this.resolveIPFSUrl(tokenUri, chainId);
4633
+ const response = await fetch(resolvedUri);
4634
+ if (!response.ok) {
4635
+ throw new Error(`HTTP error! status: ${response.status}`);
4636
+ }
4637
+ const metadata = await response.json();
4638
+ // Process and return clean metadata
4639
+ return {
4640
+ name: metadata.name || '',
4641
+ description: metadata.description || '',
4642
+ image: metadata.image ? await this.resolveIPFSUrl(metadata.image, chainId) : '',
4643
+ attributes: metadata.attributes || [],
4644
+ animation_url: metadata.animation_url ? await this.resolveIPFSUrl(metadata.animation_url, chainId) : undefined,
4645
+ external_url: metadata.external_url || undefined
4646
+ };
4647
+ }
4648
+ catch (error) {
4649
+ console.error('Error fetching metadata:', error);
4650
+ return null;
4651
+ }
4652
+ }
4653
+ async fetchFromUrl(url) {
4654
+ try {
4655
+ const response = await fetch(url);
4656
+ if (!response.ok) {
4657
+ throw new Error(`Failed to fetch from ${url}: ${response.statusText}`);
4658
+ }
4659
+ return await response.json();
4660
+ }
4661
+ catch (error) {
4662
+ console.error(`Error fetching from URL ${url}:`, error);
4663
+ throw error;
4664
+ }
4665
+ }
4390
4666
  }
4391
4667
 
4392
- //import { PublicHttpProviderService } from '../web3-chain/services/public-http-provider.service';
4393
4668
  function createWeb3SDK(apiClient) {
4394
4669
  // TODO: FIX LATER - TEMPORARY CONSTRUCTION
4395
4670
  const web3ProviderService = new Web3ProviderService();
4396
4671
  const web3ChainSDK = createWeb3ChainSDK(apiClient, web3ProviderService);
4397
- const web3Api = new Web3Api(web3ChainSDK.service);
4398
- const web3Service = new Web3Service(web3Api, web3ChainSDK.service);
4672
+ // Create Web3ApplicationService - main entry point for all Web3 operations
4673
+ const web3InfrastructureApi = new Web3InfrastructureApi(web3ChainSDK.service);
4674
+ const ipfsInfrastructureApi = new IPFSInfrastructureApi(web3ChainSDK.service);
4675
+ const web3ApplicationService = new Web3ApplicationService(web3InfrastructureApi, ipfsInfrastructureApi);
4676
+ // Clean SDK - all functions route through Web3ApplicationService
4399
4677
  return {
4400
- getCreditsBalance: (request) => web3Service.getERC20Balance(request),
4401
- getRewardsCollection: (request) => web3Service.getERC1155Collection(request),
4402
- getStampsCollection: (request) => web3Service.getERC721Collection(request),
4403
- api: web3Api,
4404
- service: web3Service
4678
+ getTokenBalance: (request) => web3ApplicationService.getSpecificTokenBalance(request),
4679
+ getTokenMetadata: (request) => web3ApplicationService.getTokenMetadata(request),
4680
+ getTokenCollection: (request) => web3ApplicationService.getTokenCollection(request),
4681
+ resolveIPFSUrl: (url, chainId) => web3ApplicationService.resolveIPFSUrl(url, chainId),
4682
+ fetchAndProcessMetadata: (tokenUri, chainId) => web3ApplicationService.fetchAndProcessMetadata(tokenUri, chainId),
4683
+ applicationService: web3ApplicationService
4405
4684
  };
4406
4685
  }
4407
4686
 
@@ -4418,6 +4697,7 @@ exports.ChainTypes = ChainTypes;
4418
4697
  exports.DEFAULT_PERS_CONFIG = DEFAULT_PERS_CONFIG;
4419
4698
  exports.DonationApi = DonationApi;
4420
4699
  exports.DonationService = DonationService;
4700
+ exports.IPFSInfrastructureApi = IPFSInfrastructureApi;
4421
4701
  exports.PaymentApi = PurchaseApi;
4422
4702
  exports.PaymentService = PaymentService;
4423
4703
  exports.PersApiClient = PersApiClient;
@@ -4425,7 +4705,6 @@ exports.PersApiError = PersApiError;
4425
4705
  exports.PersSDK = PersSDK;
4426
4706
  exports.RedemptionApi = RedemptionApi;
4427
4707
  exports.RedemptionService = RedemptionService;
4428
- exports.SimpleCache = SimpleCache;
4429
4708
  exports.TenantApi = TenantApi;
4430
4709
  exports.TenantService = TenantService;
4431
4710
  exports.TokenApi = TokenApi;
@@ -4437,8 +4716,10 @@ exports.UserApi = UserApi;
4437
4716
  exports.UserService = UserService;
4438
4717
  exports.UserStatusApi = UserStatusApi;
4439
4718
  exports.UserStatusService = UserStatusService;
4719
+ exports.Web3ApplicationService = Web3ApplicationService;
4440
4720
  exports.Web3ChainApi = Web3ChainApi;
4441
4721
  exports.Web3ChainService = Web3ChainService;
4722
+ exports.Web3InfrastructureApi = Web3InfrastructureApi;
4442
4723
  exports.Web3ProviderService = Web3ProviderService;
4443
4724
  exports.buildApiRoot = buildApiRoot;
4444
4725
  exports.createAnalyticsSDK = createAnalyticsSDK;