@explorins/pers-sdk 1.2.3 → 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 -3
- package/dist/auth-admin.cjs.map +1 -1
- package/dist/auth-admin.js +7 -3
- 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 -439
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +718 -440
- 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/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
|
-
|
|
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
|
-
|
|
870
|
-
const
|
|
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
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
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 {
|
|
1051
|
+
export { IPFSInfrastructureApi, Web3ApplicationService, Web3InfrastructureApi, createWeb3SDK };
|
|
881
1052
|
//# sourceMappingURL=web3.js.map
|