@explorins/pers-sdk 1.2.2 → 1.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/auth-admin/api/auth-admin-api.d.ts +3 -2
- package/dist/auth-admin/api/auth-admin-api.d.ts.map +1 -1
- package/dist/auth-admin.cjs +7 -2
- package/dist/auth-admin.cjs.map +1 -1
- package/dist/auth-admin.js +7 -2
- package/dist/auth-admin.js.map +1 -1
- package/dist/business/api/business-api.d.ts +17 -32
- package/dist/business/api/business-api.d.ts.map +1 -1
- package/dist/business.cjs +26 -50
- package/dist/business.cjs.map +1 -1
- package/dist/business.js +26 -50
- package/dist/business.js.map +1 -1
- package/dist/campaign/api/campaign-api.d.ts +47 -30
- package/dist/campaign/api/campaign-api.d.ts.map +1 -1
- package/dist/campaign/index.d.ts +5 -5
- package/dist/campaign/services/campaign-service.d.ts +6 -6
- package/dist/campaign/services/campaign-service.d.ts.map +1 -1
- package/dist/campaign.cjs +62 -41
- package/dist/campaign.cjs.map +1 -1
- package/dist/campaign.js +62 -41
- package/dist/campaign.js.map +1 -1
- package/dist/index.cjs +719 -438
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +718 -439
- package/dist/index.js.map +1 -1
- package/dist/package.json +1 -1
- package/dist/redemption/api/redemption-api.d.ts +58 -14
- package/dist/redemption/api/redemption-api.d.ts.map +1 -1
- package/dist/redemption/index.d.ts +2 -2
- package/dist/redemption/models/index.d.ts +1 -1
- package/dist/redemption/models/index.d.ts.map +1 -1
- package/dist/redemption/services/redemption-service.d.ts +3 -3
- package/dist/redemption/services/redemption-service.d.ts.map +1 -1
- package/dist/redemption.cjs +89 -15
- package/dist/redemption.cjs.map +1 -1
- package/dist/redemption.js +89 -15
- package/dist/redemption.js.map +1 -1
- package/dist/shared/interfaces/pers-shared-lib.interfaces.d.ts +3 -3
- package/dist/shared/interfaces/pers-shared-lib.interfaces.d.ts.map +1 -1
- package/dist/transaction/api/transaction-api.d.ts +23 -19
- package/dist/transaction/api/transaction-api.d.ts.map +1 -1
- package/dist/transaction/index.d.ts +3 -7
- package/dist/transaction/index.d.ts.map +1 -1
- package/dist/transaction/models/index.d.ts +0 -1
- package/dist/transaction/models/index.d.ts.map +1 -1
- package/dist/transaction/services/transaction-service.d.ts +5 -7
- package/dist/transaction/services/transaction-service.d.ts.map +1 -1
- package/dist/transaction.cjs +85 -50
- package/dist/transaction.cjs.map +1 -1
- package/dist/transaction.js +85 -50
- package/dist/transaction.js.map +1 -1
- package/dist/web3/application/index.d.ts +6 -0
- package/dist/web3/application/index.d.ts.map +1 -0
- package/dist/web3/application/web3-application.service.d.ts +53 -0
- package/dist/web3/application/web3-application.service.d.ts.map +1 -0
- package/dist/web3/domain/models/index.d.ts +58 -0
- package/dist/web3/domain/models/index.d.ts.map +1 -0
- package/dist/web3/domain/services/contract-domain.service.d.ts +20 -0
- package/dist/web3/domain/services/contract-domain.service.d.ts.map +1 -0
- package/dist/web3/domain/services/index.d.ts +8 -0
- package/dist/web3/domain/services/index.d.ts.map +1 -0
- package/dist/web3/domain/services/metadata-domain.service.d.ts +12 -0
- package/dist/web3/domain/services/metadata-domain.service.d.ts.map +1 -0
- package/dist/web3/domain/services/token-domain.service.d.ts +48 -0
- package/dist/web3/domain/services/token-domain.service.d.ts.map +1 -0
- package/dist/web3/index.d.ts +10 -11
- package/dist/web3/index.d.ts.map +1 -1
- package/dist/web3/infrastructure/api/index.d.ts +6 -0
- package/dist/web3/infrastructure/api/index.d.ts.map +1 -0
- package/dist/web3/infrastructure/api/ipfs-api.d.ts +15 -0
- package/dist/web3/infrastructure/api/ipfs-api.d.ts.map +1 -0
- package/dist/web3/{api → infrastructure/api}/web3-api.d.ts +6 -2
- package/dist/web3/infrastructure/api/web3-api.d.ts.map +1 -0
- package/dist/web3/infrastructure/index.d.ts +2 -0
- package/dist/web3/infrastructure/index.d.ts.map +1 -0
- package/dist/web3.cjs +509 -336
- package/dist/web3.cjs.map +1 -1
- package/dist/web3.js +507 -336
- package/dist/web3.js.map +1 -1
- package/package.json +1 -1
- package/dist/web3/api/web3-api.d.ts.map +0 -1
- package/dist/web3/models/index.d.ts +0 -92
- package/dist/web3/models/index.d.ts.map +0 -1
- package/dist/web3/services/web3-service.d.ts +0 -21
- 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
|
|
507
|
-
*
|
|
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 /
|
|
523
|
+
* Endpoint: GET /business-types
|
|
521
524
|
* Auth: @ApiSecurity('projectKey')
|
|
522
525
|
*/
|
|
523
526
|
async getAllBusinessTypes() {
|
|
524
|
-
return this.apiClient.get(
|
|
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 /
|
|
532
|
+
* Endpoint: POST /business-types
|
|
530
533
|
* Auth: @TenantAdmin()
|
|
531
534
|
*/
|
|
532
535
|
async createBusinessType(dto) {
|
|
533
|
-
return this.apiClient.post(
|
|
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 /
|
|
541
|
+
* Endpoint: PUT /business-types
|
|
539
542
|
* Auth: @TenantAdmin()
|
|
540
543
|
*/
|
|
541
544
|
async updateBusinessType(dto) {
|
|
542
|
-
return this.apiClient.put(
|
|
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 /
|
|
550
|
+
* Endpoint: DELETE /business-types/{id}
|
|
548
551
|
* Auth: @TenantAdmin()
|
|
549
552
|
*/
|
|
550
553
|
async deleteBusinessType(id) {
|
|
551
|
-
return this.apiClient.delete(`${this.
|
|
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
|
|
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
|
-
*
|
|
590
|
+
* Get all active businesses (convenience method)
|
|
595
591
|
*
|
|
596
|
-
* Endpoint: GET /businesses
|
|
597
|
-
* Auth: @
|
|
592
|
+
* Endpoint: GET /businesses
|
|
593
|
+
* Auth: @ApiSecurity('projectKey')
|
|
598
594
|
*/
|
|
599
|
-
async
|
|
600
|
-
|
|
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(
|
|
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}/
|
|
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
|
-
|
|
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(
|
|
903
|
-
return this.apiClient.post(`${this.basePath}
|
|
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
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
*
|
|
1023
|
-
*
|
|
1024
|
-
* NEW ENDPOINT: POST /transactions/query-recipient
|
|
1020
|
+
* Query transactions by recipient using unified endpoint
|
|
1025
1021
|
*/
|
|
1026
1022
|
async queryTransactionsByRecipient(accountSelector) {
|
|
1027
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1116
|
-
}
|
|
1123
|
+
/* async createAdminTransaction(request: TransactionRequestDTO): Promise<TransactionRequestResponseDTO> {
|
|
1124
|
+
return this.transactionApi.createAdminTransaction(request);
|
|
1125
|
+
} */
|
|
1117
1126
|
/**
|
|
1118
1127
|
* ADMIN: Get all tenant transactions
|
|
1119
1128
|
*/
|
|
@@ -1167,15 +1176,16 @@ function createTransactionSDK(apiClient) {
|
|
|
1167
1176
|
// Direct access to service methods (primary interface)
|
|
1168
1177
|
// Public methods
|
|
1169
1178
|
getTransactionById: (transactionId) => transactionService.getTransactionById(transactionId),
|
|
1179
|
+
createTransaction: (request) => transactionService.createTransaction(request),
|
|
1170
1180
|
// Auth methods
|
|
1171
|
-
createAuthTransaction: (request) => transactionService.createAuthTransaction(request),
|
|
1181
|
+
// createAuthTransaction: (request: TransactionRequestDTO) => transactionService.createAuthTransaction(request),
|
|
1172
1182
|
getUserTransactionHistory: (type) => transactionService.getUserTransactionHistory(type),
|
|
1173
|
-
prepareClientSignedTransaction: (request) => transactionService.prepareClientSignedTransaction(request),
|
|
1174
|
-
burnUserTokens: (request) => transactionService.burnUserTokens(request),
|
|
1183
|
+
//prepareClientSignedTransaction: (request: TransactionRequestDTO) => transactionService.prepareClientSignedTransaction(request),
|
|
1184
|
+
// burnUserTokens: (request: UserBurnTokenRequestDTO) => transactionService.burnUserTokens(request),
|
|
1175
1185
|
// Business methods
|
|
1176
|
-
createBusinessTransaction: (request) => transactionService.createBusinessTransaction(request),
|
|
1186
|
+
// createBusinessTransaction: (request: TransactionRequestDTO) => transactionService.createBusinessTransaction(request),
|
|
1177
1187
|
// Admin methods
|
|
1178
|
-
createAdminTransaction: (request) => transactionService.createAdminTransaction(request),
|
|
1188
|
+
// createAdminTransaction: (request: TransactionRequestDTO) => transactionService.createAdminTransaction(request),
|
|
1179
1189
|
getTenantTransactions: () => transactionService.getTenantTransactions(),
|
|
1180
1190
|
getPaginatedTransactions: (params) => transactionService.getPaginatedTransactions(params),
|
|
1181
1191
|
exportTransactionsCSV: () => transactionService.exportTransactionsCSV(),
|
|
@@ -1277,8 +1287,11 @@ class AuthAdminApi {
|
|
|
1277
1287
|
* Note: JWT handling and auth bypass headers may need special implementation
|
|
1278
1288
|
*/
|
|
1279
1289
|
async loginTenantAdmin(jwt) {
|
|
1280
|
-
|
|
1281
|
-
|
|
1290
|
+
const body = {
|
|
1291
|
+
authToken: jwt,
|
|
1292
|
+
authType: persShared.AccountOwnerType.TENANT
|
|
1293
|
+
};
|
|
1294
|
+
return this.apiClient.post(`${this.basePath}/token`, body);
|
|
1282
1295
|
}
|
|
1283
1296
|
/**
|
|
1284
1297
|
* ADMIN: Refresh access token
|
|
@@ -1587,81 +1600,102 @@ class CampaignApi {
|
|
|
1587
1600
|
* NEW: POST /campaign-claims/user
|
|
1588
1601
|
*/
|
|
1589
1602
|
async claimCampaign(request) {
|
|
1590
|
-
return this.apiClient.post('/campaign-claims
|
|
1603
|
+
return this.apiClient.post('/campaign-claims', request);
|
|
1591
1604
|
}
|
|
1592
1605
|
/**
|
|
1593
1606
|
* USER: Get claims for logged user
|
|
1594
1607
|
* NEW: GET /campaign-claims/users/me
|
|
1595
1608
|
*/
|
|
1596
1609
|
async getClaimsForLoggedUser() {
|
|
1597
|
-
return this.apiClient.get('/campaign-claims/
|
|
1610
|
+
return this.apiClient.get('/campaign-claims/me');
|
|
1598
1611
|
}
|
|
1599
1612
|
/**
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
async
|
|
1604
|
-
|
|
1613
|
+
* ADMIN: Get all campaign claims
|
|
1614
|
+
* Updated to use unified endpoint
|
|
1615
|
+
*/
|
|
1616
|
+
async getCampaignClaims() {
|
|
1617
|
+
// Admin context - no parameters needed for all claims
|
|
1618
|
+
return this.apiClient.get('/campaign-claims');
|
|
1605
1619
|
}
|
|
1606
1620
|
/**
|
|
1607
|
-
*
|
|
1608
|
-
*
|
|
1621
|
+
* ADMIN: Get campaign claims by campaign ID
|
|
1622
|
+
* Updated to use query parameters
|
|
1609
1623
|
*/
|
|
1610
|
-
async
|
|
1611
|
-
return this.apiClient.
|
|
1624
|
+
async getCampaignClaimsByCampaignId(campaignId) {
|
|
1625
|
+
return this.apiClient.get(`/campaign-claims?campaignId=${campaignId}`);
|
|
1612
1626
|
}
|
|
1613
1627
|
/**
|
|
1614
|
-
* ADMIN:
|
|
1615
|
-
*
|
|
1628
|
+
* ADMIN: Get campaign claims by user ID
|
|
1629
|
+
* Updated to use query parameters
|
|
1616
1630
|
*/
|
|
1617
|
-
async
|
|
1618
|
-
return this.apiClient.
|
|
1631
|
+
async getCampaignClaimsByUserId(userId) {
|
|
1632
|
+
return this.apiClient.get(`/campaign-claims?userId=${userId}`);
|
|
1619
1633
|
}
|
|
1620
1634
|
/**
|
|
1621
|
-
* ADMIN: Get
|
|
1622
|
-
*
|
|
1635
|
+
* ADMIN: Get campaign claims by business ID
|
|
1636
|
+
* Updated to use query parameters
|
|
1623
1637
|
*/
|
|
1624
|
-
async
|
|
1625
|
-
return this.apiClient.get(
|
|
1638
|
+
async getCampaignClaimsByBusinessId(businessId) {
|
|
1639
|
+
return this.apiClient.get(`/campaign-claims?businessId=${businessId}`);
|
|
1626
1640
|
}
|
|
1627
1641
|
/**
|
|
1628
|
-
* ADMIN: Get campaign claims by
|
|
1629
|
-
*
|
|
1642
|
+
* ADMIN: Get campaign claims by user ID for specific campaign
|
|
1643
|
+
* Combined filtering using query parameters
|
|
1630
1644
|
*/
|
|
1631
|
-
async
|
|
1632
|
-
return this.apiClient.get(`/campaign-claims
|
|
1645
|
+
async getCampaignClaimsByUserAndCampaign(userId, campaignId) {
|
|
1646
|
+
return this.apiClient.get(`/campaign-claims?userId=${userId}&campaignId=${campaignId}`);
|
|
1633
1647
|
}
|
|
1634
1648
|
/**
|
|
1635
|
-
* ADMIN: Get campaign claims by
|
|
1636
|
-
*
|
|
1649
|
+
* ADMIN: Get campaign claims by business ID for specific campaign
|
|
1650
|
+
* Combined filtering using query parameters
|
|
1637
1651
|
*/
|
|
1638
|
-
async
|
|
1639
|
-
return this.apiClient.get(`/campaign-claims
|
|
1652
|
+
async getCampaignClaimsByBusinessAndCampaign(businessId, campaignId) {
|
|
1653
|
+
return this.apiClient.get(`/campaign-claims?businessId=${businessId}&campaignId=${campaignId}`);
|
|
1640
1654
|
}
|
|
1641
1655
|
/**
|
|
1642
|
-
*
|
|
1643
|
-
*
|
|
1656
|
+
* USER: Get user's own claims (all campaigns)
|
|
1657
|
+
* Use convenience endpoint
|
|
1644
1658
|
*/
|
|
1645
|
-
async
|
|
1646
|
-
return this.apiClient.get(
|
|
1659
|
+
async getUserClaims() {
|
|
1660
|
+
return this.apiClient.get('/campaign-claims/me');
|
|
1647
1661
|
}
|
|
1648
1662
|
/**
|
|
1649
1663
|
* USER: Get user's claims for specific campaign
|
|
1650
|
-
*
|
|
1664
|
+
* Use convenience endpoint with query parameter
|
|
1651
1665
|
*/
|
|
1652
1666
|
async getUserClaimsForCampaign(campaignId) {
|
|
1653
|
-
return this.apiClient.get(`/campaign-claims/
|
|
1667
|
+
return this.apiClient.get(`/campaign-claims/me?campaignId=${campaignId}`);
|
|
1668
|
+
}
|
|
1669
|
+
/**
|
|
1670
|
+
* BUSINESS: Get business claims (all campaigns)
|
|
1671
|
+
* Uses unified endpoint with business context
|
|
1672
|
+
*/
|
|
1673
|
+
async getBusinessClaims() {
|
|
1674
|
+
return this.apiClient.get('/campaign-claims');
|
|
1675
|
+
}
|
|
1676
|
+
/**
|
|
1677
|
+
* BUSINESS: Get business claims for specific campaign
|
|
1678
|
+
* Uses unified endpoint with business context and campaign filter
|
|
1679
|
+
*/
|
|
1680
|
+
async getBusinessClaimsForCampaign(campaignId) {
|
|
1681
|
+
return this.apiClient.get(`/campaign-claims?campaignId=${campaignId}`);
|
|
1682
|
+
}
|
|
1683
|
+
/**
|
|
1684
|
+
* Helper: Build query string from parameters
|
|
1685
|
+
*/
|
|
1686
|
+
buildQueryString(params) {
|
|
1687
|
+
const validParams = Object.entries(params)
|
|
1688
|
+
.filter(([_, value]) => value !== undefined)
|
|
1689
|
+
.map(([key, value]) => `${key}=${encodeURIComponent(value)}`)
|
|
1690
|
+
.join('&');
|
|
1691
|
+
return validParams ? `?${validParams}` : '';
|
|
1654
1692
|
}
|
|
1655
|
-
// ==========================================
|
|
1656
|
-
// BACKWARD COMPATIBILITY (DEPRECATED)
|
|
1657
|
-
// ==========================================
|
|
1658
1693
|
/**
|
|
1659
|
-
*
|
|
1660
|
-
* LEGACY: Get campaigns with active filter
|
|
1694
|
+
* Flexible admin claims query with multiple filters
|
|
1661
1695
|
*/
|
|
1662
|
-
async
|
|
1663
|
-
|
|
1664
|
-
return this.
|
|
1696
|
+
async getAdminClaims(filters) {
|
|
1697
|
+
const queryString = this.buildQueryString(filters || {});
|
|
1698
|
+
return this.apiClient.get(`/campaign-claims${queryString}`);
|
|
1665
1699
|
}
|
|
1666
1700
|
}
|
|
1667
1701
|
|
|
@@ -2172,20 +2206,21 @@ function createPaymentSDK(apiClient) {
|
|
|
2172
2206
|
/**
|
|
2173
2207
|
* Platform-Agnostic Redemption API Client (UPDATED - RESTful Design)
|
|
2174
2208
|
*
|
|
2175
|
-
* Updated to work with the new RESTful /redemptions endpoints.
|
|
2209
|
+
* Updated to work with the new RESTful /redemptions and /redemption-redeems endpoints.
|
|
2176
2210
|
* Handles redemption operations using the PERS backend with intelligent access detection.
|
|
2177
2211
|
* Uses @explorins/pers-shared DTOs for consistency with backend.
|
|
2178
2212
|
*
|
|
2179
|
-
* Migration Update: Updated all endpoints
|
|
2213
|
+
* Migration Update: Updated all endpoints for unified controller pattern
|
|
2180
2214
|
* - Removed role revelation from URLs (no more /admin, /auth paths)
|
|
2181
2215
|
* - Added intelligent access detection for unified endpoints
|
|
2182
|
-
* -
|
|
2183
|
-
* - Enhanced redemption process with
|
|
2216
|
+
* - Added new /redemption-redeems endpoints for redeem processing
|
|
2217
|
+
* - Enhanced redemption process with role-based access control
|
|
2184
2218
|
*/
|
|
2185
2219
|
class RedemptionApi {
|
|
2186
2220
|
constructor(apiClient) {
|
|
2187
2221
|
this.apiClient = apiClient;
|
|
2188
2222
|
this.basePath = '/redemptions';
|
|
2223
|
+
this.redeemsPath = '/redemption-redeems';
|
|
2189
2224
|
}
|
|
2190
2225
|
// ==========================================
|
|
2191
2226
|
// PUBLIC OPERATIONS (Project Key)
|
|
@@ -2220,7 +2255,7 @@ class RedemptionApi {
|
|
|
2220
2255
|
* Updated: /redemption/type → /redemptions/types
|
|
2221
2256
|
*/
|
|
2222
2257
|
async getRedemptionTypes() {
|
|
2223
|
-
return this.apiClient.get(
|
|
2258
|
+
return this.apiClient.get(`/redemption-types`);
|
|
2224
2259
|
}
|
|
2225
2260
|
/**
|
|
2226
2261
|
* PUBLIC: Get redemption by ID
|
|
@@ -2233,33 +2268,106 @@ class RedemptionApi {
|
|
|
2233
2268
|
/**
|
|
2234
2269
|
* PUBLIC: Get available supply for redemption
|
|
2235
2270
|
*
|
|
2236
|
-
* Updated: /redemption/:id/available-supply → /redemptions/:id/
|
|
2271
|
+
* Updated: /redemption/:id/available-supply → /redemptions/:id/supply
|
|
2237
2272
|
*/
|
|
2238
2273
|
async getRedemptionAvailableSupply(id) {
|
|
2239
|
-
return this.apiClient.get(`${this.basePath}/${id}/
|
|
2274
|
+
return this.apiClient.get(`${this.basePath}/${id}/supply`);
|
|
2240
2275
|
}
|
|
2241
2276
|
// ==========================================
|
|
2242
|
-
//
|
|
2277
|
+
// REDEMPTION EXECUTION (NEW UNIFIED ENDPOINT)
|
|
2243
2278
|
// ==========================================
|
|
2244
2279
|
/**
|
|
2245
|
-
*
|
|
2280
|
+
* Execute redemption (unified endpoint)
|
|
2246
2281
|
*
|
|
2247
|
-
*
|
|
2248
|
-
*
|
|
2282
|
+
* NEW: POST /redemption-redeems - Role-based processing
|
|
2283
|
+
* - USER: Direct user redemption processing
|
|
2284
|
+
* - ADMIN: Can process redemptions for any account type
|
|
2285
|
+
* - BUSINESS: Process redemptions for customers
|
|
2249
2286
|
*/
|
|
2250
2287
|
async redeemRedemption(redemptionId) {
|
|
2251
2288
|
const body = {
|
|
2252
2289
|
redemptionId: redemptionId,
|
|
2253
2290
|
};
|
|
2254
|
-
return this.apiClient.post(
|
|
2291
|
+
return this.apiClient.post(this.redeemsPath, body);
|
|
2292
|
+
}
|
|
2293
|
+
// ==========================================
|
|
2294
|
+
// REDEMPTION REDEEMS QUERIES (NEW ENDPOINTS)
|
|
2295
|
+
// ==========================================
|
|
2296
|
+
/**
|
|
2297
|
+
* Get redemption redeems with filtering (unified endpoint)
|
|
2298
|
+
*
|
|
2299
|
+
* NEW: GET /redemption-redeems with query parameters
|
|
2300
|
+
* Role-based access: Users see only their own, admins can filter by userId/businessId
|
|
2301
|
+
*/
|
|
2302
|
+
async getRedemptionRedeems(filters) {
|
|
2303
|
+
let url = this.redeemsPath;
|
|
2304
|
+
const params = new URLSearchParams();
|
|
2305
|
+
if (filters?.redemptionId)
|
|
2306
|
+
params.append('redemptionId', filters.redemptionId);
|
|
2307
|
+
if (filters?.userId)
|
|
2308
|
+
params.append('userId', filters.userId);
|
|
2309
|
+
if (filters?.businessId)
|
|
2310
|
+
params.append('businessId', filters.businessId);
|
|
2311
|
+
const queryString = params.toString();
|
|
2312
|
+
if (queryString) {
|
|
2313
|
+
url += `?${queryString}`;
|
|
2314
|
+
}
|
|
2315
|
+
return this.apiClient.get(url);
|
|
2316
|
+
}
|
|
2317
|
+
/**
|
|
2318
|
+
* Get specific redemption redeem by ID
|
|
2319
|
+
*
|
|
2320
|
+
* NEW: GET /redemption-redeems/:id
|
|
2321
|
+
*/
|
|
2322
|
+
async getRedemptionRedeemById(id) {
|
|
2323
|
+
return this.apiClient.get(`${this.redeemsPath}/${id}`);
|
|
2324
|
+
}
|
|
2325
|
+
/**
|
|
2326
|
+
* USER: Get my redemption redeems (convenience endpoint)
|
|
2327
|
+
*
|
|
2328
|
+
* NEW: GET /redemption-redeems/me with optional filtering
|
|
2329
|
+
*/
|
|
2330
|
+
async getMyRedemptionRedeems(redemptionId) {
|
|
2331
|
+
let url = `${this.redeemsPath}/me`;
|
|
2332
|
+
if (redemptionId) {
|
|
2333
|
+
url += `?redemptionId=${redemptionId}`;
|
|
2334
|
+
}
|
|
2335
|
+
return this.apiClient.get(url);
|
|
2336
|
+
}
|
|
2337
|
+
/**
|
|
2338
|
+
* ADMIN: Get redemption redeems by user ID
|
|
2339
|
+
*
|
|
2340
|
+
* NEW: GET /redemption-redeems?userId=X
|
|
2341
|
+
*/
|
|
2342
|
+
async getRedemptionRedeemsByUserId(userId, redemptionId) {
|
|
2343
|
+
return this.getRedemptionRedeems({ userId, redemptionId });
|
|
2344
|
+
}
|
|
2345
|
+
/**
|
|
2346
|
+
* ADMIN: Get redemption redeems by business ID
|
|
2347
|
+
*
|
|
2348
|
+
* NEW: GET /redemption-redeems?businessId=X
|
|
2349
|
+
*/
|
|
2350
|
+
async getRedemptionRedeemsByBusinessId(businessId, redemptionId) {
|
|
2351
|
+
return this.getRedemptionRedeems({ businessId, redemptionId });
|
|
2255
2352
|
}
|
|
2353
|
+
/**
|
|
2354
|
+
* ADMIN: Get redemption redeems by redemption ID
|
|
2355
|
+
*
|
|
2356
|
+
* NEW: GET /redemption-redeems?redemptionId=X
|
|
2357
|
+
*/
|
|
2358
|
+
async getRedemptionRedeemsByRedemptionId(redemptionId) {
|
|
2359
|
+
return this.getRedemptionRedeems({ redemptionId });
|
|
2360
|
+
}
|
|
2361
|
+
// ==========================================
|
|
2362
|
+
// USER OPERATIONS (JWT + Project Key)
|
|
2363
|
+
// ==========================================
|
|
2256
2364
|
/**
|
|
2257
2365
|
* USER: Get user redemption history
|
|
2258
2366
|
*
|
|
2259
|
-
* Updated:
|
|
2367
|
+
* Updated: Uses new convenience endpoint /redemption-redeems/me
|
|
2260
2368
|
*/
|
|
2261
2369
|
async getUserRedemptionHistory() {
|
|
2262
|
-
return this.
|
|
2370
|
+
return this.getMyRedemptionRedeems();
|
|
2263
2371
|
}
|
|
2264
2372
|
/**
|
|
2265
2373
|
* USER: Get user redemptions (backward compatibility)
|
|
@@ -2328,7 +2436,7 @@ class RedemptionApi {
|
|
|
2328
2436
|
* Updated: /redemption/admin/type → /redemptions/types
|
|
2329
2437
|
*/
|
|
2330
2438
|
async createRedemptionType(redemptionType) {
|
|
2331
|
-
return this.apiClient.post(`${this.basePath}/types`, redemptionType);
|
|
2439
|
+
return this.apiClient.post(`${this.basePath}/redemption-types`, redemptionType);
|
|
2332
2440
|
}
|
|
2333
2441
|
// ==========================================
|
|
2334
2442
|
// TOKEN UNIT MANAGEMENT (Admin)
|
|
@@ -4063,311 +4171,445 @@ function createWeb3ChainSDK(apiClient, providerService) {
|
|
|
4063
4171
|
};
|
|
4064
4172
|
}
|
|
4065
4173
|
|
|
4066
|
-
|
|
4067
|
-
|
|
4068
|
-
|
|
4174
|
+
/**
|
|
4175
|
+
* TokenDomainService - Domain service for token operations
|
|
4176
|
+
* Implements business logic for token balance, metadata, and collection operations
|
|
4177
|
+
*/
|
|
4178
|
+
class TokenDomainService {
|
|
4179
|
+
constructor(web3Api, metadataService, contractService) {
|
|
4180
|
+
this.web3Api = web3Api;
|
|
4181
|
+
this.metadataService = metadataService;
|
|
4182
|
+
this.contractService = contractService;
|
|
4069
4183
|
}
|
|
4070
4184
|
async getTokenBalance(request) {
|
|
4071
|
-
const
|
|
4072
|
-
const tokenContract = web3Ts.getSmartContractInstance(request.contractAddress, request.abi, web3);
|
|
4073
|
-
const balance = await web3Ts.getAddressTokenBalanceByContract(tokenContract, request.accountAddress, request.tokenId);
|
|
4074
|
-
return Number(balance);
|
|
4075
|
-
}
|
|
4076
|
-
async getTokenUri(request) {
|
|
4077
|
-
const web3 = await this.web3ChainService.getWeb3ByChainId(request.chainId);
|
|
4078
|
-
// ✅ DIRECT: Use web3-ts functions directly
|
|
4079
|
-
const tokenContract = web3Ts.getSmartContractInstance(request.contractAddress, request.abi, web3);
|
|
4080
|
-
const tokenId = Number(request.tokenId);
|
|
4081
|
-
const tokenUri = await web3Ts.getTokenUri(tokenContract, tokenId);
|
|
4082
|
-
return String(tokenUri);
|
|
4083
|
-
}
|
|
4084
|
-
async getTokenOfOwnerByIndex(request) {
|
|
4085
|
-
const web3 = await this.web3ChainService.getWeb3ByChainId(request.chainId);
|
|
4086
|
-
// ✅ DIRECT: Use web3-ts functions directly
|
|
4087
|
-
const tokenContract = web3Ts.getSmartContractInstance(request.contractAddress, request.abi, web3);
|
|
4088
|
-
const tokenId = await web3Ts.getTokenOfOwnerByIndex(tokenContract, request.accountAddress, request.tokenIndex);
|
|
4089
|
-
return String(tokenId);
|
|
4090
|
-
}
|
|
4091
|
-
}
|
|
4092
|
-
|
|
4093
|
-
class SimpleCache {
|
|
4094
|
-
constructor() {
|
|
4095
|
-
this.storage = {};
|
|
4096
|
-
this.defaultTTL = 10 * 1000; // 10 seconds
|
|
4097
|
-
}
|
|
4098
|
-
set(key, data, ttl) {
|
|
4099
|
-
this.storage[key] = {
|
|
4100
|
-
data,
|
|
4101
|
-
timestamp: Date.now(),
|
|
4102
|
-
ttl: ttl ?? this.defaultTTL
|
|
4103
|
-
};
|
|
4104
|
-
}
|
|
4105
|
-
get(key) {
|
|
4106
|
-
const entry = this.storage[key];
|
|
4107
|
-
if (!entry) {
|
|
4108
|
-
return null;
|
|
4109
|
-
}
|
|
4110
|
-
const now = Date.now();
|
|
4111
|
-
const isExpired = (now - entry.timestamp) > entry.ttl;
|
|
4112
|
-
if (isExpired) {
|
|
4113
|
-
delete this.storage[key];
|
|
4114
|
-
return null;
|
|
4115
|
-
}
|
|
4116
|
-
return entry.data;
|
|
4117
|
-
}
|
|
4118
|
-
clear() {
|
|
4119
|
-
this.storage = {};
|
|
4120
|
-
}
|
|
4121
|
-
cleanup() {
|
|
4122
|
-
const now = Date.now();
|
|
4123
|
-
Object.keys(this.storage).forEach(key => {
|
|
4124
|
-
const entry = this.storage[key];
|
|
4125
|
-
if ((now - entry.timestamp) > entry.ttl) {
|
|
4126
|
-
delete this.storage[key];
|
|
4127
|
-
}
|
|
4128
|
-
});
|
|
4129
|
-
}
|
|
4130
|
-
}
|
|
4131
|
-
|
|
4132
|
-
class Web3Service {
|
|
4133
|
-
constructor(web3Api, web3ChainService) {
|
|
4134
|
-
this.web3Api = web3Api;
|
|
4135
|
-
this.web3ChainService = web3ChainService;
|
|
4136
|
-
//temporary fix, remove when the backend supports custom gateways
|
|
4137
|
-
this.defaultIpfsGatewayDomain = 'pers.mypinata.cloud';
|
|
4138
|
-
// ✅ CACHE: Simple 10-second cache instance
|
|
4139
|
-
this.cache = new SimpleCache();
|
|
4140
|
-
this.cleanupInterval = null;
|
|
4141
|
-
this.cleanupInterval = setInterval(() => {
|
|
4142
|
-
this.cache.cleanup();
|
|
4143
|
-
}, 30 * 1000);
|
|
4144
|
-
}
|
|
4145
|
-
destroy() {
|
|
4146
|
-
if (this.cleanupInterval) {
|
|
4147
|
-
clearInterval(this.cleanupInterval);
|
|
4148
|
-
this.cleanupInterval = null;
|
|
4149
|
-
}
|
|
4150
|
-
this.cache.clear();
|
|
4151
|
-
}
|
|
4152
|
-
async getERC20Balance(request) {
|
|
4153
|
-
const cacheKey = `erc20_balance_${request.accountAddress}_${request.token.contractAddress}_${request.token.chainId}`;
|
|
4154
|
-
// ✅ CACHE CHECK: Try to get from cache first
|
|
4155
|
-
const cached = this.cache.get(cacheKey);
|
|
4156
|
-
if (cached) {
|
|
4157
|
-
console.debug(`💾 [Web3Service] Using cached ERC20 balance for ${request.token.symbol}`);
|
|
4158
|
-
return cached;
|
|
4159
|
-
}
|
|
4160
|
-
console.debug(`🔄 [Web3Service] Fetching fresh ERC20 balance for ${request.token.symbol}`);
|
|
4161
|
-
const rawBalance = await this.web3Api.getTokenBalance({
|
|
4185
|
+
const balance = await this.web3Api.getTokenBalance({
|
|
4162
4186
|
accountAddress: request.accountAddress,
|
|
4163
|
-
contractAddress: request.
|
|
4164
|
-
abi: request.
|
|
4165
|
-
tokenId:
|
|
4166
|
-
chainId: request.
|
|
4187
|
+
contractAddress: request.contractAddress,
|
|
4188
|
+
abi: request.abi,
|
|
4189
|
+
tokenId: request.tokenId,
|
|
4190
|
+
chainId: request.chainId
|
|
4167
4191
|
});
|
|
4168
|
-
|
|
4169
|
-
|
|
4170
|
-
|
|
4171
|
-
|
|
4172
|
-
|
|
4173
|
-
decimals,
|
|
4174
|
-
symbol,
|
|
4175
|
-
hasBalance: rawBalance > 0
|
|
4192
|
+
return {
|
|
4193
|
+
tokenId: request.tokenId,
|
|
4194
|
+
balance,
|
|
4195
|
+
hasBalance: balance > 0,
|
|
4196
|
+
metadata: null
|
|
4176
4197
|
};
|
|
4177
|
-
|
|
4178
|
-
|
|
4179
|
-
|
|
4180
|
-
|
|
4181
|
-
|
|
4182
|
-
|
|
4183
|
-
|
|
4184
|
-
|
|
4185
|
-
|
|
4186
|
-
|
|
4187
|
-
|
|
4188
|
-
|
|
4189
|
-
|
|
4190
|
-
|
|
4191
|
-
|
|
4192
|
-
|
|
4193
|
-
|
|
4194
|
-
|
|
4195
|
-
|
|
4196
|
-
|
|
4197
|
-
try {
|
|
4198
|
-
const rawBalance = await this.web3Api.getTokenBalance({
|
|
4199
|
-
accountAddress: request.accountAddress,
|
|
4200
|
-
contractAddress: token.contractAddress,
|
|
4201
|
-
abi: token.abi,
|
|
4202
|
-
tokenId,
|
|
4203
|
-
chainId: token.chainId
|
|
4204
|
-
});
|
|
4205
|
-
const decimals = token.decimals ?? 0; // ERC1155 usually no decimals
|
|
4206
|
-
return {
|
|
4207
|
-
tokenId,
|
|
4208
|
-
balance: rawBalance,
|
|
4209
|
-
formattedBalance: this.formatBalance(rawBalance, decimals),
|
|
4210
|
-
hasBalance: rawBalance > 0,
|
|
4211
|
-
// ✅ FIXED: Convert null to undefined for findMetadata
|
|
4212
|
-
metadata: this.findMetadata(token.metadata ?? undefined, tokenId)
|
|
4213
|
-
};
|
|
4214
|
-
}
|
|
4215
|
-
catch (error) {
|
|
4216
|
-
console.warn(`Failed to get balance for token ${token.contractAddress}:${tokenId}`, error);
|
|
4217
|
-
return null; // Skip failed tokens
|
|
4218
|
-
}
|
|
4219
|
-
}));
|
|
4220
|
-
// Filter successful results with balance > 0
|
|
4221
|
-
const successfulResults = [];
|
|
4222
|
-
for (const result of balanceResults) {
|
|
4223
|
-
if (result.status === 'fulfilled' && result.value !== null && result.value.hasBalance) {
|
|
4224
|
-
successfulResults.push(result.value);
|
|
4198
|
+
}
|
|
4199
|
+
async getTokenWithMetadata(params) {
|
|
4200
|
+
try {
|
|
4201
|
+
const balance = await this.web3Api.getTokenBalance({
|
|
4202
|
+
accountAddress: params.accountAddress,
|
|
4203
|
+
contractAddress: params.contractAddress,
|
|
4204
|
+
abi: params.abi,
|
|
4205
|
+
tokenId: params.tokenId,
|
|
4206
|
+
chainId: params.chainId
|
|
4207
|
+
});
|
|
4208
|
+
let metadata = null;
|
|
4209
|
+
if (balance > 0) {
|
|
4210
|
+
const tokenUri = await this.web3Api.getTokenUri({
|
|
4211
|
+
contractAddress: params.contractAddress,
|
|
4212
|
+
abi: params.abi,
|
|
4213
|
+
tokenId: params.tokenId,
|
|
4214
|
+
chainId: params.chainId
|
|
4215
|
+
});
|
|
4216
|
+
if (tokenUri) {
|
|
4217
|
+
metadata = await this.metadataService.fetchAndProcessMetadata(tokenUri, params.chainId);
|
|
4225
4218
|
}
|
|
4226
4219
|
}
|
|
4227
4220
|
return {
|
|
4228
|
-
|
|
4229
|
-
|
|
4221
|
+
tokenId: params.tokenId,
|
|
4222
|
+
balance,
|
|
4223
|
+
hasBalance: balance > 0,
|
|
4224
|
+
metadata
|
|
4230
4225
|
};
|
|
4231
|
-
}));
|
|
4232
|
-
const response = {
|
|
4233
|
-
accountAddress: request.accountAddress,
|
|
4234
|
-
tokens: tokenResults.filter(t => t.results.length > 0)
|
|
4235
|
-
};
|
|
4236
|
-
// ✅ CACHE SET: Store complete collection result
|
|
4237
|
-
this.cache.set(cacheKey, response);
|
|
4238
|
-
return response;
|
|
4239
|
-
}
|
|
4240
|
-
async getERC721Collection(request) {
|
|
4241
|
-
// ✅ CACHE KEY: Create unique cache key for NFT collection
|
|
4242
|
-
const contractAddresses = request.nftContracts.map(t => t.contractAddress).sort().join(',');
|
|
4243
|
-
const maxNFTs = request.maxNFTsPerContract || 50;
|
|
4244
|
-
const cacheKey = `erc721_collection_${request.accountAddress}_${contractAddresses}_${maxNFTs}`;
|
|
4245
|
-
// ✅ CACHE CHECK: Try to get from cache first
|
|
4246
|
-
const cached = this.cache.get(cacheKey);
|
|
4247
|
-
if (cached) {
|
|
4248
|
-
console.debug(`💾 [Web3Service] Using cached ERC721 collection for ${request.accountAddress}`);
|
|
4249
|
-
return cached;
|
|
4250
4226
|
}
|
|
4251
|
-
|
|
4252
|
-
|
|
4253
|
-
|
|
4254
|
-
|
|
4255
|
-
|
|
4256
|
-
|
|
4257
|
-
|
|
4258
|
-
|
|
4259
|
-
|
|
4260
|
-
|
|
4227
|
+
catch (error) {
|
|
4228
|
+
console.error('Error getting token with metadata:', error);
|
|
4229
|
+
return {
|
|
4230
|
+
tokenId: params.tokenId,
|
|
4231
|
+
balance: 0,
|
|
4232
|
+
hasBalance: false,
|
|
4233
|
+
metadata: null
|
|
4234
|
+
};
|
|
4235
|
+
}
|
|
4236
|
+
}
|
|
4237
|
+
async getTokenCollection(params) {
|
|
4238
|
+
try {
|
|
4239
|
+
const contractAnalysis = this.contractService.analyzeContract(params.abi);
|
|
4240
|
+
const tokens = [];
|
|
4241
|
+
if (!contractAnalysis.hasEnumeration && !contractAnalysis.isERC1155) {
|
|
4242
|
+
console.warn('Contract does not support enumeration, cannot retrieve full collection');
|
|
4243
|
+
return {
|
|
4244
|
+
accountAddress: params.accountAddress,
|
|
4245
|
+
contractAddress: params.contractAddress,
|
|
4246
|
+
totalBalance: 0,
|
|
4247
|
+
tokensRetrieved: 0,
|
|
4248
|
+
tokens: [],
|
|
4249
|
+
note: 'Contract does not support enumeration'
|
|
4250
|
+
};
|
|
4251
|
+
}
|
|
4252
|
+
else if (contractAnalysis.isERC1155) {
|
|
4253
|
+
const tokenIdsToProcess = params.tokenIds || [];
|
|
4254
|
+
if (tokenIdsToProcess.length > 0) {
|
|
4255
|
+
for (const tokenId of tokenIdsToProcess) {
|
|
4256
|
+
const tokenBalance = await this.getTokenWithMetadata({
|
|
4257
|
+
accountAddress: params.accountAddress,
|
|
4258
|
+
contractAddress: params.contractAddress,
|
|
4259
|
+
abi: params.abi,
|
|
4260
|
+
tokenId,
|
|
4261
|
+
chainId: params.chainId
|
|
4262
|
+
});
|
|
4263
|
+
tokens.push(tokenBalance);
|
|
4264
|
+
}
|
|
4265
|
+
}
|
|
4266
|
+
console.log('ERC-1155 User balances:', tokens);
|
|
4267
|
+
// ERC-1155: Cannot enumerate without knowing token IDs
|
|
4268
|
+
// Would need to use events or provide specific token IDs
|
|
4269
|
+
console.warn('ERC-1155 collection retrieval requires specific token IDs or event analysis');
|
|
4270
|
+
return {
|
|
4271
|
+
accountAddress: params.accountAddress,
|
|
4272
|
+
contractAddress: params.contractAddress,
|
|
4273
|
+
totalBalance: 0,
|
|
4274
|
+
tokensRetrieved: 0,
|
|
4275
|
+
tokens: tokens,
|
|
4276
|
+
note: 'ERC-1155 collection retrieval requires specific token IDs. Use getTokenWithMetadata() for individual tokens.'
|
|
4277
|
+
};
|
|
4278
|
+
}
|
|
4279
|
+
// Handle different token standards
|
|
4280
|
+
if (contractAnalysis.isERC721) {
|
|
4281
|
+
// ERC-721: Get user's total balance and enumerate through tokens
|
|
4282
|
+
const userBalance = await this.web3Api.getTokenBalance({
|
|
4283
|
+
accountAddress: params.accountAddress,
|
|
4284
|
+
contractAddress: params.contractAddress,
|
|
4285
|
+
abi: params.abi,
|
|
4286
|
+
tokenId: null, // null for ERC-721 total balance
|
|
4287
|
+
chainId: params.chainId
|
|
4261
4288
|
});
|
|
4262
|
-
|
|
4289
|
+
console.log(`ERC-721 User balance for ${params.accountAddress}:`, userBalance);
|
|
4290
|
+
if (userBalance === 0) {
|
|
4263
4291
|
return {
|
|
4264
|
-
|
|
4265
|
-
|
|
4266
|
-
|
|
4267
|
-
|
|
4292
|
+
accountAddress: params.accountAddress,
|
|
4293
|
+
contractAddress: params.contractAddress,
|
|
4294
|
+
totalBalance: 0,
|
|
4295
|
+
tokensRetrieved: 0,
|
|
4296
|
+
tokens: []
|
|
4268
4297
|
};
|
|
4269
4298
|
}
|
|
4270
|
-
|
|
4271
|
-
const
|
|
4299
|
+
// Enumerate through user's tokens
|
|
4300
|
+
const maxTokens = params.maxTokens || userBalance;
|
|
4301
|
+
const tokensToRetrieve = Math.min(maxTokens, userBalance);
|
|
4302
|
+
for (let i = 0; i < tokensToRetrieve; i++) {
|
|
4272
4303
|
try {
|
|
4273
4304
|
const tokenId = await this.web3Api.getTokenOfOwnerByIndex({
|
|
4274
|
-
contractAddress:
|
|
4275
|
-
abi:
|
|
4276
|
-
accountAddress:
|
|
4277
|
-
tokenIndex:
|
|
4278
|
-
chainId:
|
|
4305
|
+
contractAddress: params.contractAddress,
|
|
4306
|
+
abi: params.abi,
|
|
4307
|
+
accountAddress: params.accountAddress,
|
|
4308
|
+
tokenIndex: i,
|
|
4309
|
+
chainId: params.chainId
|
|
4279
4310
|
});
|
|
4280
|
-
const
|
|
4281
|
-
|
|
4282
|
-
|
|
4311
|
+
const tokenWithMetadata = await this.getTokenWithMetadata({
|
|
4312
|
+
accountAddress: params.accountAddress,
|
|
4313
|
+
contractAddress: params.contractAddress,
|
|
4314
|
+
abi: params.abi,
|
|
4283
4315
|
tokenId,
|
|
4284
|
-
chainId:
|
|
4316
|
+
chainId: params.chainId
|
|
4285
4317
|
});
|
|
4286
|
-
|
|
4287
|
-
|
|
4288
|
-
|
|
4289
|
-
name: metadata?.name || `Token #${tokenId}`,
|
|
4290
|
-
description: metadata?.description || '',
|
|
4291
|
-
imageUrl: await this.resolveIPFSUrl(metadata?.image || '', token.chainId),
|
|
4292
|
-
rawBalance: 1,
|
|
4293
|
-
formattedBalance: '1',
|
|
4294
|
-
hasBalance: true,
|
|
4295
|
-
metadata,
|
|
4296
|
-
tokenIndex: index
|
|
4297
|
-
};
|
|
4298
|
-
return nftItem;
|
|
4318
|
+
if (tokenWithMetadata.hasBalance) {
|
|
4319
|
+
tokens.push(tokenWithMetadata);
|
|
4320
|
+
}
|
|
4299
4321
|
}
|
|
4300
4322
|
catch (error) {
|
|
4301
|
-
console.warn(`
|
|
4302
|
-
|
|
4323
|
+
console.warn(`Error retrieving ERC-721 token at index ${i}:`, error);
|
|
4324
|
+
continue;
|
|
4303
4325
|
}
|
|
4304
|
-
}
|
|
4305
|
-
// ✅ FIXED: Usar tipo específico NFTItem
|
|
4306
|
-
const successfulNFTs = nftResults
|
|
4307
|
-
.filter((result) => result.status === 'fulfilled' && result.value !== null)
|
|
4308
|
-
.map(result => result.value);
|
|
4309
|
-
return {
|
|
4310
|
-
token,
|
|
4311
|
-
totalNFTs: totalBalance,
|
|
4312
|
-
nfts: successfulNFTs,
|
|
4313
|
-
hasMore: totalBalance > maxNFTs
|
|
4314
|
-
};
|
|
4326
|
+
}
|
|
4315
4327
|
}
|
|
4316
|
-
|
|
4317
|
-
|
|
4328
|
+
else {
|
|
4329
|
+
// Unknown standard
|
|
4318
4330
|
return {
|
|
4319
|
-
|
|
4320
|
-
|
|
4321
|
-
|
|
4322
|
-
|
|
4331
|
+
accountAddress: params.accountAddress,
|
|
4332
|
+
contractAddress: params.contractAddress,
|
|
4333
|
+
totalBalance: 0,
|
|
4334
|
+
tokensRetrieved: 0,
|
|
4335
|
+
tokens: [],
|
|
4336
|
+
note: 'Unsupported token standard for collection retrieval'
|
|
4323
4337
|
};
|
|
4324
4338
|
}
|
|
4325
|
-
|
|
4326
|
-
|
|
4327
|
-
|
|
4328
|
-
|
|
4329
|
-
|
|
4330
|
-
|
|
4331
|
-
|
|
4332
|
-
|
|
4333
|
-
|
|
4334
|
-
|
|
4339
|
+
// Calculate total balance based on retrieved tokens
|
|
4340
|
+
let totalBalance = 0;
|
|
4341
|
+
if (contractAnalysis.isERC721) {
|
|
4342
|
+
// For ERC-721, total balance is the number of unique tokens owned
|
|
4343
|
+
totalBalance = tokens.length;
|
|
4344
|
+
}
|
|
4345
|
+
else {
|
|
4346
|
+
// For other standards, sum up individual token balances
|
|
4347
|
+
totalBalance = tokens.reduce((sum, token) => sum + token.balance, 0);
|
|
4348
|
+
}
|
|
4349
|
+
return {
|
|
4350
|
+
accountAddress: params.accountAddress,
|
|
4351
|
+
contractAddress: params.contractAddress,
|
|
4352
|
+
totalBalance,
|
|
4353
|
+
tokensRetrieved: tokens.length,
|
|
4354
|
+
tokens
|
|
4355
|
+
};
|
|
4356
|
+
}
|
|
4357
|
+
catch (error) {
|
|
4358
|
+
console.error('Error getting token collection:', error);
|
|
4359
|
+
return {
|
|
4360
|
+
accountAddress: params.accountAddress,
|
|
4361
|
+
contractAddress: params.contractAddress,
|
|
4362
|
+
totalBalance: 0,
|
|
4363
|
+
tokensRetrieved: 0,
|
|
4364
|
+
tokens: [],
|
|
4365
|
+
note: 'Error retrieving collection'
|
|
4366
|
+
};
|
|
4367
|
+
}
|
|
4368
|
+
}
|
|
4369
|
+
async getTokenMetadata(params) {
|
|
4370
|
+
try {
|
|
4371
|
+
const tokenUri = await this.web3Api.getTokenUri({
|
|
4372
|
+
contractAddress: params.contractAddress,
|
|
4373
|
+
abi: params.abi,
|
|
4374
|
+
tokenId: params.tokenId,
|
|
4375
|
+
chainId: params.chainId
|
|
4376
|
+
});
|
|
4377
|
+
let metadata = null;
|
|
4378
|
+
if (tokenUri) {
|
|
4379
|
+
metadata = await this.metadataService.fetchAndProcessMetadata(tokenUri, params.chainId);
|
|
4335
4380
|
}
|
|
4381
|
+
return {
|
|
4382
|
+
tokenId: params.tokenId,
|
|
4383
|
+
tokenUri,
|
|
4384
|
+
metadata
|
|
4385
|
+
};
|
|
4386
|
+
}
|
|
4387
|
+
catch (error) {
|
|
4388
|
+
console.error('Error getting token metadata:', error);
|
|
4389
|
+
return {
|
|
4390
|
+
tokenId: params.tokenId,
|
|
4391
|
+
tokenUri: null,
|
|
4392
|
+
metadata: null
|
|
4393
|
+
};
|
|
4394
|
+
}
|
|
4395
|
+
}
|
|
4396
|
+
}
|
|
4397
|
+
|
|
4398
|
+
/**
|
|
4399
|
+
* MetadataDomainService - Clean IPFS metadata resolution
|
|
4400
|
+
*/
|
|
4401
|
+
class MetadataDomainService {
|
|
4402
|
+
constructor(ipfsApi) {
|
|
4403
|
+
this.ipfsApi = ipfsApi;
|
|
4404
|
+
}
|
|
4405
|
+
async fetchAndProcessMetadata(tokenUri, chainId) {
|
|
4406
|
+
return this.ipfsApi.fetchAndProcessMetadata(tokenUri, chainId);
|
|
4407
|
+
}
|
|
4408
|
+
async resolveIPFSUrl(url, chainId) {
|
|
4409
|
+
return this.ipfsApi.resolveIPFSUrl(url, chainId);
|
|
4410
|
+
}
|
|
4411
|
+
}
|
|
4412
|
+
|
|
4413
|
+
/**
|
|
4414
|
+
* ContractDomainService - Clean contract analysis without external dependencies
|
|
4415
|
+
*/
|
|
4416
|
+
class ContractDomainService {
|
|
4417
|
+
constructor() { }
|
|
4418
|
+
analyzeContract(abi) {
|
|
4419
|
+
const methods = abi.filter(item => item.type === 'function').map(item => item.name);
|
|
4420
|
+
// ERC-721 detection
|
|
4421
|
+
const hasOwnerOf = methods.includes('ownerOf');
|
|
4422
|
+
const hasTokenURI = methods.includes('tokenURI');
|
|
4423
|
+
const hasTransferFrom = methods.includes('transferFrom');
|
|
4424
|
+
const isERC721 = hasOwnerOf && hasTokenURI && hasTransferFrom;
|
|
4425
|
+
// ERC-1155 detection
|
|
4426
|
+
const hasBalanceOfBatch = methods.includes('balanceOfBatch');
|
|
4427
|
+
const hasSafeBatchTransferFrom = methods.includes('safeBatchTransferFrom');
|
|
4428
|
+
const hasURI = methods.includes('uri');
|
|
4429
|
+
const isERC1155 = hasBalanceOfBatch && hasSafeBatchTransferFrom && hasURI;
|
|
4430
|
+
return {
|
|
4431
|
+
hasEnumeration: methods.includes('tokenByIndex') && methods.includes('totalSupply'),
|
|
4432
|
+
hasOwnerOf,
|
|
4433
|
+
hasBalanceOf: methods.includes('balanceOf'),
|
|
4434
|
+
hasTokenURI,
|
|
4435
|
+
hasTransfer: methods.includes('transfer') || methods.includes('transferFrom'),
|
|
4436
|
+
hasApprove: methods.includes('approve'),
|
|
4437
|
+
isERC721,
|
|
4438
|
+
isERC1155
|
|
4336
4439
|
};
|
|
4337
|
-
// ✅ CACHE SET: Store complete collection response
|
|
4338
|
-
this.cache.set(cacheKey, response);
|
|
4339
|
-
return response;
|
|
4340
4440
|
}
|
|
4341
|
-
|
|
4342
|
-
|
|
4343
|
-
|
|
4344
|
-
|
|
4345
|
-
const
|
|
4346
|
-
return
|
|
4347
|
-
|
|
4348
|
-
|
|
4441
|
+
supportsEnumeration(abi) {
|
|
4442
|
+
return this.analyzeContract(abi).hasEnumeration;
|
|
4443
|
+
}
|
|
4444
|
+
supportsMethod(abi, methodName) {
|
|
4445
|
+
const methods = abi.filter(item => item.type === 'function').map(item => item.name);
|
|
4446
|
+
return methods.includes(methodName);
|
|
4447
|
+
}
|
|
4448
|
+
}
|
|
4449
|
+
|
|
4450
|
+
/**
|
|
4451
|
+
* Web3ApplicationService - Application layer entrance point
|
|
4452
|
+
* Orchestrates domain services and provides clean public interface
|
|
4453
|
+
* Simplified architecture with concrete classes
|
|
4454
|
+
*/
|
|
4455
|
+
class Web3ApplicationService {
|
|
4456
|
+
constructor(web3Api, ipfsApi) {
|
|
4457
|
+
// Type-safe metadata conversion methods for ERC-721/ERC-1155 standards
|
|
4458
|
+
this.metadataMapper = {
|
|
4459
|
+
fromERCStandard: (ercMetadata) => ({
|
|
4460
|
+
name: ercMetadata.name || '',
|
|
4461
|
+
description: ercMetadata.description || '',
|
|
4462
|
+
imageUrl: ercMetadata.image || '',
|
|
4463
|
+
externalUrl: ercMetadata.external_url,
|
|
4464
|
+
animationUrl: ercMetadata.animation_url,
|
|
4465
|
+
animationUrlConverted: undefined, // Will be set by IPFS conversion
|
|
4466
|
+
attributes: ercMetadata.attributes || [],
|
|
4467
|
+
...ercMetadata
|
|
4468
|
+
}),
|
|
4469
|
+
toERCStandard: (metadata) => ({
|
|
4470
|
+
name: metadata.name,
|
|
4471
|
+
description: metadata.description,
|
|
4472
|
+
image: metadata.imageUrl,
|
|
4473
|
+
animation_url: metadata.animationUrl,
|
|
4474
|
+
external_url: metadata.externalUrl,
|
|
4475
|
+
attributes: metadata.attributes,
|
|
4476
|
+
...Object.fromEntries(Object.entries(metadata).filter(([key]) => !['name', 'description', 'imageUrl', 'animationUrl', 'externalUrl', 'attributes', 'animationUrlConverted'].includes(key)))
|
|
4477
|
+
})
|
|
4478
|
+
};
|
|
4479
|
+
// Create domain services with injected infrastructure dependencies
|
|
4480
|
+
this.contractDomainService = new ContractDomainService();
|
|
4481
|
+
this.metadataDomainService = new MetadataDomainService(ipfsApi);
|
|
4482
|
+
this.tokenDomainService = new TokenDomainService(web3Api, this.metadataDomainService, this.contractDomainService);
|
|
4483
|
+
}
|
|
4484
|
+
/**
|
|
4485
|
+
* Get balance and metadata for a specific token
|
|
4486
|
+
*/
|
|
4487
|
+
async getSpecificTokenBalance(request) {
|
|
4488
|
+
if (!request.tokenId) {
|
|
4489
|
+
return this.tokenDomainService.getTokenBalance({
|
|
4490
|
+
accountAddress: request.accountAddress || '',
|
|
4491
|
+
contractAddress: request.contractAddress,
|
|
4492
|
+
abi: request.abi,
|
|
4493
|
+
tokenId: '',
|
|
4494
|
+
chainId: request.chainId
|
|
4495
|
+
});
|
|
4496
|
+
}
|
|
4497
|
+
return this.tokenDomainService.getTokenWithMetadata({
|
|
4498
|
+
accountAddress: request.accountAddress || '',
|
|
4499
|
+
contractAddress: request.contractAddress,
|
|
4500
|
+
abi: request.abi,
|
|
4501
|
+
tokenId: request.tokenId || '',
|
|
4502
|
+
chainId: request.chainId
|
|
4503
|
+
});
|
|
4504
|
+
}
|
|
4505
|
+
/**
|
|
4506
|
+
* Get metadata for a specific token from on-chain
|
|
4507
|
+
*/
|
|
4508
|
+
async getTokenMetadata(request) {
|
|
4509
|
+
const domainResult = await this.tokenDomainService.getTokenMetadata({
|
|
4510
|
+
contractAddress: request.contractAddress,
|
|
4511
|
+
abi: request.abi,
|
|
4512
|
+
tokenId: request.tokenId || '',
|
|
4513
|
+
chainId: request.chainId
|
|
4514
|
+
});
|
|
4515
|
+
return domainResult.metadata;
|
|
4516
|
+
}
|
|
4517
|
+
/**
|
|
4518
|
+
* Retrieve entire collection of tokens with balance and metadata
|
|
4519
|
+
*/
|
|
4520
|
+
async getTokenCollection(request) {
|
|
4521
|
+
return this.tokenDomainService.getTokenCollection({
|
|
4522
|
+
accountAddress: request.accountAddress || '',
|
|
4523
|
+
contractAddress: request.contractAddress,
|
|
4524
|
+
abi: request.abi,
|
|
4525
|
+
chainId: request.chainId,
|
|
4526
|
+
maxTokens: request.maxTokens,
|
|
4527
|
+
tokenIds: request.tokenIds
|
|
4349
4528
|
});
|
|
4350
4529
|
}
|
|
4351
|
-
|
|
4352
|
-
|
|
4353
|
-
|
|
4530
|
+
/**
|
|
4531
|
+
* Resolve IPFS URLs to HTTPS if needed
|
|
4532
|
+
*/
|
|
4533
|
+
async resolveIPFSUrl(url, chainId) {
|
|
4534
|
+
return this.metadataDomainService.resolveIPFSUrl(url, chainId);
|
|
4535
|
+
}
|
|
4536
|
+
/**
|
|
4537
|
+
* Fetch and process metadata from URI with IPFS conversion
|
|
4538
|
+
*/
|
|
4539
|
+
async fetchAndProcessMetadata(tokenUri, chainId) {
|
|
4540
|
+
const domainMetadata = await this.metadataDomainService.fetchAndProcessMetadata(tokenUri, chainId);
|
|
4541
|
+
if (!domainMetadata)
|
|
4354
4542
|
return null;
|
|
4355
|
-
|
|
4543
|
+
// Convert from ERC token standard to our clean interface
|
|
4544
|
+
const cleanMetadata = this.metadataMapper.fromERCStandard(domainMetadata);
|
|
4545
|
+
// Add IPFS conversion if needed
|
|
4546
|
+
if (cleanMetadata.animationUrl?.startsWith('ipfs://')) {
|
|
4547
|
+
return {
|
|
4548
|
+
...cleanMetadata,
|
|
4549
|
+
animationUrlConverted: await this.resolveIPFSUrl(cleanMetadata.animationUrl, chainId)
|
|
4550
|
+
};
|
|
4551
|
+
}
|
|
4552
|
+
return cleanMetadata;
|
|
4356
4553
|
}
|
|
4357
|
-
|
|
4554
|
+
}
|
|
4555
|
+
|
|
4556
|
+
/**
|
|
4557
|
+
* Web3InfrastructureApi - Infrastructure implementation for blockchain operations
|
|
4558
|
+
* Uses @explorins/web3-ts for Web3 interactions
|
|
4559
|
+
*/
|
|
4560
|
+
class Web3InfrastructureApi {
|
|
4561
|
+
constructor(web3ChainService) {
|
|
4562
|
+
this.web3ChainService = web3ChainService;
|
|
4563
|
+
}
|
|
4564
|
+
async getTokenBalance(request) {
|
|
4358
4565
|
try {
|
|
4359
|
-
|
|
4360
|
-
|
|
4361
|
-
|
|
4362
|
-
|
|
4363
|
-
|
|
4364
|
-
return await response.json();
|
|
4566
|
+
if (request.tokenId !== null)
|
|
4567
|
+
request.tokenId = request.tokenId.toString();
|
|
4568
|
+
const web3 = await this.web3ChainService.getWeb3ByChainId(request.chainId);
|
|
4569
|
+
const contract = web3Ts.getSmartContractInstance(request.contractAddress, request.abi, web3);
|
|
4570
|
+
return await web3Ts.getAccountTokenBalance(contract, request.accountAddress, request.tokenId);
|
|
4365
4571
|
}
|
|
4366
4572
|
catch (error) {
|
|
4367
|
-
console.
|
|
4368
|
-
return
|
|
4573
|
+
console.error(`Failed to get token balance for ${request.accountAddress} for ${request.contractAddress} and tokenId ${request.tokenId}, return 0 instead:`, error);
|
|
4574
|
+
return 0;
|
|
4369
4575
|
}
|
|
4370
4576
|
}
|
|
4577
|
+
async getTokenUri(request) {
|
|
4578
|
+
try {
|
|
4579
|
+
const web3 = await this.web3ChainService.getWeb3ByChainId(request.chainId);
|
|
4580
|
+
const contract = web3Ts.getSmartContractInstance(request.contractAddress, request.abi, web3);
|
|
4581
|
+
const tokenId = Number(request.tokenId);
|
|
4582
|
+
const tokenUri = await web3Ts.getTokenUri(contract, tokenId);
|
|
4583
|
+
return String(tokenUri);
|
|
4584
|
+
}
|
|
4585
|
+
catch (error) {
|
|
4586
|
+
console.error(`Failed to get token URI for tokenId ${request.tokenId}:`, error);
|
|
4587
|
+
throw error;
|
|
4588
|
+
}
|
|
4589
|
+
}
|
|
4590
|
+
async getTokenOfOwnerByIndex(request) {
|
|
4591
|
+
try {
|
|
4592
|
+
const web3 = await this.web3ChainService.getWeb3ByChainId(request.chainId);
|
|
4593
|
+
const tokenContract = web3Ts.getSmartContractInstance(request.contractAddress, request.abi, web3);
|
|
4594
|
+
const tokenId = await web3Ts.getTokenOfOwnerByIndex(tokenContract, request.accountAddress, request.tokenIndex);
|
|
4595
|
+
return String(tokenId);
|
|
4596
|
+
}
|
|
4597
|
+
catch (error) {
|
|
4598
|
+
console.error(`Failed to get token by index ${request.tokenIndex} for ${request.accountAddress}:`, error);
|
|
4599
|
+
throw error;
|
|
4600
|
+
}
|
|
4601
|
+
}
|
|
4602
|
+
}
|
|
4603
|
+
|
|
4604
|
+
/**
|
|
4605
|
+
* IPFSInfrastructureApi - Infrastructure implementation for IPFS operations
|
|
4606
|
+
* Uses Web3ChainService for IPFS gateway resolution
|
|
4607
|
+
*/
|
|
4608
|
+
class IPFSInfrastructureApi {
|
|
4609
|
+
constructor(web3ChainService) {
|
|
4610
|
+
this.web3ChainService = web3ChainService;
|
|
4611
|
+
this.defaultIpfsGatewayDomain = 'pers.mypinata.cloud';
|
|
4612
|
+
}
|
|
4371
4613
|
async getIpfsGatewayDomain(chainId) {
|
|
4372
4614
|
try {
|
|
4373
4615
|
const chainData = await this.web3ChainService.getChainDataWithCache(chainId);
|
|
@@ -4379,29 +4621,66 @@ class Web3Service {
|
|
|
4379
4621
|
}
|
|
4380
4622
|
}
|
|
4381
4623
|
async resolveIPFSUrl(url, chainId) {
|
|
4382
|
-
if (!url)
|
|
4383
|
-
return '';
|
|
4384
4624
|
if (url.startsWith('ipfs://')) {
|
|
4385
|
-
const
|
|
4386
|
-
return `https://${
|
|
4625
|
+
const gateway = await this.getIpfsGatewayDomain(chainId);
|
|
4626
|
+
return url.replace('ipfs://', `https://${gateway}/ipfs/`);
|
|
4387
4627
|
}
|
|
4388
4628
|
return url;
|
|
4389
4629
|
}
|
|
4630
|
+
async fetchAndProcessMetadata(tokenUri, chainId) {
|
|
4631
|
+
try {
|
|
4632
|
+
const resolvedUri = await this.resolveIPFSUrl(tokenUri, chainId);
|
|
4633
|
+
const response = await fetch(resolvedUri);
|
|
4634
|
+
if (!response.ok) {
|
|
4635
|
+
throw new Error(`HTTP error! status: ${response.status}`);
|
|
4636
|
+
}
|
|
4637
|
+
const metadata = await response.json();
|
|
4638
|
+
// Process and return clean metadata
|
|
4639
|
+
return {
|
|
4640
|
+
name: metadata.name || '',
|
|
4641
|
+
description: metadata.description || '',
|
|
4642
|
+
image: metadata.image ? await this.resolveIPFSUrl(metadata.image, chainId) : '',
|
|
4643
|
+
attributes: metadata.attributes || [],
|
|
4644
|
+
animation_url: metadata.animation_url ? await this.resolveIPFSUrl(metadata.animation_url, chainId) : undefined,
|
|
4645
|
+
external_url: metadata.external_url || undefined
|
|
4646
|
+
};
|
|
4647
|
+
}
|
|
4648
|
+
catch (error) {
|
|
4649
|
+
console.error('Error fetching metadata:', error);
|
|
4650
|
+
return null;
|
|
4651
|
+
}
|
|
4652
|
+
}
|
|
4653
|
+
async fetchFromUrl(url) {
|
|
4654
|
+
try {
|
|
4655
|
+
const response = await fetch(url);
|
|
4656
|
+
if (!response.ok) {
|
|
4657
|
+
throw new Error(`Failed to fetch from ${url}: ${response.statusText}`);
|
|
4658
|
+
}
|
|
4659
|
+
return await response.json();
|
|
4660
|
+
}
|
|
4661
|
+
catch (error) {
|
|
4662
|
+
console.error(`Error fetching from URL ${url}:`, error);
|
|
4663
|
+
throw error;
|
|
4664
|
+
}
|
|
4665
|
+
}
|
|
4390
4666
|
}
|
|
4391
4667
|
|
|
4392
|
-
//import { PublicHttpProviderService } from '../web3-chain/services/public-http-provider.service';
|
|
4393
4668
|
function createWeb3SDK(apiClient) {
|
|
4394
4669
|
// TODO: FIX LATER - TEMPORARY CONSTRUCTION
|
|
4395
4670
|
const web3ProviderService = new Web3ProviderService();
|
|
4396
4671
|
const web3ChainSDK = createWeb3ChainSDK(apiClient, web3ProviderService);
|
|
4397
|
-
|
|
4398
|
-
const
|
|
4672
|
+
// Create Web3ApplicationService - main entry point for all Web3 operations
|
|
4673
|
+
const web3InfrastructureApi = new Web3InfrastructureApi(web3ChainSDK.service);
|
|
4674
|
+
const ipfsInfrastructureApi = new IPFSInfrastructureApi(web3ChainSDK.service);
|
|
4675
|
+
const web3ApplicationService = new Web3ApplicationService(web3InfrastructureApi, ipfsInfrastructureApi);
|
|
4676
|
+
// Clean SDK - all functions route through Web3ApplicationService
|
|
4399
4677
|
return {
|
|
4400
|
-
|
|
4401
|
-
|
|
4402
|
-
|
|
4403
|
-
|
|
4404
|
-
|
|
4678
|
+
getTokenBalance: (request) => web3ApplicationService.getSpecificTokenBalance(request),
|
|
4679
|
+
getTokenMetadata: (request) => web3ApplicationService.getTokenMetadata(request),
|
|
4680
|
+
getTokenCollection: (request) => web3ApplicationService.getTokenCollection(request),
|
|
4681
|
+
resolveIPFSUrl: (url, chainId) => web3ApplicationService.resolveIPFSUrl(url, chainId),
|
|
4682
|
+
fetchAndProcessMetadata: (tokenUri, chainId) => web3ApplicationService.fetchAndProcessMetadata(tokenUri, chainId),
|
|
4683
|
+
applicationService: web3ApplicationService
|
|
4405
4684
|
};
|
|
4406
4685
|
}
|
|
4407
4686
|
|
|
@@ -4418,6 +4697,7 @@ exports.ChainTypes = ChainTypes;
|
|
|
4418
4697
|
exports.DEFAULT_PERS_CONFIG = DEFAULT_PERS_CONFIG;
|
|
4419
4698
|
exports.DonationApi = DonationApi;
|
|
4420
4699
|
exports.DonationService = DonationService;
|
|
4700
|
+
exports.IPFSInfrastructureApi = IPFSInfrastructureApi;
|
|
4421
4701
|
exports.PaymentApi = PurchaseApi;
|
|
4422
4702
|
exports.PaymentService = PaymentService;
|
|
4423
4703
|
exports.PersApiClient = PersApiClient;
|
|
@@ -4425,7 +4705,6 @@ exports.PersApiError = PersApiError;
|
|
|
4425
4705
|
exports.PersSDK = PersSDK;
|
|
4426
4706
|
exports.RedemptionApi = RedemptionApi;
|
|
4427
4707
|
exports.RedemptionService = RedemptionService;
|
|
4428
|
-
exports.SimpleCache = SimpleCache;
|
|
4429
4708
|
exports.TenantApi = TenantApi;
|
|
4430
4709
|
exports.TenantService = TenantService;
|
|
4431
4710
|
exports.TokenApi = TokenApi;
|
|
@@ -4437,8 +4716,10 @@ exports.UserApi = UserApi;
|
|
|
4437
4716
|
exports.UserService = UserService;
|
|
4438
4717
|
exports.UserStatusApi = UserStatusApi;
|
|
4439
4718
|
exports.UserStatusService = UserStatusService;
|
|
4719
|
+
exports.Web3ApplicationService = Web3ApplicationService;
|
|
4440
4720
|
exports.Web3ChainApi = Web3ChainApi;
|
|
4441
4721
|
exports.Web3ChainService = Web3ChainService;
|
|
4722
|
+
exports.Web3InfrastructureApi = Web3InfrastructureApi;
|
|
4442
4723
|
exports.Web3ProviderService = Web3ProviderService;
|
|
4443
4724
|
exports.buildApiRoot = buildApiRoot;
|
|
4444
4725
|
exports.createAnalyticsSDK = createAnalyticsSDK;
|