@explorins/pers-sdk 1.2.2 → 1.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/dist/auth-admin/api/auth-admin-api.d.ts +3 -2
  2. package/dist/auth-admin/api/auth-admin-api.d.ts.map +1 -1
  3. package/dist/auth-admin.cjs +7 -2
  4. package/dist/auth-admin.cjs.map +1 -1
  5. package/dist/auth-admin.js +7 -2
  6. package/dist/auth-admin.js.map +1 -1
  7. package/dist/business/api/business-api.d.ts +17 -32
  8. package/dist/business/api/business-api.d.ts.map +1 -1
  9. package/dist/business.cjs +26 -50
  10. package/dist/business.cjs.map +1 -1
  11. package/dist/business.js +26 -50
  12. package/dist/business.js.map +1 -1
  13. package/dist/campaign/api/campaign-api.d.ts +47 -30
  14. package/dist/campaign/api/campaign-api.d.ts.map +1 -1
  15. package/dist/campaign/index.d.ts +5 -5
  16. package/dist/campaign/services/campaign-service.d.ts +6 -6
  17. package/dist/campaign/services/campaign-service.d.ts.map +1 -1
  18. package/dist/campaign.cjs +62 -41
  19. package/dist/campaign.cjs.map +1 -1
  20. package/dist/campaign.js +62 -41
  21. package/dist/campaign.js.map +1 -1
  22. package/dist/index.cjs +719 -438
  23. package/dist/index.cjs.map +1 -1
  24. package/dist/index.js +718 -439
  25. package/dist/index.js.map +1 -1
  26. package/dist/package.json +1 -1
  27. package/dist/redemption/api/redemption-api.d.ts +58 -14
  28. package/dist/redemption/api/redemption-api.d.ts.map +1 -1
  29. package/dist/redemption/index.d.ts +2 -2
  30. package/dist/redemption/models/index.d.ts +1 -1
  31. package/dist/redemption/models/index.d.ts.map +1 -1
  32. package/dist/redemption/services/redemption-service.d.ts +3 -3
  33. package/dist/redemption/services/redemption-service.d.ts.map +1 -1
  34. package/dist/redemption.cjs +89 -15
  35. package/dist/redemption.cjs.map +1 -1
  36. package/dist/redemption.js +89 -15
  37. package/dist/redemption.js.map +1 -1
  38. package/dist/shared/interfaces/pers-shared-lib.interfaces.d.ts +3 -3
  39. package/dist/shared/interfaces/pers-shared-lib.interfaces.d.ts.map +1 -1
  40. package/dist/transaction/api/transaction-api.d.ts +23 -19
  41. package/dist/transaction/api/transaction-api.d.ts.map +1 -1
  42. package/dist/transaction/index.d.ts +3 -7
  43. package/dist/transaction/index.d.ts.map +1 -1
  44. package/dist/transaction/models/index.d.ts +0 -1
  45. package/dist/transaction/models/index.d.ts.map +1 -1
  46. package/dist/transaction/services/transaction-service.d.ts +5 -7
  47. package/dist/transaction/services/transaction-service.d.ts.map +1 -1
  48. package/dist/transaction.cjs +85 -50
  49. package/dist/transaction.cjs.map +1 -1
  50. package/dist/transaction.js +85 -50
  51. package/dist/transaction.js.map +1 -1
  52. package/dist/web3/application/index.d.ts +6 -0
  53. package/dist/web3/application/index.d.ts.map +1 -0
  54. package/dist/web3/application/web3-application.service.d.ts +53 -0
  55. package/dist/web3/application/web3-application.service.d.ts.map +1 -0
  56. package/dist/web3/domain/models/index.d.ts +58 -0
  57. package/dist/web3/domain/models/index.d.ts.map +1 -0
  58. package/dist/web3/domain/services/contract-domain.service.d.ts +20 -0
  59. package/dist/web3/domain/services/contract-domain.service.d.ts.map +1 -0
  60. package/dist/web3/domain/services/index.d.ts +8 -0
  61. package/dist/web3/domain/services/index.d.ts.map +1 -0
  62. package/dist/web3/domain/services/metadata-domain.service.d.ts +12 -0
  63. package/dist/web3/domain/services/metadata-domain.service.d.ts.map +1 -0
  64. package/dist/web3/domain/services/token-domain.service.d.ts +48 -0
  65. package/dist/web3/domain/services/token-domain.service.d.ts.map +1 -0
  66. package/dist/web3/index.d.ts +10 -11
  67. package/dist/web3/index.d.ts.map +1 -1
  68. package/dist/web3/infrastructure/api/index.d.ts +6 -0
  69. package/dist/web3/infrastructure/api/index.d.ts.map +1 -0
  70. package/dist/web3/infrastructure/api/ipfs-api.d.ts +15 -0
  71. package/dist/web3/infrastructure/api/ipfs-api.d.ts.map +1 -0
  72. package/dist/web3/{api → infrastructure/api}/web3-api.d.ts +6 -2
  73. package/dist/web3/infrastructure/api/web3-api.d.ts.map +1 -0
  74. package/dist/web3/infrastructure/index.d.ts +2 -0
  75. package/dist/web3/infrastructure/index.d.ts.map +1 -0
  76. package/dist/web3.cjs +509 -336
  77. package/dist/web3.cjs.map +1 -1
  78. package/dist/web3.js +507 -336
  79. package/dist/web3.js.map +1 -1
  80. package/package.json +1 -1
  81. package/dist/web3/api/web3-api.d.ts.map +0 -1
  82. package/dist/web3/models/index.d.ts +0 -92
  83. package/dist/web3/models/index.d.ts.map +0 -1
  84. package/dist/web3/services/web3-service.d.ts +0 -21
  85. package/dist/web3/services/web3-service.d.ts.map +0 -1
package/dist/web3.js CHANGED
@@ -1,333 +1,7 @@
1
- import { getSmartContractInstance, getAddressTokenBalanceByContract, getTokenUri, getTokenOfOwnerByIndex } from '@explorins/web3-ts';
2
1
  import { jwtDecode } from 'jwt-decode';
3
2
  import Web3 from 'web3';
4
3
  import { FetchRequest, JsonRpcProvider } from 'ethers';
5
-
6
- class Web3Api {
7
- constructor(web3ChainService) {
8
- this.web3ChainService = web3ChainService;
9
- }
10
- async getTokenBalance(request) {
11
- const web3 = await this.web3ChainService.getWeb3ByChainId(request.chainId);
12
- const tokenContract = getSmartContractInstance(request.contractAddress, request.abi, web3);
13
- const balance = await getAddressTokenBalanceByContract(tokenContract, request.accountAddress, request.tokenId);
14
- return Number(balance);
15
- }
16
- async getTokenUri(request) {
17
- const web3 = await this.web3ChainService.getWeb3ByChainId(request.chainId);
18
- // ✅ DIRECT: Use web3-ts functions directly
19
- const tokenContract = getSmartContractInstance(request.contractAddress, request.abi, web3);
20
- const tokenId = Number(request.tokenId);
21
- const tokenUri = await getTokenUri(tokenContract, tokenId);
22
- return String(tokenUri);
23
- }
24
- async getTokenOfOwnerByIndex(request) {
25
- const web3 = await this.web3ChainService.getWeb3ByChainId(request.chainId);
26
- // ✅ DIRECT: Use web3-ts functions directly
27
- const tokenContract = getSmartContractInstance(request.contractAddress, request.abi, web3);
28
- const tokenId = await getTokenOfOwnerByIndex(tokenContract, request.accountAddress, request.tokenIndex);
29
- return String(tokenId);
30
- }
31
- }
32
-
33
- class SimpleCache {
34
- constructor() {
35
- this.storage = {};
36
- this.defaultTTL = 10 * 1000; // 10 seconds
37
- }
38
- set(key, data, ttl) {
39
- this.storage[key] = {
40
- data,
41
- timestamp: Date.now(),
42
- ttl: ttl ?? this.defaultTTL
43
- };
44
- }
45
- get(key) {
46
- const entry = this.storage[key];
47
- if (!entry) {
48
- return null;
49
- }
50
- const now = Date.now();
51
- const isExpired = (now - entry.timestamp) > entry.ttl;
52
- if (isExpired) {
53
- delete this.storage[key];
54
- return null;
55
- }
56
- return entry.data;
57
- }
58
- clear() {
59
- this.storage = {};
60
- }
61
- cleanup() {
62
- const now = Date.now();
63
- Object.keys(this.storage).forEach(key => {
64
- const entry = this.storage[key];
65
- if ((now - entry.timestamp) > entry.ttl) {
66
- delete this.storage[key];
67
- }
68
- });
69
- }
70
- }
71
-
72
- class Web3Service {
73
- constructor(web3Api, web3ChainService) {
74
- this.web3Api = web3Api;
75
- this.web3ChainService = web3ChainService;
76
- //temporary fix, remove when the backend supports custom gateways
77
- this.defaultIpfsGatewayDomain = 'pers.mypinata.cloud';
78
- // ✅ CACHE: Simple 10-second cache instance
79
- this.cache = new SimpleCache();
80
- this.cleanupInterval = null;
81
- this.cleanupInterval = setInterval(() => {
82
- this.cache.cleanup();
83
- }, 30 * 1000);
84
- }
85
- destroy() {
86
- if (this.cleanupInterval) {
87
- clearInterval(this.cleanupInterval);
88
- this.cleanupInterval = null;
89
- }
90
- this.cache.clear();
91
- }
92
- async getERC20Balance(request) {
93
- const cacheKey = `erc20_balance_${request.accountAddress}_${request.token.contractAddress}_${request.token.chainId}`;
94
- // ✅ CACHE CHECK: Try to get from cache first
95
- const cached = this.cache.get(cacheKey);
96
- if (cached) {
97
- console.debug(`💾 [Web3Service] Using cached ERC20 balance for ${request.token.symbol}`);
98
- return cached;
99
- }
100
- console.debug(`🔄 [Web3Service] Fetching fresh ERC20 balance for ${request.token.symbol}`);
101
- const rawBalance = await this.web3Api.getTokenBalance({
102
- accountAddress: request.accountAddress,
103
- contractAddress: request.token.contractAddress,
104
- abi: request.token.abi,
105
- tokenId: null, // Always null for ERC20
106
- chainId: request.token.chainId
107
- });
108
- const decimals = request.token.decimals ?? 18;
109
- const symbol = request.token.symbol ?? 'UNKNOWN';
110
- const response = {
111
- rawBalance,
112
- formattedBalance: this.formatBalance(rawBalance, decimals),
113
- decimals,
114
- symbol,
115
- hasBalance: rawBalance > 0
116
- };
117
- // ✅ CACHE SET: Store result in cache
118
- this.cache.set(cacheKey, response);
119
- return response;
120
- }
121
- async getERC1155Collection(request) {
122
- // ✅ CACHE KEY: Create unique cache key for collection request
123
- const contractAddresses = request.tokens.map(t => t.contractAddress).sort().join(',');
124
- const cacheKey = `erc1155_collection_${request.accountAddress}_${contractAddresses}`;
125
- // ✅ CACHE CHECK: Try to get from cache first
126
- const cached = this.cache.get(cacheKey);
127
- if (cached) {
128
- console.debug(`💾 [Web3Service] Using cached ERC1155 collection for ${request.accountAddress}`);
129
- return cached;
130
- }
131
- console.debug(`🔄 [Web3Service] Fetching fresh ERC1155 collection for ${request.accountAddress}`);
132
- const tokenResults = await Promise.all(request.tokens.map(async (token) => {
133
- // ✅ FIXED: Handle null metadata properly
134
- const tokenIds = token.metadata?.map(m => m.tokenMetadataIncrementalId?.toString()).filter((id) => id !== undefined) ?? [];
135
- // Check balance for each known tokenId
136
- const balanceResults = await Promise.allSettled(tokenIds.map(async (tokenId) => {
137
- try {
138
- const rawBalance = await this.web3Api.getTokenBalance({
139
- accountAddress: request.accountAddress,
140
- contractAddress: token.contractAddress,
141
- abi: token.abi,
142
- tokenId,
143
- chainId: token.chainId
144
- });
145
- const decimals = token.decimals ?? 0; // ERC1155 usually no decimals
146
- return {
147
- tokenId,
148
- balance: rawBalance,
149
- formattedBalance: this.formatBalance(rawBalance, decimals),
150
- hasBalance: rawBalance > 0,
151
- // ✅ FIXED: Convert null to undefined for findMetadata
152
- metadata: this.findMetadata(token.metadata ?? undefined, tokenId)
153
- };
154
- }
155
- catch (error) {
156
- console.warn(`Failed to get balance for token ${token.contractAddress}:${tokenId}`, error);
157
- return null; // Skip failed tokens
158
- }
159
- }));
160
- // Filter successful results with balance > 0
161
- const successfulResults = [];
162
- for (const result of balanceResults) {
163
- if (result.status === 'fulfilled' && result.value !== null && result.value.hasBalance) {
164
- successfulResults.push(result.value);
165
- }
166
- }
167
- return {
168
- token,
169
- results: successfulResults
170
- };
171
- }));
172
- const response = {
173
- accountAddress: request.accountAddress,
174
- tokens: tokenResults.filter(t => t.results.length > 0)
175
- };
176
- // ✅ CACHE SET: Store complete collection result
177
- this.cache.set(cacheKey, response);
178
- return response;
179
- }
180
- async getERC721Collection(request) {
181
- // ✅ CACHE KEY: Create unique cache key for NFT collection
182
- const contractAddresses = request.nftContracts.map(t => t.contractAddress).sort().join(',');
183
- const maxNFTs = request.maxNFTsPerContract || 50;
184
- const cacheKey = `erc721_collection_${request.accountAddress}_${contractAddresses}_${maxNFTs}`;
185
- // ✅ CACHE CHECK: Try to get from cache first
186
- const cached = this.cache.get(cacheKey);
187
- if (cached) {
188
- console.debug(`💾 [Web3Service] Using cached ERC721 collection for ${request.accountAddress}`);
189
- return cached;
190
- }
191
- console.debug(`🔄 [Web3Service] Fetching fresh ERC721 collection for ${request.accountAddress}`);
192
- const startTime = Date.now();
193
- const contractResults = await Promise.all(request.nftContracts.map(async (token) => {
194
- try {
195
- const totalBalance = await this.web3Api.getTokenBalance({
196
- accountAddress: request.accountAddress,
197
- contractAddress: token.contractAddress,
198
- abi: token.abi,
199
- tokenId: null,
200
- chainId: token.chainId
201
- });
202
- if (totalBalance === 0) {
203
- return {
204
- token,
205
- totalNFTs: 0,
206
- nfts: [],
207
- hasMore: false
208
- };
209
- }
210
- const nftsToLoad = Math.min(totalBalance, maxNFTs);
211
- const nftResults = await Promise.allSettled(Array.from({ length: nftsToLoad }, async (_, index) => {
212
- try {
213
- const tokenId = await this.web3Api.getTokenOfOwnerByIndex({
214
- contractAddress: token.contractAddress,
215
- abi: token.abi,
216
- accountAddress: request.accountAddress,
217
- tokenIndex: index,
218
- chainId: token.chainId
219
- });
220
- const tokenUri = await this.web3Api.getTokenUri({
221
- contractAddress: token.contractAddress,
222
- abi: token.abi,
223
- tokenId,
224
- chainId: token.chainId
225
- });
226
- const metadata = await this.fetchMetadata(tokenUri, token.chainId);
227
- const nftItem = {
228
- tokenId,
229
- name: metadata?.name || `Token #${tokenId}`,
230
- description: metadata?.description || '',
231
- imageUrl: await this.resolveIPFSUrl(metadata?.image || '', token.chainId),
232
- rawBalance: 1,
233
- formattedBalance: '1',
234
- hasBalance: true,
235
- metadata,
236
- tokenIndex: index
237
- };
238
- return nftItem;
239
- }
240
- catch (error) {
241
- console.warn(`Failed to load NFT at index ${index} for ${token.symbol}:`, error);
242
- return null;
243
- }
244
- }));
245
- // ✅ FIXED: Usar tipo específico NFTItem
246
- const successfulNFTs = nftResults
247
- .filter((result) => result.status === 'fulfilled' && result.value !== null)
248
- .map(result => result.value);
249
- return {
250
- token,
251
- totalNFTs: totalBalance,
252
- nfts: successfulNFTs,
253
- hasMore: totalBalance > maxNFTs
254
- };
255
- }
256
- catch (error) {
257
- console.error(`Failed to load NFT collection for ${token.symbol}:`, error);
258
- return {
259
- token,
260
- totalNFTs: 0,
261
- nfts: [],
262
- hasMore: false
263
- };
264
- }
265
- }));
266
- const totalNFTs = contractResults.reduce((sum, contract) => sum + contract.nfts.length, 0);
267
- const loadingTime = Date.now() - startTime;
268
- const response = {
269
- accountAddress: request.accountAddress,
270
- contracts: contractResults,
271
- summary: {
272
- totalContracts: request.nftContracts.length,
273
- totalNFTs,
274
- loadingTime
275
- }
276
- };
277
- // ✅ CACHE SET: Store complete collection response
278
- this.cache.set(cacheKey, response);
279
- return response;
280
- }
281
- // ==========================================
282
- // HELPER METHODS
283
- // ==========================================
284
- formatBalance(rawBalance, decimals) {
285
- const balance = rawBalance / Math.pow(10, decimals);
286
- return balance.toLocaleString('en-US', {
287
- minimumFractionDigits: 0,
288
- maximumFractionDigits: decimals > 0 ? 2 : 0
289
- });
290
- }
291
- // ✅ FIXED: Update method signature to handle null properly
292
- findMetadata(metadata, tokenId) {
293
- if (!metadata || tokenId === null)
294
- return null;
295
- return metadata.find(m => m.tokenMetadataIncrementalId?.toString() === tokenId) || null;
296
- }
297
- async fetchMetadata(uri, chainId) {
298
- try {
299
- const httpUrl = await this.resolveIPFSUrl(uri, chainId);
300
- const response = await fetch(httpUrl);
301
- if (!response.ok) {
302
- throw new Error(`HTTP ${response.status}: ${response.statusText}`);
303
- }
304
- return await response.json();
305
- }
306
- catch (error) {
307
- console.warn('Failed to fetch NFT metadata:', error);
308
- return null;
309
- }
310
- }
311
- async getIpfsGatewayDomain(chainId) {
312
- try {
313
- const chainData = await this.web3ChainService.getChainDataWithCache(chainId);
314
- return chainData.ipfsGatewayDomain || this.defaultIpfsGatewayDomain;
315
- }
316
- catch (error) {
317
- console.warn(`Failed to get chain data for chainId ${chainId}, using default IPFS gateway:`, error);
318
- return this.defaultIpfsGatewayDomain;
319
- }
320
- }
321
- async resolveIPFSUrl(url, chainId) {
322
- if (!url)
323
- return '';
324
- if (url.startsWith('ipfs://')) {
325
- const gatewayDomain = await this.getIpfsGatewayDomain(chainId);
326
- return `https://${gatewayDomain}/ipfs/${url.slice(7)}`;
327
- }
328
- return url;
329
- }
330
- }
4
+ import { getSmartContractInstance, getAccountTokenBalance, getTokenUri, getTokenOfOwnerByIndex } from '@explorins/web3-ts';
331
5
 
332
6
  /**
333
7
  * Platform-Agnostic Web3 Chain API Client
@@ -861,21 +535,518 @@ function createWeb3ChainSDK(apiClient, providerService) {
861
535
  };
862
536
  }
863
537
 
864
- //import { PublicHttpProviderService } from '../web3-chain/services/public-http-provider.service';
538
+ /**
539
+ * TokenDomainService - Domain service for token operations
540
+ * Implements business logic for token balance, metadata, and collection operations
541
+ */
542
+ class TokenDomainService {
543
+ constructor(web3Api, metadataService, contractService) {
544
+ this.web3Api = web3Api;
545
+ this.metadataService = metadataService;
546
+ this.contractService = contractService;
547
+ }
548
+ async getTokenBalance(request) {
549
+ const balance = await this.web3Api.getTokenBalance({
550
+ accountAddress: request.accountAddress,
551
+ contractAddress: request.contractAddress,
552
+ abi: request.abi,
553
+ tokenId: request.tokenId,
554
+ chainId: request.chainId
555
+ });
556
+ return {
557
+ tokenId: request.tokenId,
558
+ balance,
559
+ hasBalance: balance > 0,
560
+ metadata: null
561
+ };
562
+ }
563
+ async getTokenWithMetadata(params) {
564
+ try {
565
+ const balance = await this.web3Api.getTokenBalance({
566
+ accountAddress: params.accountAddress,
567
+ contractAddress: params.contractAddress,
568
+ abi: params.abi,
569
+ tokenId: params.tokenId,
570
+ chainId: params.chainId
571
+ });
572
+ let metadata = null;
573
+ if (balance > 0) {
574
+ const tokenUri = await this.web3Api.getTokenUri({
575
+ contractAddress: params.contractAddress,
576
+ abi: params.abi,
577
+ tokenId: params.tokenId,
578
+ chainId: params.chainId
579
+ });
580
+ if (tokenUri) {
581
+ metadata = await this.metadataService.fetchAndProcessMetadata(tokenUri, params.chainId);
582
+ }
583
+ }
584
+ return {
585
+ tokenId: params.tokenId,
586
+ balance,
587
+ hasBalance: balance > 0,
588
+ metadata
589
+ };
590
+ }
591
+ catch (error) {
592
+ console.error('Error getting token with metadata:', error);
593
+ return {
594
+ tokenId: params.tokenId,
595
+ balance: 0,
596
+ hasBalance: false,
597
+ metadata: null
598
+ };
599
+ }
600
+ }
601
+ async getTokenCollection(params) {
602
+ try {
603
+ const contractAnalysis = this.contractService.analyzeContract(params.abi);
604
+ const tokens = [];
605
+ if (!contractAnalysis.hasEnumeration && !contractAnalysis.isERC1155) {
606
+ console.warn('Contract does not support enumeration, cannot retrieve full collection');
607
+ return {
608
+ accountAddress: params.accountAddress,
609
+ contractAddress: params.contractAddress,
610
+ totalBalance: 0,
611
+ tokensRetrieved: 0,
612
+ tokens: [],
613
+ note: 'Contract does not support enumeration'
614
+ };
615
+ }
616
+ else if (contractAnalysis.isERC1155) {
617
+ const tokenIdsToProcess = params.tokenIds || [];
618
+ if (tokenIdsToProcess.length > 0) {
619
+ for (const tokenId of tokenIdsToProcess) {
620
+ const tokenBalance = await this.getTokenWithMetadata({
621
+ accountAddress: params.accountAddress,
622
+ contractAddress: params.contractAddress,
623
+ abi: params.abi,
624
+ tokenId,
625
+ chainId: params.chainId
626
+ });
627
+ tokens.push(tokenBalance);
628
+ }
629
+ }
630
+ console.log('ERC-1155 User balances:', tokens);
631
+ // ERC-1155: Cannot enumerate without knowing token IDs
632
+ // Would need to use events or provide specific token IDs
633
+ console.warn('ERC-1155 collection retrieval requires specific token IDs or event analysis');
634
+ return {
635
+ accountAddress: params.accountAddress,
636
+ contractAddress: params.contractAddress,
637
+ totalBalance: 0,
638
+ tokensRetrieved: 0,
639
+ tokens: tokens,
640
+ note: 'ERC-1155 collection retrieval requires specific token IDs. Use getTokenWithMetadata() for individual tokens.'
641
+ };
642
+ }
643
+ // Handle different token standards
644
+ if (contractAnalysis.isERC721) {
645
+ // ERC-721: Get user's total balance and enumerate through tokens
646
+ const userBalance = await this.web3Api.getTokenBalance({
647
+ accountAddress: params.accountAddress,
648
+ contractAddress: params.contractAddress,
649
+ abi: params.abi,
650
+ tokenId: null, // null for ERC-721 total balance
651
+ chainId: params.chainId
652
+ });
653
+ console.log(`ERC-721 User balance for ${params.accountAddress}:`, userBalance);
654
+ if (userBalance === 0) {
655
+ return {
656
+ accountAddress: params.accountAddress,
657
+ contractAddress: params.contractAddress,
658
+ totalBalance: 0,
659
+ tokensRetrieved: 0,
660
+ tokens: []
661
+ };
662
+ }
663
+ // Enumerate through user's tokens
664
+ const maxTokens = params.maxTokens || userBalance;
665
+ const tokensToRetrieve = Math.min(maxTokens, userBalance);
666
+ for (let i = 0; i < tokensToRetrieve; i++) {
667
+ try {
668
+ const tokenId = await this.web3Api.getTokenOfOwnerByIndex({
669
+ contractAddress: params.contractAddress,
670
+ abi: params.abi,
671
+ accountAddress: params.accountAddress,
672
+ tokenIndex: i,
673
+ chainId: params.chainId
674
+ });
675
+ const tokenWithMetadata = await this.getTokenWithMetadata({
676
+ accountAddress: params.accountAddress,
677
+ contractAddress: params.contractAddress,
678
+ abi: params.abi,
679
+ tokenId,
680
+ chainId: params.chainId
681
+ });
682
+ if (tokenWithMetadata.hasBalance) {
683
+ tokens.push(tokenWithMetadata);
684
+ }
685
+ }
686
+ catch (error) {
687
+ console.warn(`Error retrieving ERC-721 token at index ${i}:`, error);
688
+ continue;
689
+ }
690
+ }
691
+ }
692
+ else {
693
+ // Unknown standard
694
+ return {
695
+ accountAddress: params.accountAddress,
696
+ contractAddress: params.contractAddress,
697
+ totalBalance: 0,
698
+ tokensRetrieved: 0,
699
+ tokens: [],
700
+ note: 'Unsupported token standard for collection retrieval'
701
+ };
702
+ }
703
+ // Calculate total balance based on retrieved tokens
704
+ let totalBalance = 0;
705
+ if (contractAnalysis.isERC721) {
706
+ // For ERC-721, total balance is the number of unique tokens owned
707
+ totalBalance = tokens.length;
708
+ }
709
+ else {
710
+ // For other standards, sum up individual token balances
711
+ totalBalance = tokens.reduce((sum, token) => sum + token.balance, 0);
712
+ }
713
+ return {
714
+ accountAddress: params.accountAddress,
715
+ contractAddress: params.contractAddress,
716
+ totalBalance,
717
+ tokensRetrieved: tokens.length,
718
+ tokens
719
+ };
720
+ }
721
+ catch (error) {
722
+ console.error('Error getting token collection:', error);
723
+ return {
724
+ accountAddress: params.accountAddress,
725
+ contractAddress: params.contractAddress,
726
+ totalBalance: 0,
727
+ tokensRetrieved: 0,
728
+ tokens: [],
729
+ note: 'Error retrieving collection'
730
+ };
731
+ }
732
+ }
733
+ async getTokenMetadata(params) {
734
+ try {
735
+ const tokenUri = await this.web3Api.getTokenUri({
736
+ contractAddress: params.contractAddress,
737
+ abi: params.abi,
738
+ tokenId: params.tokenId,
739
+ chainId: params.chainId
740
+ });
741
+ let metadata = null;
742
+ if (tokenUri) {
743
+ metadata = await this.metadataService.fetchAndProcessMetadata(tokenUri, params.chainId);
744
+ }
745
+ return {
746
+ tokenId: params.tokenId,
747
+ tokenUri,
748
+ metadata
749
+ };
750
+ }
751
+ catch (error) {
752
+ console.error('Error getting token metadata:', error);
753
+ return {
754
+ tokenId: params.tokenId,
755
+ tokenUri: null,
756
+ metadata: null
757
+ };
758
+ }
759
+ }
760
+ }
761
+
762
+ /**
763
+ * MetadataDomainService - Clean IPFS metadata resolution
764
+ */
765
+ class MetadataDomainService {
766
+ constructor(ipfsApi) {
767
+ this.ipfsApi = ipfsApi;
768
+ }
769
+ async fetchAndProcessMetadata(tokenUri, chainId) {
770
+ return this.ipfsApi.fetchAndProcessMetadata(tokenUri, chainId);
771
+ }
772
+ async resolveIPFSUrl(url, chainId) {
773
+ return this.ipfsApi.resolveIPFSUrl(url, chainId);
774
+ }
775
+ }
776
+
777
+ /**
778
+ * ContractDomainService - Clean contract analysis without external dependencies
779
+ */
780
+ class ContractDomainService {
781
+ constructor() { }
782
+ analyzeContract(abi) {
783
+ const methods = abi.filter(item => item.type === 'function').map(item => item.name);
784
+ // ERC-721 detection
785
+ const hasOwnerOf = methods.includes('ownerOf');
786
+ const hasTokenURI = methods.includes('tokenURI');
787
+ const hasTransferFrom = methods.includes('transferFrom');
788
+ const isERC721 = hasOwnerOf && hasTokenURI && hasTransferFrom;
789
+ // ERC-1155 detection
790
+ const hasBalanceOfBatch = methods.includes('balanceOfBatch');
791
+ const hasSafeBatchTransferFrom = methods.includes('safeBatchTransferFrom');
792
+ const hasURI = methods.includes('uri');
793
+ const isERC1155 = hasBalanceOfBatch && hasSafeBatchTransferFrom && hasURI;
794
+ return {
795
+ hasEnumeration: methods.includes('tokenByIndex') && methods.includes('totalSupply'),
796
+ hasOwnerOf,
797
+ hasBalanceOf: methods.includes('balanceOf'),
798
+ hasTokenURI,
799
+ hasTransfer: methods.includes('transfer') || methods.includes('transferFrom'),
800
+ hasApprove: methods.includes('approve'),
801
+ isERC721,
802
+ isERC1155
803
+ };
804
+ }
805
+ supportsEnumeration(abi) {
806
+ return this.analyzeContract(abi).hasEnumeration;
807
+ }
808
+ supportsMethod(abi, methodName) {
809
+ const methods = abi.filter(item => item.type === 'function').map(item => item.name);
810
+ return methods.includes(methodName);
811
+ }
812
+ }
813
+
814
+ /**
815
+ * Web3ApplicationService - Application layer entrance point
816
+ * Orchestrates domain services and provides clean public interface
817
+ * Simplified architecture with concrete classes
818
+ */
819
+ class Web3ApplicationService {
820
+ constructor(web3Api, ipfsApi) {
821
+ // Type-safe metadata conversion methods for ERC-721/ERC-1155 standards
822
+ this.metadataMapper = {
823
+ fromERCStandard: (ercMetadata) => ({
824
+ name: ercMetadata.name || '',
825
+ description: ercMetadata.description || '',
826
+ imageUrl: ercMetadata.image || '',
827
+ externalUrl: ercMetadata.external_url,
828
+ animationUrl: ercMetadata.animation_url,
829
+ animationUrlConverted: undefined, // Will be set by IPFS conversion
830
+ attributes: ercMetadata.attributes || [],
831
+ ...ercMetadata
832
+ }),
833
+ toERCStandard: (metadata) => ({
834
+ name: metadata.name,
835
+ description: metadata.description,
836
+ image: metadata.imageUrl,
837
+ animation_url: metadata.animationUrl,
838
+ external_url: metadata.externalUrl,
839
+ attributes: metadata.attributes,
840
+ ...Object.fromEntries(Object.entries(metadata).filter(([key]) => !['name', 'description', 'imageUrl', 'animationUrl', 'externalUrl', 'attributes', 'animationUrlConverted'].includes(key)))
841
+ })
842
+ };
843
+ // Create domain services with injected infrastructure dependencies
844
+ this.contractDomainService = new ContractDomainService();
845
+ this.metadataDomainService = new MetadataDomainService(ipfsApi);
846
+ this.tokenDomainService = new TokenDomainService(web3Api, this.metadataDomainService, this.contractDomainService);
847
+ }
848
+ /**
849
+ * Get balance and metadata for a specific token
850
+ */
851
+ async getSpecificTokenBalance(request) {
852
+ if (!request.tokenId) {
853
+ return this.tokenDomainService.getTokenBalance({
854
+ accountAddress: request.accountAddress || '',
855
+ contractAddress: request.contractAddress,
856
+ abi: request.abi,
857
+ tokenId: '',
858
+ chainId: request.chainId
859
+ });
860
+ }
861
+ return this.tokenDomainService.getTokenWithMetadata({
862
+ accountAddress: request.accountAddress || '',
863
+ contractAddress: request.contractAddress,
864
+ abi: request.abi,
865
+ tokenId: request.tokenId || '',
866
+ chainId: request.chainId
867
+ });
868
+ }
869
+ /**
870
+ * Get metadata for a specific token from on-chain
871
+ */
872
+ async getTokenMetadata(request) {
873
+ const domainResult = await this.tokenDomainService.getTokenMetadata({
874
+ contractAddress: request.contractAddress,
875
+ abi: request.abi,
876
+ tokenId: request.tokenId || '',
877
+ chainId: request.chainId
878
+ });
879
+ return domainResult.metadata;
880
+ }
881
+ /**
882
+ * Retrieve entire collection of tokens with balance and metadata
883
+ */
884
+ async getTokenCollection(request) {
885
+ return this.tokenDomainService.getTokenCollection({
886
+ accountAddress: request.accountAddress || '',
887
+ contractAddress: request.contractAddress,
888
+ abi: request.abi,
889
+ chainId: request.chainId,
890
+ maxTokens: request.maxTokens,
891
+ tokenIds: request.tokenIds
892
+ });
893
+ }
894
+ /**
895
+ * Resolve IPFS URLs to HTTPS if needed
896
+ */
897
+ async resolveIPFSUrl(url, chainId) {
898
+ return this.metadataDomainService.resolveIPFSUrl(url, chainId);
899
+ }
900
+ /**
901
+ * Fetch and process metadata from URI with IPFS conversion
902
+ */
903
+ async fetchAndProcessMetadata(tokenUri, chainId) {
904
+ const domainMetadata = await this.metadataDomainService.fetchAndProcessMetadata(tokenUri, chainId);
905
+ if (!domainMetadata)
906
+ return null;
907
+ // Convert from ERC token standard to our clean interface
908
+ const cleanMetadata = this.metadataMapper.fromERCStandard(domainMetadata);
909
+ // Add IPFS conversion if needed
910
+ if (cleanMetadata.animationUrl?.startsWith('ipfs://')) {
911
+ return {
912
+ ...cleanMetadata,
913
+ animationUrlConverted: await this.resolveIPFSUrl(cleanMetadata.animationUrl, chainId)
914
+ };
915
+ }
916
+ return cleanMetadata;
917
+ }
918
+ }
919
+
920
+ /**
921
+ * Web3InfrastructureApi - Infrastructure implementation for blockchain operations
922
+ * Uses @explorins/web3-ts for Web3 interactions
923
+ */
924
+ class Web3InfrastructureApi {
925
+ constructor(web3ChainService) {
926
+ this.web3ChainService = web3ChainService;
927
+ }
928
+ async getTokenBalance(request) {
929
+ try {
930
+ if (request.tokenId !== null)
931
+ request.tokenId = request.tokenId.toString();
932
+ const web3 = await this.web3ChainService.getWeb3ByChainId(request.chainId);
933
+ const contract = getSmartContractInstance(request.contractAddress, request.abi, web3);
934
+ return await getAccountTokenBalance(contract, request.accountAddress, request.tokenId);
935
+ }
936
+ catch (error) {
937
+ console.error(`Failed to get token balance for ${request.accountAddress} for ${request.contractAddress} and tokenId ${request.tokenId}, return 0 instead:`, error);
938
+ return 0;
939
+ }
940
+ }
941
+ async getTokenUri(request) {
942
+ try {
943
+ const web3 = await this.web3ChainService.getWeb3ByChainId(request.chainId);
944
+ const contract = getSmartContractInstance(request.contractAddress, request.abi, web3);
945
+ const tokenId = Number(request.tokenId);
946
+ const tokenUri = await getTokenUri(contract, tokenId);
947
+ return String(tokenUri);
948
+ }
949
+ catch (error) {
950
+ console.error(`Failed to get token URI for tokenId ${request.tokenId}:`, error);
951
+ throw error;
952
+ }
953
+ }
954
+ async getTokenOfOwnerByIndex(request) {
955
+ try {
956
+ const web3 = await this.web3ChainService.getWeb3ByChainId(request.chainId);
957
+ const tokenContract = getSmartContractInstance(request.contractAddress, request.abi, web3);
958
+ const tokenId = await getTokenOfOwnerByIndex(tokenContract, request.accountAddress, request.tokenIndex);
959
+ return String(tokenId);
960
+ }
961
+ catch (error) {
962
+ console.error(`Failed to get token by index ${request.tokenIndex} for ${request.accountAddress}:`, error);
963
+ throw error;
964
+ }
965
+ }
966
+ }
967
+
968
+ /**
969
+ * IPFSInfrastructureApi - Infrastructure implementation for IPFS operations
970
+ * Uses Web3ChainService for IPFS gateway resolution
971
+ */
972
+ class IPFSInfrastructureApi {
973
+ constructor(web3ChainService) {
974
+ this.web3ChainService = web3ChainService;
975
+ this.defaultIpfsGatewayDomain = 'pers.mypinata.cloud';
976
+ }
977
+ async getIpfsGatewayDomain(chainId) {
978
+ try {
979
+ const chainData = await this.web3ChainService.getChainDataWithCache(chainId);
980
+ return chainData.ipfsGatewayDomain || this.defaultIpfsGatewayDomain;
981
+ }
982
+ catch (error) {
983
+ console.warn(`Failed to get chain data for chainId ${chainId}, using default IPFS gateway:`, error);
984
+ return this.defaultIpfsGatewayDomain;
985
+ }
986
+ }
987
+ async resolveIPFSUrl(url, chainId) {
988
+ if (url.startsWith('ipfs://')) {
989
+ const gateway = await this.getIpfsGatewayDomain(chainId);
990
+ return url.replace('ipfs://', `https://${gateway}/ipfs/`);
991
+ }
992
+ return url;
993
+ }
994
+ async fetchAndProcessMetadata(tokenUri, chainId) {
995
+ try {
996
+ const resolvedUri = await this.resolveIPFSUrl(tokenUri, chainId);
997
+ const response = await fetch(resolvedUri);
998
+ if (!response.ok) {
999
+ throw new Error(`HTTP error! status: ${response.status}`);
1000
+ }
1001
+ const metadata = await response.json();
1002
+ // Process and return clean metadata
1003
+ return {
1004
+ name: metadata.name || '',
1005
+ description: metadata.description || '',
1006
+ image: metadata.image ? await this.resolveIPFSUrl(metadata.image, chainId) : '',
1007
+ attributes: metadata.attributes || [],
1008
+ animation_url: metadata.animation_url ? await this.resolveIPFSUrl(metadata.animation_url, chainId) : undefined,
1009
+ external_url: metadata.external_url || undefined
1010
+ };
1011
+ }
1012
+ catch (error) {
1013
+ console.error('Error fetching metadata:', error);
1014
+ return null;
1015
+ }
1016
+ }
1017
+ async fetchFromUrl(url) {
1018
+ try {
1019
+ const response = await fetch(url);
1020
+ if (!response.ok) {
1021
+ throw new Error(`Failed to fetch from ${url}: ${response.statusText}`);
1022
+ }
1023
+ return await response.json();
1024
+ }
1025
+ catch (error) {
1026
+ console.error(`Error fetching from URL ${url}:`, error);
1027
+ throw error;
1028
+ }
1029
+ }
1030
+ }
1031
+
865
1032
  function createWeb3SDK(apiClient) {
866
1033
  // TODO: FIX LATER - TEMPORARY CONSTRUCTION
867
1034
  const web3ProviderService = new Web3ProviderService();
868
1035
  const web3ChainSDK = createWeb3ChainSDK(apiClient, web3ProviderService);
869
- const web3Api = new Web3Api(web3ChainSDK.service);
870
- const web3Service = new Web3Service(web3Api, web3ChainSDK.service);
1036
+ // Create Web3ApplicationService - main entry point for all Web3 operations
1037
+ const web3InfrastructureApi = new Web3InfrastructureApi(web3ChainSDK.service);
1038
+ const ipfsInfrastructureApi = new IPFSInfrastructureApi(web3ChainSDK.service);
1039
+ const web3ApplicationService = new Web3ApplicationService(web3InfrastructureApi, ipfsInfrastructureApi);
1040
+ // Clean SDK - all functions route through Web3ApplicationService
871
1041
  return {
872
- getCreditsBalance: (request) => web3Service.getERC20Balance(request),
873
- getRewardsCollection: (request) => web3Service.getERC1155Collection(request),
874
- getStampsCollection: (request) => web3Service.getERC721Collection(request),
875
- api: web3Api,
876
- service: web3Service
1042
+ getTokenBalance: (request) => web3ApplicationService.getSpecificTokenBalance(request),
1043
+ getTokenMetadata: (request) => web3ApplicationService.getTokenMetadata(request),
1044
+ getTokenCollection: (request) => web3ApplicationService.getTokenCollection(request),
1045
+ resolveIPFSUrl: (url, chainId) => web3ApplicationService.resolveIPFSUrl(url, chainId),
1046
+ fetchAndProcessMetadata: (tokenUri, chainId) => web3ApplicationService.fetchAndProcessMetadata(tokenUri, chainId),
1047
+ applicationService: web3ApplicationService
877
1048
  };
878
1049
  }
879
1050
 
880
- export { SimpleCache, createWeb3SDK };
1051
+ export { IPFSInfrastructureApi, Web3ApplicationService, Web3InfrastructureApi, createWeb3SDK };
881
1052
  //# sourceMappingURL=web3.js.map