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