@pioneer-platform/pioneer-sdk 8.11.10 → 8.11.14

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/package.json CHANGED
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "author": "highlander",
3
3
  "name": "@pioneer-platform/pioneer-sdk",
4
- "version": "8.11.10",
4
+ "version": "8.11.14",
5
5
  "dependencies": {
6
6
  "@keepkey/keepkey-sdk": "^0.2.62",
7
7
  "@pioneer-platform/loggerdog": "^8.11.0",
8
8
  "@pioneer-platform/pioneer-caip": "^9.10.0",
9
9
  "@pioneer-platform/pioneer-client": "^9.10.10",
10
10
  "@pioneer-platform/pioneer-coins": "^9.11.0",
11
- "@pioneer-platform/pioneer-discovery": "^8.11.4",
11
+ "@pioneer-platform/pioneer-discovery": "^8.11.14",
12
12
  "@pioneer-platform/pioneer-events": "^8.11.0",
13
13
  "coinselect": "^3.1.13",
14
14
  "eventemitter3": "^5.0.1",
@@ -8,6 +8,18 @@ export async function getCosmosStakingCharts(params: ChartParams): Promise<Chart
8
8
  const balances: ChartBalance[] = [];
9
9
 
10
10
  try {
11
+ // Fast-path: skip staking in test/e2e or when explicitly requested
12
+ try {
13
+ const fastFlag = typeof process !== 'undefined' && process.env && process.env.PIONEER_FAST === '1';
14
+ const isTestContext = typeof context === 'string' && /test|e2e/i.test(context);
15
+ if (fastFlag || isTestContext) {
16
+ console.log(tag, 'Fast mode detected (test/e2e). Skipping cosmos staking fetch.');
17
+ return balances;
18
+ }
19
+ } catch (_) {
20
+ // ignore
21
+ }
22
+
11
23
  console.log(tag, 'Adding Cosmos staking positions to charts...');
12
24
 
13
25
  // Find cosmos pubkeys that could have staking positions
@@ -27,31 +39,29 @@ export async function getCosmosStakingCharts(params: ChartParams): Promise<Chart
27
39
 
28
40
  console.log(tag, 'Found cosmos pubkeys for staking:', cosmosPubkeys.length);
29
41
 
30
- for (const cosmosPubkey of cosmosPubkeys) {
31
- if (!cosmosPubkey.address) {
32
- continue;
33
- }
42
+ await Promise.allSettled(
43
+ cosmosPubkeys.map(async (cosmosPubkey: any) => {
44
+ if (!cosmosPubkey.address) return;
34
45
 
35
- // Check which cosmos networks this pubkey supports
36
- const cosmosNetworks = cosmosPubkey.networks.filter(
37
- (n: string) => n.includes('cosmos:cosmoshub') || n.includes('cosmos:osmosis'),
38
- );
46
+ const cosmosNetworks = cosmosPubkey.networks.filter(
47
+ (n: string) => n.includes('cosmos:cosmoshub') || n.includes('cosmos:osmosis'),
48
+ );
39
49
 
40
- for (const networkId of cosmosNetworks) {
41
- // Only process if this network is in our blockchains list
42
- if (!blockchains.includes(networkId)) {
43
- continue;
44
- }
45
-
46
- await fetchStakingPositionsForNetwork(
47
- networkId,
48
- cosmosPubkey.address,
49
- context,
50
- pioneer,
51
- balances
50
+ await Promise.allSettled(
51
+ cosmosNetworks
52
+ .filter((networkId: string) => blockchains.includes(networkId))
53
+ .map(async (networkId: string) => {
54
+ await fetchStakingPositionsForNetwork(
55
+ networkId,
56
+ cosmosPubkey.address,
57
+ context,
58
+ pioneer,
59
+ balances
60
+ );
61
+ })
52
62
  );
53
- }
54
- }
63
+ })
64
+ );
55
65
  } catch (e) {
56
66
  console.error(tag, 'Error adding cosmos staking positions:', e);
57
67
  }
package/src/charts/evm.ts CHANGED
@@ -18,31 +18,72 @@ export async function getEvmCharts(params: ChartParams): Promise<ChartBalance[]>
18
18
  console.log(tag, 'Total pubkeys available:', pubkeys.length);
19
19
  console.log(tag, 'Blockchains to process:', blockchains);
20
20
 
21
- // Only call portfolio endpoint if we have an EVM address (Zapper requirement)
22
- if (!primaryAddress) {
23
- console.log(
24
- tag,
25
- 'No EVM address found, skipping portfolio lookup (Zapper only supports Ethereum)',
26
- );
27
- return balances;
21
+ // Build pubkeys array for batch request
22
+ // Map all pubkeys to { pubkey, caip } format for each supported blockchain
23
+ const pubkeysForBatch: { pubkey: string; caip: string }[] = [];
24
+
25
+ for (const pubkey of pubkeys) {
26
+ const address = pubkey.address || pubkey.master || pubkey.pubkey;
27
+ if (!address) continue;
28
+
29
+ // Get networks this pubkey supports
30
+ const supportedNetworks = pubkey.networks || [];
31
+
32
+ // For each blockchain we're processing
33
+ for (const blockchain of blockchains) {
34
+ // Check if this pubkey supports this blockchain
35
+ // Networks can be specific (eip155:1) or wildcard (eip155:*)
36
+ const supportsNetwork = supportedNetworks.some((net: string) =>
37
+ net === blockchain ||
38
+ (net.endsWith(':*') && blockchain.startsWith(net.replace(':*', ':')))
39
+ );
40
+
41
+ if (supportsNetwork) {
42
+ // Build the CAIP for this combination
43
+ // For EVM chains, use slip44:60 for ETH asset
44
+ // For non-EVM chains, we'd need different logic (not implemented yet)
45
+ let caip: string;
46
+ if (blockchain.startsWith('eip155:')) {
47
+ caip = `${blockchain}/slip44:60`;
48
+ } else {
49
+ // For non-EVM chains, use generic format
50
+ // TODO: Implement proper CAIP construction for other chains
51
+ caip = `${blockchain}/slip44:0`;
52
+ }
53
+
54
+ pubkeysForBatch.push({
55
+ pubkey: address,
56
+ caip: caip
57
+ });
58
+ }
59
+ }
28
60
  }
29
61
 
30
- console.log(tag, 'Using EVM address for portfolio:', primaryAddress);
62
+ console.log(tag, `Built ${pubkeysForBatch.length} pubkey-chain combinations for batch request`);
63
+
64
+ // Only call portfolio endpoint if we have pubkeys to query
65
+ if (pubkeysForBatch.length === 0) {
66
+ console.log(tag, 'No pubkeys to query, skipping portfolio lookup');
67
+ return balances;
68
+ }
31
69
 
32
70
  // REDUNDANCY: Fetch stable coins from dedicated endpoint for ALL EVM networks
33
- // This ensures USDC/USDT balances are always available even if Zapper fails
34
- await fetchStableCoins(pioneer, primaryAddress, blockchains, balances, context);
71
+ // This ensures USDC/USDT balances are always available even if cache is empty
72
+ if (primaryAddress) {
73
+ await fetchStableCoins(pioneer, primaryAddress, blockchains, balances, context);
74
+ }
35
75
 
36
76
  // CUSTOM TOKENS: Fetch user-defined custom tokens from MongoDB-backed endpoint
37
77
  // This ensures user's custom tokens are always included in their portfolio
38
78
  await fetchCustomTokens({ blockchains, pioneer, pubkeys, context }, balances);
39
79
 
40
80
  try {
41
- // NOTE: Zapper API returns portfolio data across ALL networks for a given address
42
- // We use eip155:1 as the networkId parameter, but Zapper will return data for all EVM chains
81
+ // BATCH + NON-BLOCKING: Call portfolio endpoint with ALL pubkeys in one request
82
+ // The endpoint returns immediately from cache, never blocks waiting for blockchain APIs
83
+ console.log(tag, `Calling GetPortfolio with ${pubkeysForBatch.length} pubkeys (batch + non-blocking)`);
84
+
43
85
  let portfolio = await pioneer.GetPortfolio({
44
- networkId: 'eip155:1', // Required by endpoint, but Zapper returns all networks
45
- address: primaryAddress
86
+ pubkeys: pubkeysForBatch
46
87
  });
47
88
 
48
89
  // Handle double-wrapped response from Swagger client
@@ -73,10 +114,10 @@ export async function getEvmCharts(params: ChartParams): Promise<ChartBalance[]>
73
114
  }
74
115
  console.log(tag, `Processed ${processedCount} balances, skipped ${skippedCount}`);
75
116
 
76
- // Process tokens from portfolio (if they exist)
117
+ // Process tokens from portfolio.tokens array (returned by backend with blocking token fetch)
77
118
  if (portfolio.tokens && portfolio.tokens.length > 0) {
78
119
  console.log(tag, 'Processing portfolio.tokens:', portfolio.tokens.length);
79
-
120
+
80
121
  for (const token of portfolio.tokens) {
81
122
  const processedToken = processPortfolioToken(token, primaryAddress, context, blockchains);
82
123
  if (processedToken && !checkDuplicateBalance(balances, processedToken.caip, processedToken.pubkey)) {
@@ -84,6 +125,8 @@ export async function getEvmCharts(params: ChartParams): Promise<ChartBalance[]>
84
125
  }
85
126
  }
86
127
  }
128
+
129
+ console.log(tag, `Total balances (native + tokens): ${balances.length}`);
87
130
  } catch (e) {
88
131
  console.error(tag, 'Error fetching portfolio:', e);
89
132
  }
@@ -243,6 +286,18 @@ async function fetchStableCoins(
243
286
  ): Promise<void> {
244
287
  console.log(tag, 'Fetching stable coins for redundancy...');
245
288
 
289
+ // Fast-path: skip redundancy in test/e2e or when explicitly requested
290
+ try {
291
+ const fastFlag = typeof process !== 'undefined' && process.env && process.env.PIONEER_FAST === '1';
292
+ const isTestContext = typeof context === 'string' && /test|e2e/i.test(context);
293
+ if (fastFlag || isTestContext) {
294
+ console.log(tag, 'Fast mode detected (test/e2e). Skipping stable coin redundancy.');
295
+ return;
296
+ }
297
+ } catch (_) {
298
+ // no-op if process/env not available
299
+ }
300
+
246
301
  // Networks that support stable coins endpoint
247
302
  const supportedNetworks = ['eip155:1', 'eip155:137', 'eip155:8453', 'eip155:56'];
248
303
 
@@ -256,30 +311,42 @@ async function fetchStableCoins(
256
311
 
257
312
  console.log(tag, `Checking stable coins on ${networksToCheck.length} networks`);
258
313
 
259
- // Fetch stable coins for each network
260
- for (const networkId of networksToCheck) {
261
- try {
262
- const response = await pioneer.GetStableCoins({ networkId, address: primaryAddress });
263
- const stableCoins = response?.data?.tokens || [];
264
-
265
- console.log(tag, `Found ${stableCoins.length} stable coins on ${networkId}`);
266
-
267
- // Process each stable coin
268
- for (const token of stableCoins) {
269
- // Convert to ChartBalance format
270
- const chartBalance = processPortfolioToken(token, primaryAddress, context, blockchains);
314
+ // Helper: timeout a promise
315
+ const withTimeout = <T>(p: Promise<T>, ms: number): Promise<T> => {
316
+ return new Promise<T>((resolve, reject) => {
317
+ const t = setTimeout(() => reject(new Error(`timeout ${ms}ms`)), ms);
318
+ p.then((v) => { clearTimeout(t); resolve(v); })
319
+ .catch((e) => { clearTimeout(t); reject(e); });
320
+ });
321
+ };
271
322
 
272
- // Add if not already in balances (avoid duplicates)
273
- if (chartBalance && !checkDuplicateBalance(balances, chartBalance.caip, chartBalance.pubkey)) {
274
- balances.push(chartBalance);
275
- console.log(tag, `Added stable coin: ${chartBalance.symbol} = ${chartBalance.balance}`);
323
+ // Fetch stable coins for each network in parallel with short timeouts
324
+ const results = await Promise.allSettled(
325
+ networksToCheck.map(async (networkId) => {
326
+ try {
327
+ const response = await withTimeout(
328
+ pioneer.GetStableCoins({ networkId, address: primaryAddress }),
329
+ 2000
330
+ );
331
+ const stableCoins = response?.data?.tokens || [];
332
+ console.log(tag, `Found ${stableCoins.length} stable coins on ${networkId}`);
333
+
334
+ for (const token of stableCoins) {
335
+ const chartBalance = processPortfolioToken(token, primaryAddress, context, blockchains);
336
+ if (chartBalance && !checkDuplicateBalance(balances, chartBalance.caip, chartBalance.pubkey)) {
337
+ balances.push(chartBalance);
338
+ console.log(tag, `Added stable coin: ${chartBalance.symbol} = ${chartBalance.balance}`);
339
+ }
276
340
  }
341
+ } catch (error: any) {
342
+ console.error(tag, `Error fetching stable coins for ${networkId}:`, error?.message || error);
277
343
  }
278
- } catch (error: any) {
279
- console.error(tag, `Error fetching stable coins for ${networkId}:`, error.message);
280
- // Continue with other networks even if one fails
281
- }
282
- }
344
+ })
345
+ );
346
+
347
+ // Optional: log aggregate failures (for diagnostics only)
348
+ const failures = results.filter(r => r.status === 'rejected').length;
349
+ if (failures > 0) console.log(tag, `Stable coin fetch had ${failures} failures (non-blocking)`);
283
350
 
284
351
  console.log(tag, `Stable coin redundancy complete. Total balances: ${balances.length}`);
285
352
  }
@@ -45,12 +45,15 @@ export async function getMayaCharts(
45
45
 
46
46
  // Try to get MAYA token balance via a separate call
47
47
  // This is a workaround for the portfolio API not returning MAYA tokens
48
- const mayaBalanceResponse = await pioneer.GetPortfolioBalances([
49
- {
50
- caip: 'cosmos:mayachain-mainnet-v1/denom:maya',
51
- pubkey: mayaPubkey.address,
52
- },
53
- ]);
48
+ // FIX: Wrap in object with pubkeys field to match server API
49
+ const mayaBalanceResponse = await pioneer.GetPortfolioBalances({
50
+ pubkeys: [
51
+ {
52
+ caip: 'cosmos:mayachain-mainnet-v1/denom:maya',
53
+ pubkey: mayaPubkey.address,
54
+ },
55
+ ],
56
+ });
54
57
 
55
58
  console.log(
56
59
  tag,
package/src/index.ts CHANGED
@@ -1678,6 +1678,47 @@ export class SDK {
1678
1678
  if (duplicatesRemoved > 0) {
1679
1679
  }
1680
1680
 
1681
+ // FIX 1: Validate that all pubkeys have networks field
1682
+ const pubkeysWithoutNetworks = this.pubkeys.filter(
1683
+ pk => !pk.networks || !Array.isArray(pk.networks) || pk.networks.length === 0
1684
+ );
1685
+
1686
+ if (pubkeysWithoutNetworks.length > 0) {
1687
+ console.error(tag, 'ERROR: Some pubkeys missing networks field!');
1688
+ console.error(tag, 'Affected pubkeys:', pubkeysWithoutNetworks.length);
1689
+
1690
+ // Log details
1691
+ pubkeysWithoutNetworks.forEach(pk => {
1692
+ console.error(tag, ` - ${pk.note || pk.pubkey}: networks=${pk.networks}`);
1693
+ });
1694
+
1695
+ // Try to fix by matching with paths
1696
+ for (const pubkey of pubkeysWithoutNetworks) {
1697
+ const matchingPath = this.paths.find(p =>
1698
+ JSON.stringify(p.addressNList) === JSON.stringify(pubkey.addressNList)
1699
+ );
1700
+
1701
+ if (matchingPath && matchingPath.networks) {
1702
+ console.warn(tag, ` ⚠️ Auto-fixing: Adding networks from path ${matchingPath.note}`);
1703
+ pubkey.networks = matchingPath.networks;
1704
+ }
1705
+ }
1706
+
1707
+ // Check if auto-fix worked
1708
+ const stillMissing = this.pubkeys.filter(
1709
+ pk => !pk.networks || !Array.isArray(pk.networks) || pk.networks.length === 0
1710
+ );
1711
+
1712
+ if (stillMissing.length > 0) {
1713
+ console.error(tag, `❌ CRITICAL: ${stillMissing.length} pubkeys still missing networks after auto-fix!`);
1714
+ stillMissing.forEach(pk => {
1715
+ console.error(tag, ` - ${pk.note || pk.pubkey}`);
1716
+ });
1717
+ } else {
1718
+ console.log(tag, `✅ Auto-fix successful: All pubkeys now have networks field`);
1719
+ }
1720
+ }
1721
+
1681
1722
  // Emit event to notify that pubkeys have been set
1682
1723
  this.events.emit('SET_PUBKEYS', this.pubkeys);
1683
1724
 
@@ -1701,6 +1742,24 @@ export class SDK {
1701
1742
  throw new Error('Pioneer client not initialized. Call init() first.');
1702
1743
  }
1703
1744
 
1745
+ // DIAGNOSTIC: Log input
1746
+ console.log('🔍 [DIAGNOSTIC] Input networks:', networkIds);
1747
+ console.log('🔍 [DIAGNOSTIC] Total pubkeys:', this.pubkeys.length);
1748
+
1749
+ // DIAGNOSTIC: Check which pubkeys have networks field
1750
+ const pubkeysWithNetworks = this.pubkeys.filter(p => p.networks && Array.isArray(p.networks));
1751
+ const pubkeysWithoutNetworks = this.pubkeys.filter(p => !p.networks || !Array.isArray(p.networks));
1752
+
1753
+ console.log('🔍 [DIAGNOSTIC] Pubkeys WITH networks:', pubkeysWithNetworks.length);
1754
+ console.log('🔍 [DIAGNOSTIC] Pubkeys WITHOUT networks:', pubkeysWithoutNetworks.length);
1755
+
1756
+ if (pubkeysWithoutNetworks.length > 0) {
1757
+ console.warn('⚠️ [WARNING] Some pubkeys missing networks field:');
1758
+ pubkeysWithoutNetworks.forEach(pk => {
1759
+ console.warn(` - ${pk.note || pk.pubkey.slice(0, 10)}: networks=${pk.networks}`);
1760
+ });
1761
+ }
1762
+
1704
1763
  const assetQuery: { caip: string; pubkey: string }[] = [];
1705
1764
 
1706
1765
  for (const networkId of networkIds) {
@@ -1711,7 +1770,7 @@ export class SDK {
1711
1770
  }
1712
1771
 
1713
1772
  const isEip155 = adjustedNetworkId.includes('eip155');
1714
- const pubkeys = this.pubkeys.filter(
1773
+ let pubkeys = this.pubkeys.filter(
1715
1774
  (pubkey) =>
1716
1775
  pubkey.networks &&
1717
1776
  Array.isArray(pubkey.networks) &&
@@ -1721,21 +1780,73 @@ export class SDK {
1721
1780
  }),
1722
1781
  );
1723
1782
 
1783
+ // FIX 2: Fallback query for missing networks
1784
+ if (pubkeys.length === 0) {
1785
+ console.warn(tag, `⚠️ No pubkeys found for ${networkId} with networks field`);
1786
+ console.warn(tag, 'Attempting fallback: finding pubkeys by path matching');
1787
+
1788
+ // Find paths for this network
1789
+ const pathsForNetwork = this.paths.filter(p =>
1790
+ p.networks?.includes(networkId) ||
1791
+ (networkId.startsWith('eip155:') && p.networks?.includes('eip155:*'))
1792
+ );
1793
+
1794
+ // Find pubkeys matching those paths
1795
+ for (const path of pathsForNetwork) {
1796
+ const matchingPubkey = this.pubkeys.find(pk =>
1797
+ JSON.stringify(pk.addressNList) === JSON.stringify(path.addressNList)
1798
+ );
1799
+
1800
+ if (matchingPubkey) {
1801
+ console.warn(tag, ` ✓ Found pubkey via path matching: ${matchingPubkey.note || matchingPubkey.pubkey.slice(0, 10)}`);
1802
+ pubkeys.push(matchingPubkey);
1803
+ }
1804
+ }
1805
+
1806
+ if (pubkeys.length > 0) {
1807
+ console.warn(tag, ` ✅ Fallback successful: Found ${pubkeys.length} pubkeys for ${networkId}`);
1808
+ } else {
1809
+ console.error(tag, ` ❌ Fallback failed: No pubkeys found for ${networkId}`);
1810
+ }
1811
+ }
1812
+
1724
1813
  const caipNative = await networkIdToCaip(networkId);
1725
1814
  for (const pubkey of pubkeys) {
1726
1815
  assetQuery.push({ caip: caipNative, pubkey: pubkey.pubkey });
1727
1816
  }
1728
1817
  }
1729
1818
 
1819
+ // DIAGNOSTIC: Log assetQuery before API call
1820
+ console.log('🔍 [DIAGNOSTIC] Built assetQuery with', assetQuery.length, 'entries');
1821
+ console.log('🔍 [DIAGNOSTIC] Sample queries:', assetQuery.slice(0, 5));
1822
+
1823
+ // Group by CAIP to see which chains are included
1824
+ const caipCounts = new Map<string, number>();
1825
+ for (const query of assetQuery) {
1826
+ caipCounts.set(query.caip, (caipCounts.get(query.caip) || 0) + 1);
1827
+ }
1828
+ console.log('🔍 [DIAGNOSTIC] Queries by chain:');
1829
+ caipCounts.forEach((count, caip) => {
1830
+ console.log(` - ${caip}: ${count} queries`);
1831
+ });
1832
+
1833
+ console.log(`⏱️ [PERF] Starting GetPortfolioBalances API call...`);
1834
+ const apiCallStart = performance.now();
1730
1835
  console.time('GetPortfolioBalances Response Time');
1731
1836
 
1732
1837
  try {
1733
- let marketInfo = await this.pioneer.GetPortfolioBalances(assetQuery);
1838
+ // FIX: Wrap assetQuery in object with pubkeys field to match server API
1839
+ let marketInfo = await this.pioneer.GetPortfolioBalances({ pubkeys: assetQuery });
1840
+ const apiCallTime = performance.now() - apiCallStart;
1734
1841
  console.timeEnd('GetPortfolioBalances Response Time');
1842
+ console.log(`⏱️ [PERF] API call completed in ${apiCallTime.toFixed(0)}ms`);
1735
1843
 
1844
+ const enrichStart = performance.now();
1736
1845
  let balances = marketInfo.data;
1846
+ console.log(`⏱️ [PERF] Received ${balances?.length || 0} balances from server`);
1737
1847
 
1738
1848
  // Enrich balances with asset info
1849
+ console.log(`⏱️ [PERF] Starting balance enrichment...`);
1739
1850
  for (let balance of balances) {
1740
1851
  const assetInfo = this.assetsMap.get(balance.caip.toLowerCase()) || this.assetsMap.get(balance.caip);
1741
1852
  if (!assetInfo) continue;
@@ -1751,8 +1862,12 @@ export class SDK {
1751
1862
  color, // Add color from mapping
1752
1863
  });
1753
1864
  }
1865
+ const enrichTime = performance.now() - enrichStart;
1866
+ console.log(`⏱️ [PERF] Enrichment completed in ${enrichTime.toFixed(0)}ms`);
1867
+
1754
1868
  this.balances = balances;
1755
1869
  this.events.emit('SET_BALANCES', this.balances);
1870
+ console.log(`⏱️ [PERF] Total getBalancesForNetworks: ${(performance.now() - apiCallStart).toFixed(0)}ms`);
1756
1871
  return this.balances;
1757
1872
  } catch (apiError: any) {
1758
1873
  console.error(tag, 'GetPortfolioBalances API call failed:', apiError);
@@ -1834,32 +1949,68 @@ export class SDK {
1834
1949
  this.getCharts = async function () {
1835
1950
  const tag = `${TAG} | getCharts | `;
1836
1951
  try {
1837
- console.log(tag, 'Fetching charts');
1952
+ console.log(tag, 'Fetching charts from batch endpoint');
1838
1953
 
1839
- // Fetch balances from the `getCharts` function
1840
- const newBalances = await getCharts(
1841
- this.blockchains,
1842
- this.pioneer,
1843
- this.pubkeys,
1844
- this.context,
1845
- );
1846
- console.log(tag, 'newBalances: ', newBalances);
1954
+ // Build pubkeys array for batch request
1955
+ const pubkeysForBatch: { pubkey: string; caip: string }[] = [];
1956
+
1957
+ for (const pubkey of this.pubkeys) {
1958
+ const address = pubkey.address || pubkey.master || pubkey.pubkey;
1959
+ if (!address) continue;
1960
+
1961
+ // Get networks this pubkey supports
1962
+ const supportedNetworks = pubkey.networks || [];
1963
+
1964
+ // For each blockchain we're processing
1965
+ for (const blockchain of this.blockchains) {
1966
+ // Check if this pubkey supports this blockchain
1967
+ const supportsNetwork = supportedNetworks.some((net: string) =>
1968
+ net === blockchain ||
1969
+ (net.endsWith(':*') && blockchain.startsWith(net.replace(':*', ':')))
1970
+ );
1971
+
1972
+ if (supportsNetwork) {
1973
+ // Build the CAIP for this combination
1974
+ let caip: string;
1975
+ if (blockchain.startsWith('eip155:')) {
1976
+ caip = `${blockchain}/slip44:60`;
1977
+ } else {
1978
+ caip = `${blockchain}/slip44:0`; // Fallback for non-EVM
1979
+ }
1980
+
1981
+ pubkeysForBatch.push({
1982
+ pubkey: address,
1983
+ caip: caip
1984
+ });
1985
+ }
1986
+ }
1987
+ }
1988
+
1989
+ console.log(tag, `Calling batch GetCharts with ${pubkeysForBatch.length} pubkeys`);
1990
+
1991
+ // Single batch call to get ALL charts data
1992
+ const chartsResponse = await this.pioneer.GetCharts({
1993
+ pubkeys: pubkeysForBatch
1994
+ });
1995
+
1996
+ const newBalances = chartsResponse?.data?.balances || [];
1997
+ console.log(tag, `Received ${newBalances.length} balances from batch endpoint`);
1847
1998
 
1848
1999
  // Deduplicate balances using a Map with `identifier` as the key
1849
2000
  const uniqueBalances = new Map(
1850
2001
  [...this.balances, ...newBalances].map((balance: any) => [
1851
- balance.identifier,
2002
+ balance.identifier || `${balance.caip}:${balance.pubkey}`,
1852
2003
  {
1853
2004
  ...balance,
1854
2005
  type: balance.type || 'balance',
1855
2006
  },
1856
2007
  ]),
1857
2008
  );
1858
- console.log(tag, 'uniqueBalances: ', uniqueBalances);
2009
+ console.log(tag, 'uniqueBalances: ', uniqueBalances.size);
1859
2010
 
1860
2011
  // Convert Map back to array and set this.balances
1861
2012
  this.balances = Array.from(uniqueBalances.values());
1862
- console.log(tag, 'Updated this.balances: ', this.balances);
2013
+ console.log(tag, 'Updated this.balances: ', this.balances.length);
1863
2014
 
1864
2015
  return this.balances;
1865
2016
  } catch (e) {