@explorins/pers-sdk 1.2.3 → 1.2.5

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 (89) hide show
  1. package/dist/auth-admin/api/auth-admin-api.d.ts +4 -2
  2. package/dist/auth-admin/api/auth-admin-api.d.ts.map +1 -1
  3. package/dist/auth-admin/index.d.ts +1 -0
  4. package/dist/auth-admin/index.d.ts.map +1 -1
  5. package/dist/auth-admin/services/auth-admin-service.d.ts +4 -0
  6. package/dist/auth-admin/services/auth-admin-service.d.ts.map +1 -1
  7. package/dist/auth-admin.cjs +21 -3
  8. package/dist/auth-admin.cjs.map +1 -1
  9. package/dist/auth-admin.js +21 -3
  10. package/dist/auth-admin.js.map +1 -1
  11. package/dist/business/api/business-api.d.ts +17 -32
  12. package/dist/business/api/business-api.d.ts.map +1 -1
  13. package/dist/business.cjs +26 -50
  14. package/dist/business.cjs.map +1 -1
  15. package/dist/business.js +26 -50
  16. package/dist/business.js.map +1 -1
  17. package/dist/campaign/api/campaign-api.d.ts +47 -30
  18. package/dist/campaign/api/campaign-api.d.ts.map +1 -1
  19. package/dist/campaign/index.d.ts +5 -5
  20. package/dist/campaign/services/campaign-service.d.ts +6 -6
  21. package/dist/campaign/services/campaign-service.d.ts.map +1 -1
  22. package/dist/campaign.cjs +62 -41
  23. package/dist/campaign.cjs.map +1 -1
  24. package/dist/campaign.js +62 -41
  25. package/dist/campaign.js.map +1 -1
  26. package/dist/index.cjs +733 -439
  27. package/dist/index.cjs.map +1 -1
  28. package/dist/index.js +732 -440
  29. package/dist/index.js.map +1 -1
  30. package/dist/package.json +1 -1
  31. package/dist/redemption/api/redemption-api.d.ts +58 -14
  32. package/dist/redemption/api/redemption-api.d.ts.map +1 -1
  33. package/dist/redemption/index.d.ts +2 -2
  34. package/dist/redemption/models/index.d.ts +1 -1
  35. package/dist/redemption/models/index.d.ts.map +1 -1
  36. package/dist/redemption/services/redemption-service.d.ts +3 -3
  37. package/dist/redemption/services/redemption-service.d.ts.map +1 -1
  38. package/dist/redemption.cjs +89 -15
  39. package/dist/redemption.cjs.map +1 -1
  40. package/dist/redemption.js +89 -15
  41. package/dist/redemption.js.map +1 -1
  42. package/dist/shared/interfaces/pers-shared-lib.interfaces.d.ts +3 -3
  43. package/dist/shared/interfaces/pers-shared-lib.interfaces.d.ts.map +1 -1
  44. package/dist/transaction/api/transaction-api.d.ts +23 -19
  45. package/dist/transaction/api/transaction-api.d.ts.map +1 -1
  46. package/dist/transaction/index.d.ts +3 -7
  47. package/dist/transaction/index.d.ts.map +1 -1
  48. package/dist/transaction/models/index.d.ts +0 -1
  49. package/dist/transaction/models/index.d.ts.map +1 -1
  50. package/dist/transaction/services/transaction-service.d.ts +5 -7
  51. package/dist/transaction/services/transaction-service.d.ts.map +1 -1
  52. package/dist/transaction.cjs +85 -50
  53. package/dist/transaction.cjs.map +1 -1
  54. package/dist/transaction.js +85 -50
  55. package/dist/transaction.js.map +1 -1
  56. package/dist/web3/application/index.d.ts +6 -0
  57. package/dist/web3/application/index.d.ts.map +1 -0
  58. package/dist/web3/application/web3-application.service.d.ts +53 -0
  59. package/dist/web3/application/web3-application.service.d.ts.map +1 -0
  60. package/dist/web3/domain/models/index.d.ts +58 -0
  61. package/dist/web3/domain/models/index.d.ts.map +1 -0
  62. package/dist/web3/domain/services/contract-domain.service.d.ts +20 -0
  63. package/dist/web3/domain/services/contract-domain.service.d.ts.map +1 -0
  64. package/dist/web3/domain/services/index.d.ts +8 -0
  65. package/dist/web3/domain/services/index.d.ts.map +1 -0
  66. package/dist/web3/domain/services/metadata-domain.service.d.ts +12 -0
  67. package/dist/web3/domain/services/metadata-domain.service.d.ts.map +1 -0
  68. package/dist/web3/domain/services/token-domain.service.d.ts +48 -0
  69. package/dist/web3/domain/services/token-domain.service.d.ts.map +1 -0
  70. package/dist/web3/index.d.ts +10 -11
  71. package/dist/web3/index.d.ts.map +1 -1
  72. package/dist/web3/infrastructure/api/index.d.ts +6 -0
  73. package/dist/web3/infrastructure/api/index.d.ts.map +1 -0
  74. package/dist/web3/infrastructure/api/ipfs-api.d.ts +15 -0
  75. package/dist/web3/infrastructure/api/ipfs-api.d.ts.map +1 -0
  76. package/dist/web3/{api → infrastructure/api}/web3-api.d.ts +6 -2
  77. package/dist/web3/infrastructure/api/web3-api.d.ts.map +1 -0
  78. package/dist/web3/infrastructure/index.d.ts +2 -0
  79. package/dist/web3/infrastructure/index.d.ts.map +1 -0
  80. package/dist/web3.cjs +509 -336
  81. package/dist/web3.cjs.map +1 -1
  82. package/dist/web3.js +507 -336
  83. package/dist/web3.js.map +1 -1
  84. package/package.json +1 -1
  85. package/dist/web3/api/web3-api.d.ts.map +0 -1
  86. package/dist/web3/models/index.d.ts +0 -92
  87. package/dist/web3/models/index.d.ts.map +0 -1
  88. package/dist/web3/services/web3-service.d.ts +0 -21
  89. 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,9 +1287,18 @@ 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
- const requestBody = { authToken: jwt };
1282
- return this.apiClient.post(`${this.basePath}/token`, requestBody);
1290
+ const body = {
1291
+ authToken: jwt,
1292
+ authType: persShared.AccountOwnerType.TENANT
1293
+ };
1294
+ return this.apiClient.post(`${this.basePath}/token`, body);
1295
+ }
1296
+ async loginUser(jwt) {
1297
+ const body = {
1298
+ authToken: jwt,
1299
+ authType: persShared.AccountOwnerType.USER
1300
+ };
1301
+ return this.apiClient.post(`${this.basePath}/token`, body);
1283
1302
  }
1284
1303
  /**
1285
1304
  * ADMIN: Refresh access token
@@ -1311,6 +1330,12 @@ class AuthAdminService {
1311
1330
  async loginTenantAdmin(jwt) {
1312
1331
  return this.authAdminApi.loginTenantAdmin(jwt);
1313
1332
  }
1333
+ /**
1334
+ * ADMIN: Login user with JWT
1335
+ */
1336
+ async loginUser(jwt) {
1337
+ return this.authAdminApi.loginUser(jwt);
1338
+ }
1314
1339
  /**
1315
1340
  * ADMIN: Refresh access token
1316
1341
  */
@@ -1339,6 +1364,7 @@ function createAuthAdminSDK(apiClient) {
1339
1364
  // Direct access to service methods (primary interface)
1340
1365
  // Admin authentication methods
1341
1366
  loginTenantAdmin: (jwt) => authAdminService.loginTenantAdmin(jwt),
1367
+ loginUser: (jwt) => authAdminService.loginUser(jwt),
1342
1368
  refreshAccessToken: (refreshToken) => authAdminService.refreshAccessToken(refreshToken),
1343
1369
  // Advanced access for edge cases
1344
1370
  api: authAdminApi,
@@ -1588,81 +1614,102 @@ class CampaignApi {
1588
1614
  * NEW: POST /campaign-claims/user
1589
1615
  */
1590
1616
  async claimCampaign(request) {
1591
- return this.apiClient.post('/campaign-claims/user', request);
1617
+ return this.apiClient.post('/campaign-claims', request);
1592
1618
  }
1593
1619
  /**
1594
1620
  * USER: Get claims for logged user
1595
1621
  * NEW: GET /campaign-claims/users/me
1596
1622
  */
1597
1623
  async getClaimsForLoggedUser() {
1598
- return this.apiClient.get('/campaign-claims/users/me');
1624
+ return this.apiClient.get('/campaign-claims/me');
1599
1625
  }
1600
1626
  /**
1601
- * BUSINESS: Claim campaign reward for customer
1602
- * NEW: POST /campaign-claims/business
1603
- */
1604
- async businessClaimCampaign(request) {
1605
- return this.apiClient.post('/campaign-claims/business', request);
1627
+ * ADMIN: Get all campaign claims
1628
+ * Updated to use unified endpoint
1629
+ */
1630
+ async getCampaignClaims() {
1631
+ // Admin context - no parameters needed for all claims
1632
+ return this.apiClient.get('/campaign-claims');
1606
1633
  }
1607
1634
  /**
1608
- * SYSTEM: Process automated claim
1609
- * NEW: POST /campaign-claims/system
1635
+ * ADMIN: Get campaign claims by campaign ID
1636
+ * Updated to use query parameters
1610
1637
  */
1611
- async systemClaimCampaign(request) {
1612
- return this.apiClient.post('/campaign-claims/system', request);
1638
+ async getCampaignClaimsByCampaignId(campaignId) {
1639
+ return this.apiClient.get(`/campaign-claims?campaignId=${campaignId}`);
1613
1640
  }
1614
1641
  /**
1615
- * ADMIN: Manual claim processing
1616
- * NEW: POST /campaign-claims/admin
1642
+ * ADMIN: Get campaign claims by user ID
1643
+ * Updated to use query parameters
1617
1644
  */
1618
- async adminClaimCampaign(request) {
1619
- return this.apiClient.post('/campaign-claims/admin', request);
1645
+ async getCampaignClaimsByUserId(userId) {
1646
+ return this.apiClient.get(`/campaign-claims?userId=${userId}`);
1620
1647
  }
1621
1648
  /**
1622
- * ADMIN: Get all campaign claims
1623
- * NEW: GET /campaign-claims/admin
1649
+ * ADMIN: Get campaign claims by business ID
1650
+ * Updated to use query parameters
1624
1651
  */
1625
- async getCampaignClaims() {
1626
- return this.apiClient.get('/campaign-claims/admin');
1652
+ async getCampaignClaimsByBusinessId(businessId) {
1653
+ return this.apiClient.get(`/campaign-claims?businessId=${businessId}`);
1627
1654
  }
1628
1655
  /**
1629
- * ADMIN: Get campaign claims by campaign ID
1630
- * NEW: GET /campaign-claims/admin/{campaignId}
1656
+ * ADMIN: Get campaign claims by user ID for specific campaign
1657
+ * Combined filtering using query parameters
1631
1658
  */
1632
- async getCampaignClaimsByCampaignId(campaignId) {
1633
- return this.apiClient.get(`/campaign-claims/admin/${campaignId}`);
1659
+ async getCampaignClaimsByUserAndCampaign(userId, campaignId) {
1660
+ return this.apiClient.get(`/campaign-claims?userId=${userId}&campaignId=${campaignId}`);
1634
1661
  }
1635
1662
  /**
1636
- * ADMIN: Get campaign claims by user ID
1637
- * NEW: GET /campaign-claims/admin/users/{userId}
1663
+ * ADMIN: Get campaign claims by business ID for specific campaign
1664
+ * Combined filtering using query parameters
1638
1665
  */
1639
- async getCampaignClaimsByUserId(userId) {
1640
- return this.apiClient.get(`/campaign-claims/admin/users/${userId}`);
1666
+ async getCampaignClaimsByBusinessAndCampaign(businessId, campaignId) {
1667
+ return this.apiClient.get(`/campaign-claims?businessId=${businessId}&campaignId=${campaignId}`);
1641
1668
  }
1642
1669
  /**
1643
- * ADMIN: Get campaign claims by business ID
1644
- * NEW: GET /campaign-claims/admin/businesses/{businessId}
1670
+ * USER: Get user's own claims (all campaigns)
1671
+ * Use convenience endpoint
1645
1672
  */
1646
- async getCampaignClaimsByBusinessId(businessId) {
1647
- return this.apiClient.get(`/campaign-claims/admin/businesses/${businessId}`);
1673
+ async getUserClaims() {
1674
+ return this.apiClient.get('/campaign-claims/me');
1648
1675
  }
1649
1676
  /**
1650
1677
  * USER: Get user's claims for specific campaign
1651
- * NEW: GET /campaign-claims/campaigns/{campaignId}/users/me
1678
+ * Use convenience endpoint with query parameter
1652
1679
  */
1653
1680
  async getUserClaimsForCampaign(campaignId) {
1654
- return this.apiClient.get(`/campaign-claims/campaigns/${campaignId}/users/me`);
1681
+ return this.apiClient.get(`/campaign-claims/me?campaignId=${campaignId}`);
1682
+ }
1683
+ /**
1684
+ * BUSINESS: Get business claims (all campaigns)
1685
+ * Uses unified endpoint with business context
1686
+ */
1687
+ async getBusinessClaims() {
1688
+ return this.apiClient.get('/campaign-claims');
1655
1689
  }
1656
- // ==========================================
1657
- // BACKWARD COMPATIBILITY (DEPRECATED)
1658
- // ==========================================
1659
1690
  /**
1660
- * @deprecated Use getCampaigns() instead
1661
- * LEGACY: Get campaigns with active filter
1691
+ * BUSINESS: Get business claims for specific campaign
1692
+ * Uses unified endpoint with business context and campaign filter
1662
1693
  */
1663
- async getCampaignsLegacy(active) {
1664
- console.warn('CampaignApi.getCampaignsLegacy() is deprecated. Use getCampaigns() instead.');
1665
- return this.getCampaigns(active !== undefined ? { active } : undefined);
1694
+ async getBusinessClaimsForCampaign(campaignId) {
1695
+ return this.apiClient.get(`/campaign-claims?campaignId=${campaignId}`);
1696
+ }
1697
+ /**
1698
+ * Helper: Build query string from parameters
1699
+ */
1700
+ buildQueryString(params) {
1701
+ const validParams = Object.entries(params)
1702
+ .filter(([_, value]) => value !== undefined)
1703
+ .map(([key, value]) => `${key}=${encodeURIComponent(value)}`)
1704
+ .join('&');
1705
+ return validParams ? `?${validParams}` : '';
1706
+ }
1707
+ /**
1708
+ * Flexible admin claims query with multiple filters
1709
+ */
1710
+ async getAdminClaims(filters) {
1711
+ const queryString = this.buildQueryString(filters || {});
1712
+ return this.apiClient.get(`/campaign-claims${queryString}`);
1666
1713
  }
1667
1714
  }
1668
1715
 
@@ -2173,20 +2220,21 @@ function createPaymentSDK(apiClient) {
2173
2220
  /**
2174
2221
  * Platform-Agnostic Redemption API Client (UPDATED - RESTful Design)
2175
2222
  *
2176
- * Updated to work with the new RESTful /redemptions endpoints.
2223
+ * Updated to work with the new RESTful /redemptions and /redemption-redeems endpoints.
2177
2224
  * Handles redemption operations using the PERS backend with intelligent access detection.
2178
2225
  * Uses @explorins/pers-shared DTOs for consistency with backend.
2179
2226
  *
2180
- * Migration Update: Updated all endpoints from /redemption to /redemptions
2227
+ * Migration Update: Updated all endpoints for unified controller pattern
2181
2228
  * - Removed role revelation from URLs (no more /admin, /auth paths)
2182
2229
  * - Added intelligent access detection for unified endpoints
2183
- * - Updated toggle endpoint to follow /status pattern
2184
- * - Enhanced redemption process with path-based IDs
2230
+ * - Added new /redemption-redeems endpoints for redeem processing
2231
+ * - Enhanced redemption process with role-based access control
2185
2232
  */
2186
2233
  class RedemptionApi {
2187
2234
  constructor(apiClient) {
2188
2235
  this.apiClient = apiClient;
2189
2236
  this.basePath = '/redemptions';
2237
+ this.redeemsPath = '/redemption-redeems';
2190
2238
  }
2191
2239
  // ==========================================
2192
2240
  // PUBLIC OPERATIONS (Project Key)
@@ -2221,7 +2269,7 @@ class RedemptionApi {
2221
2269
  * Updated: /redemption/type → /redemptions/types
2222
2270
  */
2223
2271
  async getRedemptionTypes() {
2224
- return this.apiClient.get(`${this.basePath}/types`);
2272
+ return this.apiClient.get(`/redemption-types`);
2225
2273
  }
2226
2274
  /**
2227
2275
  * PUBLIC: Get redemption by ID
@@ -2234,33 +2282,106 @@ class RedemptionApi {
2234
2282
  /**
2235
2283
  * PUBLIC: Get available supply for redemption
2236
2284
  *
2237
- * Updated: /redemption/:id/available-supply → /redemptions/:id/available-supply
2285
+ * Updated: /redemption/:id/available-supply → /redemptions/:id/supply
2238
2286
  */
2239
2287
  async getRedemptionAvailableSupply(id) {
2240
- return this.apiClient.get(`${this.basePath}/${id}/available-supply`);
2288
+ return this.apiClient.get(`${this.basePath}/${id}/supply`);
2241
2289
  }
2242
2290
  // ==========================================
2243
- // USER OPERATIONS (JWT + Project Key)
2291
+ // REDEMPTION EXECUTION (NEW UNIFIED ENDPOINT)
2244
2292
  // ==========================================
2245
2293
  /**
2246
- * USER: Redeem a redemption
2294
+ * Execute redemption (unified endpoint)
2247
2295
  *
2248
- * Updated: /redemption/auth/redeem /redemptions/:id/redeem
2249
- * Enhanced: Path-based redemption ID for better RESTful design
2296
+ * NEW: POST /redemption-redeems - Role-based processing
2297
+ * - USER: Direct user redemption processing
2298
+ * - ADMIN: Can process redemptions for any account type
2299
+ * - BUSINESS: Process redemptions for customers
2250
2300
  */
2251
2301
  async redeemRedemption(redemptionId) {
2252
2302
  const body = {
2253
2303
  redemptionId: redemptionId,
2254
2304
  };
2255
- return this.apiClient.post(`${this.basePath}/${redemptionId}/redeem`, body);
2305
+ return this.apiClient.post(this.redeemsPath, body);
2306
+ }
2307
+ // ==========================================
2308
+ // REDEMPTION REDEEMS QUERIES (NEW ENDPOINTS)
2309
+ // ==========================================
2310
+ /**
2311
+ * Get redemption redeems with filtering (unified endpoint)
2312
+ *
2313
+ * NEW: GET /redemption-redeems with query parameters
2314
+ * Role-based access: Users see only their own, admins can filter by userId/businessId
2315
+ */
2316
+ async getRedemptionRedeems(filters) {
2317
+ let url = this.redeemsPath;
2318
+ const params = new URLSearchParams();
2319
+ if (filters?.redemptionId)
2320
+ params.append('redemptionId', filters.redemptionId);
2321
+ if (filters?.userId)
2322
+ params.append('userId', filters.userId);
2323
+ if (filters?.businessId)
2324
+ params.append('businessId', filters.businessId);
2325
+ const queryString = params.toString();
2326
+ if (queryString) {
2327
+ url += `?${queryString}`;
2328
+ }
2329
+ return this.apiClient.get(url);
2330
+ }
2331
+ /**
2332
+ * Get specific redemption redeem by ID
2333
+ *
2334
+ * NEW: GET /redemption-redeems/:id
2335
+ */
2336
+ async getRedemptionRedeemById(id) {
2337
+ return this.apiClient.get(`${this.redeemsPath}/${id}`);
2338
+ }
2339
+ /**
2340
+ * USER: Get my redemption redeems (convenience endpoint)
2341
+ *
2342
+ * NEW: GET /redemption-redeems/me with optional filtering
2343
+ */
2344
+ async getMyRedemptionRedeems(redemptionId) {
2345
+ let url = `${this.redeemsPath}/me`;
2346
+ if (redemptionId) {
2347
+ url += `?redemptionId=${redemptionId}`;
2348
+ }
2349
+ return this.apiClient.get(url);
2256
2350
  }
2351
+ /**
2352
+ * ADMIN: Get redemption redeems by user ID
2353
+ *
2354
+ * NEW: GET /redemption-redeems?userId=X
2355
+ */
2356
+ async getRedemptionRedeemsByUserId(userId, redemptionId) {
2357
+ return this.getRedemptionRedeems({ userId, redemptionId });
2358
+ }
2359
+ /**
2360
+ * ADMIN: Get redemption redeems by business ID
2361
+ *
2362
+ * NEW: GET /redemption-redeems?businessId=X
2363
+ */
2364
+ async getRedemptionRedeemsByBusinessId(businessId, redemptionId) {
2365
+ return this.getRedemptionRedeems({ businessId, redemptionId });
2366
+ }
2367
+ /**
2368
+ * ADMIN: Get redemption redeems by redemption ID
2369
+ *
2370
+ * NEW: GET /redemption-redeems?redemptionId=X
2371
+ */
2372
+ async getRedemptionRedeemsByRedemptionId(redemptionId) {
2373
+ return this.getRedemptionRedeems({ redemptionId });
2374
+ }
2375
+ // ==========================================
2376
+ // USER OPERATIONS (JWT + Project Key)
2377
+ // ==========================================
2257
2378
  /**
2258
2379
  * USER: Get user redemption history
2259
2380
  *
2260
- * Updated: /redemption/auth/redeem /redemptions/me/history
2381
+ * Updated: Uses new convenience endpoint /redemption-redeems/me
2261
2382
  */
2262
2383
  async getUserRedemptionHistory() {
2263
- return this.apiClient.get(`${this.basePath}/me/history`);
2384
+ return this.getMyRedemptionRedeems();
2264
2385
  }
2265
2386
  /**
2266
2387
  * USER: Get user redemptions (backward compatibility)
@@ -2329,7 +2450,7 @@ class RedemptionApi {
2329
2450
  * Updated: /redemption/admin/type → /redemptions/types
2330
2451
  */
2331
2452
  async createRedemptionType(redemptionType) {
2332
- return this.apiClient.post(`${this.basePath}/types`, redemptionType);
2453
+ return this.apiClient.post(`${this.basePath}/redemption-types`, redemptionType);
2333
2454
  }
2334
2455
  // ==========================================
2335
2456
  // TOKEN UNIT MANAGEMENT (Admin)
@@ -4064,311 +4185,445 @@ function createWeb3ChainSDK(apiClient, providerService) {
4064
4185
  };
4065
4186
  }
4066
4187
 
4067
- class Web3Api {
4068
- constructor(web3ChainService) {
4069
- this.web3ChainService = web3ChainService;
4188
+ /**
4189
+ * TokenDomainService - Domain service for token operations
4190
+ * Implements business logic for token balance, metadata, and collection operations
4191
+ */
4192
+ class TokenDomainService {
4193
+ constructor(web3Api, metadataService, contractService) {
4194
+ this.web3Api = web3Api;
4195
+ this.metadataService = metadataService;
4196
+ this.contractService = contractService;
4070
4197
  }
4071
4198
  async getTokenBalance(request) {
4072
- const web3 = await this.web3ChainService.getWeb3ByChainId(request.chainId);
4073
- const tokenContract = web3Ts.getSmartContractInstance(request.contractAddress, request.abi, web3);
4074
- const balance = await web3Ts.getAddressTokenBalanceByContract(tokenContract, request.accountAddress, request.tokenId);
4075
- return Number(balance);
4076
- }
4077
- async getTokenUri(request) {
4078
- const web3 = await this.web3ChainService.getWeb3ByChainId(request.chainId);
4079
- // ✅ DIRECT: Use web3-ts functions directly
4080
- const tokenContract = web3Ts.getSmartContractInstance(request.contractAddress, request.abi, web3);
4081
- const tokenId = Number(request.tokenId);
4082
- const tokenUri = await web3Ts.getTokenUri(tokenContract, tokenId);
4083
- return String(tokenUri);
4084
- }
4085
- async getTokenOfOwnerByIndex(request) {
4086
- const web3 = await this.web3ChainService.getWeb3ByChainId(request.chainId);
4087
- // ✅ DIRECT: Use web3-ts functions directly
4088
- const tokenContract = web3Ts.getSmartContractInstance(request.contractAddress, request.abi, web3);
4089
- const tokenId = await web3Ts.getTokenOfOwnerByIndex(tokenContract, request.accountAddress, request.tokenIndex);
4090
- return String(tokenId);
4091
- }
4092
- }
4093
-
4094
- class SimpleCache {
4095
- constructor() {
4096
- this.storage = {};
4097
- this.defaultTTL = 10 * 1000; // 10 seconds
4098
- }
4099
- set(key, data, ttl) {
4100
- this.storage[key] = {
4101
- data,
4102
- timestamp: Date.now(),
4103
- ttl: ttl ?? this.defaultTTL
4104
- };
4105
- }
4106
- get(key) {
4107
- const entry = this.storage[key];
4108
- if (!entry) {
4109
- return null;
4110
- }
4111
- const now = Date.now();
4112
- const isExpired = (now - entry.timestamp) > entry.ttl;
4113
- if (isExpired) {
4114
- delete this.storage[key];
4115
- return null;
4116
- }
4117
- return entry.data;
4118
- }
4119
- clear() {
4120
- this.storage = {};
4121
- }
4122
- cleanup() {
4123
- const now = Date.now();
4124
- Object.keys(this.storage).forEach(key => {
4125
- const entry = this.storage[key];
4126
- if ((now - entry.timestamp) > entry.ttl) {
4127
- delete this.storage[key];
4128
- }
4129
- });
4130
- }
4131
- }
4132
-
4133
- class Web3Service {
4134
- constructor(web3Api, web3ChainService) {
4135
- this.web3Api = web3Api;
4136
- this.web3ChainService = web3ChainService;
4137
- //temporary fix, remove when the backend supports custom gateways
4138
- this.defaultIpfsGatewayDomain = 'pers.mypinata.cloud';
4139
- // ✅ CACHE: Simple 10-second cache instance
4140
- this.cache = new SimpleCache();
4141
- this.cleanupInterval = null;
4142
- this.cleanupInterval = setInterval(() => {
4143
- this.cache.cleanup();
4144
- }, 30 * 1000);
4145
- }
4146
- destroy() {
4147
- if (this.cleanupInterval) {
4148
- clearInterval(this.cleanupInterval);
4149
- this.cleanupInterval = null;
4150
- }
4151
- this.cache.clear();
4152
- }
4153
- async getERC20Balance(request) {
4154
- const cacheKey = `erc20_balance_${request.accountAddress}_${request.token.contractAddress}_${request.token.chainId}`;
4155
- // ✅ CACHE CHECK: Try to get from cache first
4156
- const cached = this.cache.get(cacheKey);
4157
- if (cached) {
4158
- console.debug(`💾 [Web3Service] Using cached ERC20 balance for ${request.token.symbol}`);
4159
- return cached;
4160
- }
4161
- console.debug(`🔄 [Web3Service] Fetching fresh ERC20 balance for ${request.token.symbol}`);
4162
- const rawBalance = await this.web3Api.getTokenBalance({
4199
+ const balance = await this.web3Api.getTokenBalance({
4163
4200
  accountAddress: request.accountAddress,
4164
- contractAddress: request.token.contractAddress,
4165
- abi: request.token.abi,
4166
- tokenId: null, // Always null for ERC20
4167
- chainId: request.token.chainId
4201
+ contractAddress: request.contractAddress,
4202
+ abi: request.abi,
4203
+ tokenId: request.tokenId,
4204
+ chainId: request.chainId
4168
4205
  });
4169
- const decimals = request.token.decimals ?? 18;
4170
- const symbol = request.token.symbol ?? 'UNKNOWN';
4171
- const response = {
4172
- rawBalance,
4173
- formattedBalance: this.formatBalance(rawBalance, decimals),
4174
- decimals,
4175
- symbol,
4176
- hasBalance: rawBalance > 0
4206
+ return {
4207
+ tokenId: request.tokenId,
4208
+ balance,
4209
+ hasBalance: balance > 0,
4210
+ metadata: null
4177
4211
  };
4178
- // ✅ CACHE SET: Store result in cache
4179
- this.cache.set(cacheKey, response);
4180
- return response;
4181
- }
4182
- async getERC1155Collection(request) {
4183
- // ✅ CACHE KEY: Create unique cache key for collection request
4184
- const contractAddresses = request.tokens.map(t => t.contractAddress).sort().join(',');
4185
- const cacheKey = `erc1155_collection_${request.accountAddress}_${contractAddresses}`;
4186
- // ✅ CACHE CHECK: Try to get from cache first
4187
- const cached = this.cache.get(cacheKey);
4188
- if (cached) {
4189
- console.debug(`💾 [Web3Service] Using cached ERC1155 collection for ${request.accountAddress}`);
4190
- return cached;
4191
- }
4192
- console.debug(`🔄 [Web3Service] Fetching fresh ERC1155 collection for ${request.accountAddress}`);
4193
- const tokenResults = await Promise.all(request.tokens.map(async (token) => {
4194
- // ✅ FIXED: Handle null metadata properly
4195
- const tokenIds = token.metadata?.map(m => m.tokenMetadataIncrementalId?.toString()).filter((id) => id !== undefined) ?? [];
4196
- // Check balance for each known tokenId
4197
- const balanceResults = await Promise.allSettled(tokenIds.map(async (tokenId) => {
4198
- try {
4199
- const rawBalance = await this.web3Api.getTokenBalance({
4200
- accountAddress: request.accountAddress,
4201
- contractAddress: token.contractAddress,
4202
- abi: token.abi,
4203
- tokenId,
4204
- chainId: token.chainId
4205
- });
4206
- const decimals = token.decimals ?? 0; // ERC1155 usually no decimals
4207
- return {
4208
- tokenId,
4209
- balance: rawBalance,
4210
- formattedBalance: this.formatBalance(rawBalance, decimals),
4211
- hasBalance: rawBalance > 0,
4212
- // ✅ FIXED: Convert null to undefined for findMetadata
4213
- metadata: this.findMetadata(token.metadata ?? undefined, tokenId)
4214
- };
4215
- }
4216
- catch (error) {
4217
- console.warn(`Failed to get balance for token ${token.contractAddress}:${tokenId}`, error);
4218
- return null; // Skip failed tokens
4219
- }
4220
- }));
4221
- // Filter successful results with balance > 0
4222
- const successfulResults = [];
4223
- for (const result of balanceResults) {
4224
- if (result.status === 'fulfilled' && result.value !== null && result.value.hasBalance) {
4225
- successfulResults.push(result.value);
4212
+ }
4213
+ async getTokenWithMetadata(params) {
4214
+ try {
4215
+ const balance = await this.web3Api.getTokenBalance({
4216
+ accountAddress: params.accountAddress,
4217
+ contractAddress: params.contractAddress,
4218
+ abi: params.abi,
4219
+ tokenId: params.tokenId,
4220
+ chainId: params.chainId
4221
+ });
4222
+ let metadata = null;
4223
+ if (balance > 0) {
4224
+ const tokenUri = await this.web3Api.getTokenUri({
4225
+ contractAddress: params.contractAddress,
4226
+ abi: params.abi,
4227
+ tokenId: params.tokenId,
4228
+ chainId: params.chainId
4229
+ });
4230
+ if (tokenUri) {
4231
+ metadata = await this.metadataService.fetchAndProcessMetadata(tokenUri, params.chainId);
4226
4232
  }
4227
4233
  }
4228
4234
  return {
4229
- token,
4230
- results: successfulResults
4235
+ tokenId: params.tokenId,
4236
+ balance,
4237
+ hasBalance: balance > 0,
4238
+ metadata
4231
4239
  };
4232
- }));
4233
- const response = {
4234
- accountAddress: request.accountAddress,
4235
- tokens: tokenResults.filter(t => t.results.length > 0)
4236
- };
4237
- // ✅ CACHE SET: Store complete collection result
4238
- this.cache.set(cacheKey, response);
4239
- return response;
4240
- }
4241
- async getERC721Collection(request) {
4242
- // ✅ CACHE KEY: Create unique cache key for NFT collection
4243
- const contractAddresses = request.nftContracts.map(t => t.contractAddress).sort().join(',');
4244
- const maxNFTs = request.maxNFTsPerContract || 50;
4245
- const cacheKey = `erc721_collection_${request.accountAddress}_${contractAddresses}_${maxNFTs}`;
4246
- // ✅ CACHE CHECK: Try to get from cache first
4247
- const cached = this.cache.get(cacheKey);
4248
- if (cached) {
4249
- console.debug(`💾 [Web3Service] Using cached ERC721 collection for ${request.accountAddress}`);
4250
- return cached;
4251
4240
  }
4252
- console.debug(`🔄 [Web3Service] Fetching fresh ERC721 collection for ${request.accountAddress}`);
4253
- const startTime = Date.now();
4254
- const contractResults = await Promise.all(request.nftContracts.map(async (token) => {
4255
- try {
4256
- const totalBalance = await this.web3Api.getTokenBalance({
4257
- accountAddress: request.accountAddress,
4258
- contractAddress: token.contractAddress,
4259
- abi: token.abi,
4260
- tokenId: null,
4261
- chainId: token.chainId
4241
+ catch (error) {
4242
+ console.error('Error getting token with metadata:', error);
4243
+ return {
4244
+ tokenId: params.tokenId,
4245
+ balance: 0,
4246
+ hasBalance: false,
4247
+ metadata: null
4248
+ };
4249
+ }
4250
+ }
4251
+ async getTokenCollection(params) {
4252
+ try {
4253
+ const contractAnalysis = this.contractService.analyzeContract(params.abi);
4254
+ const tokens = [];
4255
+ if (!contractAnalysis.hasEnumeration && !contractAnalysis.isERC1155) {
4256
+ console.warn('Contract does not support enumeration, cannot retrieve full collection');
4257
+ return {
4258
+ accountAddress: params.accountAddress,
4259
+ contractAddress: params.contractAddress,
4260
+ totalBalance: 0,
4261
+ tokensRetrieved: 0,
4262
+ tokens: [],
4263
+ note: 'Contract does not support enumeration'
4264
+ };
4265
+ }
4266
+ else if (contractAnalysis.isERC1155) {
4267
+ const tokenIdsToProcess = params.tokenIds || [];
4268
+ if (tokenIdsToProcess.length > 0) {
4269
+ for (const tokenId of tokenIdsToProcess) {
4270
+ const tokenBalance = await this.getTokenWithMetadata({
4271
+ accountAddress: params.accountAddress,
4272
+ contractAddress: params.contractAddress,
4273
+ abi: params.abi,
4274
+ tokenId,
4275
+ chainId: params.chainId
4276
+ });
4277
+ tokens.push(tokenBalance);
4278
+ }
4279
+ }
4280
+ console.log('ERC-1155 User balances:', tokens);
4281
+ // ERC-1155: Cannot enumerate without knowing token IDs
4282
+ // Would need to use events or provide specific token IDs
4283
+ console.warn('ERC-1155 collection retrieval requires specific token IDs or event analysis');
4284
+ return {
4285
+ accountAddress: params.accountAddress,
4286
+ contractAddress: params.contractAddress,
4287
+ totalBalance: 0,
4288
+ tokensRetrieved: 0,
4289
+ tokens: tokens,
4290
+ note: 'ERC-1155 collection retrieval requires specific token IDs. Use getTokenWithMetadata() for individual tokens.'
4291
+ };
4292
+ }
4293
+ // Handle different token standards
4294
+ if (contractAnalysis.isERC721) {
4295
+ // ERC-721: Get user's total balance and enumerate through tokens
4296
+ const userBalance = await this.web3Api.getTokenBalance({
4297
+ accountAddress: params.accountAddress,
4298
+ contractAddress: params.contractAddress,
4299
+ abi: params.abi,
4300
+ tokenId: null, // null for ERC-721 total balance
4301
+ chainId: params.chainId
4262
4302
  });
4263
- if (totalBalance === 0) {
4303
+ console.log(`ERC-721 User balance for ${params.accountAddress}:`, userBalance);
4304
+ if (userBalance === 0) {
4264
4305
  return {
4265
- token,
4266
- totalNFTs: 0,
4267
- nfts: [],
4268
- hasMore: false
4306
+ accountAddress: params.accountAddress,
4307
+ contractAddress: params.contractAddress,
4308
+ totalBalance: 0,
4309
+ tokensRetrieved: 0,
4310
+ tokens: []
4269
4311
  };
4270
4312
  }
4271
- const nftsToLoad = Math.min(totalBalance, maxNFTs);
4272
- const nftResults = await Promise.allSettled(Array.from({ length: nftsToLoad }, async (_, index) => {
4313
+ // Enumerate through user's tokens
4314
+ const maxTokens = params.maxTokens || userBalance;
4315
+ const tokensToRetrieve = Math.min(maxTokens, userBalance);
4316
+ for (let i = 0; i < tokensToRetrieve; i++) {
4273
4317
  try {
4274
4318
  const tokenId = await this.web3Api.getTokenOfOwnerByIndex({
4275
- contractAddress: token.contractAddress,
4276
- abi: token.abi,
4277
- accountAddress: request.accountAddress,
4278
- tokenIndex: index,
4279
- chainId: token.chainId
4319
+ contractAddress: params.contractAddress,
4320
+ abi: params.abi,
4321
+ accountAddress: params.accountAddress,
4322
+ tokenIndex: i,
4323
+ chainId: params.chainId
4280
4324
  });
4281
- const tokenUri = await this.web3Api.getTokenUri({
4282
- contractAddress: token.contractAddress,
4283
- abi: token.abi,
4325
+ const tokenWithMetadata = await this.getTokenWithMetadata({
4326
+ accountAddress: params.accountAddress,
4327
+ contractAddress: params.contractAddress,
4328
+ abi: params.abi,
4284
4329
  tokenId,
4285
- chainId: token.chainId
4330
+ chainId: params.chainId
4286
4331
  });
4287
- const metadata = await this.fetchMetadata(tokenUri, token.chainId);
4288
- const nftItem = {
4289
- tokenId,
4290
- name: metadata?.name || `Token #${tokenId}`,
4291
- description: metadata?.description || '',
4292
- imageUrl: await this.resolveIPFSUrl(metadata?.image || '', token.chainId),
4293
- rawBalance: 1,
4294
- formattedBalance: '1',
4295
- hasBalance: true,
4296
- metadata,
4297
- tokenIndex: index
4298
- };
4299
- return nftItem;
4332
+ if (tokenWithMetadata.hasBalance) {
4333
+ tokens.push(tokenWithMetadata);
4334
+ }
4300
4335
  }
4301
4336
  catch (error) {
4302
- console.warn(`Failed to load NFT at index ${index} for ${token.symbol}:`, error);
4303
- return null;
4337
+ console.warn(`Error retrieving ERC-721 token at index ${i}:`, error);
4338
+ continue;
4304
4339
  }
4305
- }));
4306
- // ✅ FIXED: Usar tipo específico NFTItem
4307
- const successfulNFTs = nftResults
4308
- .filter((result) => result.status === 'fulfilled' && result.value !== null)
4309
- .map(result => result.value);
4310
- return {
4311
- token,
4312
- totalNFTs: totalBalance,
4313
- nfts: successfulNFTs,
4314
- hasMore: totalBalance > maxNFTs
4315
- };
4340
+ }
4316
4341
  }
4317
- catch (error) {
4318
- console.error(`Failed to load NFT collection for ${token.symbol}:`, error);
4342
+ else {
4343
+ // Unknown standard
4319
4344
  return {
4320
- token,
4321
- totalNFTs: 0,
4322
- nfts: [],
4323
- hasMore: false
4345
+ accountAddress: params.accountAddress,
4346
+ contractAddress: params.contractAddress,
4347
+ totalBalance: 0,
4348
+ tokensRetrieved: 0,
4349
+ tokens: [],
4350
+ note: 'Unsupported token standard for collection retrieval'
4324
4351
  };
4325
4352
  }
4326
- }));
4327
- const totalNFTs = contractResults.reduce((sum, contract) => sum + contract.nfts.length, 0);
4328
- const loadingTime = Date.now() - startTime;
4329
- const response = {
4330
- accountAddress: request.accountAddress,
4331
- contracts: contractResults,
4332
- summary: {
4333
- totalContracts: request.nftContracts.length,
4334
- totalNFTs,
4335
- loadingTime
4353
+ // Calculate total balance based on retrieved tokens
4354
+ let totalBalance = 0;
4355
+ if (contractAnalysis.isERC721) {
4356
+ // For ERC-721, total balance is the number of unique tokens owned
4357
+ totalBalance = tokens.length;
4358
+ }
4359
+ else {
4360
+ // For other standards, sum up individual token balances
4361
+ totalBalance = tokens.reduce((sum, token) => sum + token.balance, 0);
4362
+ }
4363
+ return {
4364
+ accountAddress: params.accountAddress,
4365
+ contractAddress: params.contractAddress,
4366
+ totalBalance,
4367
+ tokensRetrieved: tokens.length,
4368
+ tokens
4369
+ };
4370
+ }
4371
+ catch (error) {
4372
+ console.error('Error getting token collection:', error);
4373
+ return {
4374
+ accountAddress: params.accountAddress,
4375
+ contractAddress: params.contractAddress,
4376
+ totalBalance: 0,
4377
+ tokensRetrieved: 0,
4378
+ tokens: [],
4379
+ note: 'Error retrieving collection'
4380
+ };
4381
+ }
4382
+ }
4383
+ async getTokenMetadata(params) {
4384
+ try {
4385
+ const tokenUri = await this.web3Api.getTokenUri({
4386
+ contractAddress: params.contractAddress,
4387
+ abi: params.abi,
4388
+ tokenId: params.tokenId,
4389
+ chainId: params.chainId
4390
+ });
4391
+ let metadata = null;
4392
+ if (tokenUri) {
4393
+ metadata = await this.metadataService.fetchAndProcessMetadata(tokenUri, params.chainId);
4336
4394
  }
4395
+ return {
4396
+ tokenId: params.tokenId,
4397
+ tokenUri,
4398
+ metadata
4399
+ };
4400
+ }
4401
+ catch (error) {
4402
+ console.error('Error getting token metadata:', error);
4403
+ return {
4404
+ tokenId: params.tokenId,
4405
+ tokenUri: null,
4406
+ metadata: null
4407
+ };
4408
+ }
4409
+ }
4410
+ }
4411
+
4412
+ /**
4413
+ * MetadataDomainService - Clean IPFS metadata resolution
4414
+ */
4415
+ class MetadataDomainService {
4416
+ constructor(ipfsApi) {
4417
+ this.ipfsApi = ipfsApi;
4418
+ }
4419
+ async fetchAndProcessMetadata(tokenUri, chainId) {
4420
+ return this.ipfsApi.fetchAndProcessMetadata(tokenUri, chainId);
4421
+ }
4422
+ async resolveIPFSUrl(url, chainId) {
4423
+ return this.ipfsApi.resolveIPFSUrl(url, chainId);
4424
+ }
4425
+ }
4426
+
4427
+ /**
4428
+ * ContractDomainService - Clean contract analysis without external dependencies
4429
+ */
4430
+ class ContractDomainService {
4431
+ constructor() { }
4432
+ analyzeContract(abi) {
4433
+ const methods = abi.filter(item => item.type === 'function').map(item => item.name);
4434
+ // ERC-721 detection
4435
+ const hasOwnerOf = methods.includes('ownerOf');
4436
+ const hasTokenURI = methods.includes('tokenURI');
4437
+ const hasTransferFrom = methods.includes('transferFrom');
4438
+ const isERC721 = hasOwnerOf && hasTokenURI && hasTransferFrom;
4439
+ // ERC-1155 detection
4440
+ const hasBalanceOfBatch = methods.includes('balanceOfBatch');
4441
+ const hasSafeBatchTransferFrom = methods.includes('safeBatchTransferFrom');
4442
+ const hasURI = methods.includes('uri');
4443
+ const isERC1155 = hasBalanceOfBatch && hasSafeBatchTransferFrom && hasURI;
4444
+ return {
4445
+ hasEnumeration: methods.includes('tokenByIndex') && methods.includes('totalSupply'),
4446
+ hasOwnerOf,
4447
+ hasBalanceOf: methods.includes('balanceOf'),
4448
+ hasTokenURI,
4449
+ hasTransfer: methods.includes('transfer') || methods.includes('transferFrom'),
4450
+ hasApprove: methods.includes('approve'),
4451
+ isERC721,
4452
+ isERC1155
4337
4453
  };
4338
- // ✅ CACHE SET: Store complete collection response
4339
- this.cache.set(cacheKey, response);
4340
- return response;
4341
4454
  }
4342
- // ==========================================
4343
- // HELPER METHODS
4344
- // ==========================================
4345
- formatBalance(rawBalance, decimals) {
4346
- const balance = rawBalance / Math.pow(10, decimals);
4347
- return balance.toLocaleString('en-US', {
4348
- minimumFractionDigits: 0,
4349
- maximumFractionDigits: decimals > 0 ? 2 : 0
4455
+ supportsEnumeration(abi) {
4456
+ return this.analyzeContract(abi).hasEnumeration;
4457
+ }
4458
+ supportsMethod(abi, methodName) {
4459
+ const methods = abi.filter(item => item.type === 'function').map(item => item.name);
4460
+ return methods.includes(methodName);
4461
+ }
4462
+ }
4463
+
4464
+ /**
4465
+ * Web3ApplicationService - Application layer entrance point
4466
+ * Orchestrates domain services and provides clean public interface
4467
+ * Simplified architecture with concrete classes
4468
+ */
4469
+ class Web3ApplicationService {
4470
+ constructor(web3Api, ipfsApi) {
4471
+ // Type-safe metadata conversion methods for ERC-721/ERC-1155 standards
4472
+ this.metadataMapper = {
4473
+ fromERCStandard: (ercMetadata) => ({
4474
+ name: ercMetadata.name || '',
4475
+ description: ercMetadata.description || '',
4476
+ imageUrl: ercMetadata.image || '',
4477
+ externalUrl: ercMetadata.external_url,
4478
+ animationUrl: ercMetadata.animation_url,
4479
+ animationUrlConverted: undefined, // Will be set by IPFS conversion
4480
+ attributes: ercMetadata.attributes || [],
4481
+ ...ercMetadata
4482
+ }),
4483
+ toERCStandard: (metadata) => ({
4484
+ name: metadata.name,
4485
+ description: metadata.description,
4486
+ image: metadata.imageUrl,
4487
+ animation_url: metadata.animationUrl,
4488
+ external_url: metadata.externalUrl,
4489
+ attributes: metadata.attributes,
4490
+ ...Object.fromEntries(Object.entries(metadata).filter(([key]) => !['name', 'description', 'imageUrl', 'animationUrl', 'externalUrl', 'attributes', 'animationUrlConverted'].includes(key)))
4491
+ })
4492
+ };
4493
+ // Create domain services with injected infrastructure dependencies
4494
+ this.contractDomainService = new ContractDomainService();
4495
+ this.metadataDomainService = new MetadataDomainService(ipfsApi);
4496
+ this.tokenDomainService = new TokenDomainService(web3Api, this.metadataDomainService, this.contractDomainService);
4497
+ }
4498
+ /**
4499
+ * Get balance and metadata for a specific token
4500
+ */
4501
+ async getSpecificTokenBalance(request) {
4502
+ if (!request.tokenId) {
4503
+ return this.tokenDomainService.getTokenBalance({
4504
+ accountAddress: request.accountAddress || '',
4505
+ contractAddress: request.contractAddress,
4506
+ abi: request.abi,
4507
+ tokenId: '',
4508
+ chainId: request.chainId
4509
+ });
4510
+ }
4511
+ return this.tokenDomainService.getTokenWithMetadata({
4512
+ accountAddress: request.accountAddress || '',
4513
+ contractAddress: request.contractAddress,
4514
+ abi: request.abi,
4515
+ tokenId: request.tokenId || '',
4516
+ chainId: request.chainId
4517
+ });
4518
+ }
4519
+ /**
4520
+ * Get metadata for a specific token from on-chain
4521
+ */
4522
+ async getTokenMetadata(request) {
4523
+ const domainResult = await this.tokenDomainService.getTokenMetadata({
4524
+ contractAddress: request.contractAddress,
4525
+ abi: request.abi,
4526
+ tokenId: request.tokenId || '',
4527
+ chainId: request.chainId
4528
+ });
4529
+ return domainResult.metadata;
4530
+ }
4531
+ /**
4532
+ * Retrieve entire collection of tokens with balance and metadata
4533
+ */
4534
+ async getTokenCollection(request) {
4535
+ return this.tokenDomainService.getTokenCollection({
4536
+ accountAddress: request.accountAddress || '',
4537
+ contractAddress: request.contractAddress,
4538
+ abi: request.abi,
4539
+ chainId: request.chainId,
4540
+ maxTokens: request.maxTokens,
4541
+ tokenIds: request.tokenIds
4350
4542
  });
4351
4543
  }
4352
- // ✅ FIXED: Update method signature to handle null properly
4353
- findMetadata(metadata, tokenId) {
4354
- if (!metadata || tokenId === null)
4544
+ /**
4545
+ * Resolve IPFS URLs to HTTPS if needed
4546
+ */
4547
+ async resolveIPFSUrl(url, chainId) {
4548
+ return this.metadataDomainService.resolveIPFSUrl(url, chainId);
4549
+ }
4550
+ /**
4551
+ * Fetch and process metadata from URI with IPFS conversion
4552
+ */
4553
+ async fetchAndProcessMetadata(tokenUri, chainId) {
4554
+ const domainMetadata = await this.metadataDomainService.fetchAndProcessMetadata(tokenUri, chainId);
4555
+ if (!domainMetadata)
4355
4556
  return null;
4356
- return metadata.find(m => m.tokenMetadataIncrementalId?.toString() === tokenId) || null;
4557
+ // Convert from ERC token standard to our clean interface
4558
+ const cleanMetadata = this.metadataMapper.fromERCStandard(domainMetadata);
4559
+ // Add IPFS conversion if needed
4560
+ if (cleanMetadata.animationUrl?.startsWith('ipfs://')) {
4561
+ return {
4562
+ ...cleanMetadata,
4563
+ animationUrlConverted: await this.resolveIPFSUrl(cleanMetadata.animationUrl, chainId)
4564
+ };
4565
+ }
4566
+ return cleanMetadata;
4567
+ }
4568
+ }
4569
+
4570
+ /**
4571
+ * Web3InfrastructureApi - Infrastructure implementation for blockchain operations
4572
+ * Uses @explorins/web3-ts for Web3 interactions
4573
+ */
4574
+ class Web3InfrastructureApi {
4575
+ constructor(web3ChainService) {
4576
+ this.web3ChainService = web3ChainService;
4357
4577
  }
4358
- async fetchMetadata(uri, chainId) {
4578
+ async getTokenBalance(request) {
4359
4579
  try {
4360
- const httpUrl = await this.resolveIPFSUrl(uri, chainId);
4361
- const response = await fetch(httpUrl);
4362
- if (!response.ok) {
4363
- throw new Error(`HTTP ${response.status}: ${response.statusText}`);
4364
- }
4365
- return await response.json();
4580
+ if (request.tokenId !== null)
4581
+ request.tokenId = request.tokenId.toString();
4582
+ const web3 = await this.web3ChainService.getWeb3ByChainId(request.chainId);
4583
+ const contract = web3Ts.getSmartContractInstance(request.contractAddress, request.abi, web3);
4584
+ return await web3Ts.getAccountTokenBalance(contract, request.accountAddress, request.tokenId);
4366
4585
  }
4367
4586
  catch (error) {
4368
- console.warn('Failed to fetch NFT metadata:', error);
4369
- return null;
4587
+ console.error(`Failed to get token balance for ${request.accountAddress} for ${request.contractAddress} and tokenId ${request.tokenId}, return 0 instead:`, error);
4588
+ return 0;
4370
4589
  }
4371
4590
  }
4591
+ async getTokenUri(request) {
4592
+ try {
4593
+ const web3 = await this.web3ChainService.getWeb3ByChainId(request.chainId);
4594
+ const contract = web3Ts.getSmartContractInstance(request.contractAddress, request.abi, web3);
4595
+ const tokenId = Number(request.tokenId);
4596
+ const tokenUri = await web3Ts.getTokenUri(contract, tokenId);
4597
+ return String(tokenUri);
4598
+ }
4599
+ catch (error) {
4600
+ console.error(`Failed to get token URI for tokenId ${request.tokenId}:`, error);
4601
+ throw error;
4602
+ }
4603
+ }
4604
+ async getTokenOfOwnerByIndex(request) {
4605
+ try {
4606
+ const web3 = await this.web3ChainService.getWeb3ByChainId(request.chainId);
4607
+ const tokenContract = web3Ts.getSmartContractInstance(request.contractAddress, request.abi, web3);
4608
+ const tokenId = await web3Ts.getTokenOfOwnerByIndex(tokenContract, request.accountAddress, request.tokenIndex);
4609
+ return String(tokenId);
4610
+ }
4611
+ catch (error) {
4612
+ console.error(`Failed to get token by index ${request.tokenIndex} for ${request.accountAddress}:`, error);
4613
+ throw error;
4614
+ }
4615
+ }
4616
+ }
4617
+
4618
+ /**
4619
+ * IPFSInfrastructureApi - Infrastructure implementation for IPFS operations
4620
+ * Uses Web3ChainService for IPFS gateway resolution
4621
+ */
4622
+ class IPFSInfrastructureApi {
4623
+ constructor(web3ChainService) {
4624
+ this.web3ChainService = web3ChainService;
4625
+ this.defaultIpfsGatewayDomain = 'pers.mypinata.cloud';
4626
+ }
4372
4627
  async getIpfsGatewayDomain(chainId) {
4373
4628
  try {
4374
4629
  const chainData = await this.web3ChainService.getChainDataWithCache(chainId);
@@ -4380,29 +4635,66 @@ class Web3Service {
4380
4635
  }
4381
4636
  }
4382
4637
  async resolveIPFSUrl(url, chainId) {
4383
- if (!url)
4384
- return '';
4385
4638
  if (url.startsWith('ipfs://')) {
4386
- const gatewayDomain = await this.getIpfsGatewayDomain(chainId);
4387
- return `https://${gatewayDomain}/ipfs/${url.slice(7)}`;
4639
+ const gateway = await this.getIpfsGatewayDomain(chainId);
4640
+ return url.replace('ipfs://', `https://${gateway}/ipfs/`);
4388
4641
  }
4389
4642
  return url;
4390
4643
  }
4644
+ async fetchAndProcessMetadata(tokenUri, chainId) {
4645
+ try {
4646
+ const resolvedUri = await this.resolveIPFSUrl(tokenUri, chainId);
4647
+ const response = await fetch(resolvedUri);
4648
+ if (!response.ok) {
4649
+ throw new Error(`HTTP error! status: ${response.status}`);
4650
+ }
4651
+ const metadata = await response.json();
4652
+ // Process and return clean metadata
4653
+ return {
4654
+ name: metadata.name || '',
4655
+ description: metadata.description || '',
4656
+ image: metadata.image ? await this.resolveIPFSUrl(metadata.image, chainId) : '',
4657
+ attributes: metadata.attributes || [],
4658
+ animation_url: metadata.animation_url ? await this.resolveIPFSUrl(metadata.animation_url, chainId) : undefined,
4659
+ external_url: metadata.external_url || undefined
4660
+ };
4661
+ }
4662
+ catch (error) {
4663
+ console.error('Error fetching metadata:', error);
4664
+ return null;
4665
+ }
4666
+ }
4667
+ async fetchFromUrl(url) {
4668
+ try {
4669
+ const response = await fetch(url);
4670
+ if (!response.ok) {
4671
+ throw new Error(`Failed to fetch from ${url}: ${response.statusText}`);
4672
+ }
4673
+ return await response.json();
4674
+ }
4675
+ catch (error) {
4676
+ console.error(`Error fetching from URL ${url}:`, error);
4677
+ throw error;
4678
+ }
4679
+ }
4391
4680
  }
4392
4681
 
4393
- //import { PublicHttpProviderService } from '../web3-chain/services/public-http-provider.service';
4394
4682
  function createWeb3SDK(apiClient) {
4395
4683
  // TODO: FIX LATER - TEMPORARY CONSTRUCTION
4396
4684
  const web3ProviderService = new Web3ProviderService();
4397
4685
  const web3ChainSDK = createWeb3ChainSDK(apiClient, web3ProviderService);
4398
- const web3Api = new Web3Api(web3ChainSDK.service);
4399
- const web3Service = new Web3Service(web3Api, web3ChainSDK.service);
4686
+ // Create Web3ApplicationService - main entry point for all Web3 operations
4687
+ const web3InfrastructureApi = new Web3InfrastructureApi(web3ChainSDK.service);
4688
+ const ipfsInfrastructureApi = new IPFSInfrastructureApi(web3ChainSDK.service);
4689
+ const web3ApplicationService = new Web3ApplicationService(web3InfrastructureApi, ipfsInfrastructureApi);
4690
+ // Clean SDK - all functions route through Web3ApplicationService
4400
4691
  return {
4401
- getCreditsBalance: (request) => web3Service.getERC20Balance(request),
4402
- getRewardsCollection: (request) => web3Service.getERC1155Collection(request),
4403
- getStampsCollection: (request) => web3Service.getERC721Collection(request),
4404
- api: web3Api,
4405
- service: web3Service
4692
+ getTokenBalance: (request) => web3ApplicationService.getSpecificTokenBalance(request),
4693
+ getTokenMetadata: (request) => web3ApplicationService.getTokenMetadata(request),
4694
+ getTokenCollection: (request) => web3ApplicationService.getTokenCollection(request),
4695
+ resolveIPFSUrl: (url, chainId) => web3ApplicationService.resolveIPFSUrl(url, chainId),
4696
+ fetchAndProcessMetadata: (tokenUri, chainId) => web3ApplicationService.fetchAndProcessMetadata(tokenUri, chainId),
4697
+ applicationService: web3ApplicationService
4406
4698
  };
4407
4699
  }
4408
4700
 
@@ -4419,6 +4711,7 @@ exports.ChainTypes = ChainTypes;
4419
4711
  exports.DEFAULT_PERS_CONFIG = DEFAULT_PERS_CONFIG;
4420
4712
  exports.DonationApi = DonationApi;
4421
4713
  exports.DonationService = DonationService;
4714
+ exports.IPFSInfrastructureApi = IPFSInfrastructureApi;
4422
4715
  exports.PaymentApi = PurchaseApi;
4423
4716
  exports.PaymentService = PaymentService;
4424
4717
  exports.PersApiClient = PersApiClient;
@@ -4426,7 +4719,6 @@ exports.PersApiError = PersApiError;
4426
4719
  exports.PersSDK = PersSDK;
4427
4720
  exports.RedemptionApi = RedemptionApi;
4428
4721
  exports.RedemptionService = RedemptionService;
4429
- exports.SimpleCache = SimpleCache;
4430
4722
  exports.TenantApi = TenantApi;
4431
4723
  exports.TenantService = TenantService;
4432
4724
  exports.TokenApi = TokenApi;
@@ -4438,8 +4730,10 @@ exports.UserApi = UserApi;
4438
4730
  exports.UserService = UserService;
4439
4731
  exports.UserStatusApi = UserStatusApi;
4440
4732
  exports.UserStatusService = UserStatusService;
4733
+ exports.Web3ApplicationService = Web3ApplicationService;
4441
4734
  exports.Web3ChainApi = Web3ChainApi;
4442
4735
  exports.Web3ChainService = Web3ChainService;
4736
+ exports.Web3InfrastructureApi = Web3InfrastructureApi;
4443
4737
  exports.Web3ProviderService = Web3ProviderService;
4444
4738
  exports.buildApiRoot = buildApiRoot;
4445
4739
  exports.createAnalyticsSDK = createAnalyticsSDK;