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