@pioneer-platform/pioneer-cache 3.0.4 → 3.0.7

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.
@@ -1,5 +1,5 @@
1
1
 
2
2
  
3
- > @pioneer-platform/pioneer-cache@3.0.4 build /Users/highlander/WebstormProjects/keepkey-stack/projects/pioneer/modules/pioneer/pioneer-cache
3
+ > @pioneer-platform/pioneer-cache@3.0.7 build /Users/highlander/WebstormProjects/keepkey-stack/projects/pioneer/modules/pioneer/pioneer-cache
4
4
  > tsc
5
5
 
package/CHANGELOG.md CHANGED
@@ -1,5 +1,28 @@
1
1
  # @pioneer-platform/pioneer-cache
2
2
 
3
+ ## 3.0.7
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [5df7310]
8
+ - @pioneer-platform/pioneer-discovery@10.0.9
9
+
10
+ ## 3.0.6
11
+
12
+ ### Patch Changes
13
+
14
+ - chore: chore: chore: chore: chore: version bump pioneer-sdk and pioneer-server for perf fixes
15
+ - Updated dependencies
16
+ - @pioneer-platform/pioneer-discovery@10.0.8
17
+
18
+ ## 3.0.5
19
+
20
+ ### Patch Changes
21
+
22
+ - chore: chore: chore: chore: chore: version bump pioneer-sdk and pioneer-server for perf fixes
23
+ - Updated dependencies
24
+ - @pioneer-platform/pioneer-discovery@10.0.7
25
+
3
26
  ## 3.0.4
4
27
 
5
28
  ### Patch Changes
@@ -66,6 +66,11 @@ export declare class PortfolioCache extends BaseCache<PortfolioData> {
66
66
  * INTEGRATED: Part of portfolio background refresh, no direct blockchain queries from endpoints
67
67
  */
68
68
  private fetchCustomTokens;
69
+ /**
70
+ * Fetch SPL token balances for a Solana address
71
+ * INTEGRATED: Part of portfolio background refresh
72
+ */
73
+ private fetchSplTokens;
69
74
  /**
70
75
  * Fetch portfolio from blockchain APIs
71
76
  *
@@ -295,6 +295,88 @@ class PortfolioCache extends base_cache_1.BaseCache {
295
295
  return customCharts;
296
296
  }
297
297
  }
298
+ /**
299
+ * Fetch SPL token balances for a Solana address
300
+ * INTEGRATED: Part of portfolio background refresh
301
+ */
302
+ async fetchSplTokens(address) {
303
+ const tag = this.TAG + 'fetchSplTokens | ';
304
+ const splCharts = [];
305
+ try {
306
+ // Get Solana network module (from global or require)
307
+ const globalNetworks = global.pioneerNetworks;
308
+ const solana = globalNetworks?.solana || require('@pioneer-platform/solana-network');
309
+ if (!solana || !solana.getTokenBalances) {
310
+ log.warn(tag, 'Solana network module not available, skipping SPL token fetch');
311
+ return splCharts;
312
+ }
313
+ const tokenAccounts = await Promise.race([
314
+ solana.getTokenBalances(address),
315
+ new Promise((_, reject) => setTimeout(() => reject(new Error('timeout')), 10000))
316
+ ]).catch((err) => {
317
+ log.warn(tag, `SPL token fetch failed: ${err.message}`);
318
+ return [];
319
+ });
320
+ if (!tokenAccounts || tokenAccounts.length === 0) {
321
+ log.debug(tag, 'No SPL token accounts found');
322
+ return splCharts;
323
+ }
324
+ log.info(tag, `Found ${tokenAccounts.length} SPL token accounts, enriching with metadata...`);
325
+ const assetData = require('@pioneer-platform/pioneer-discovery').assetData;
326
+ const SOLANA_NETWORK_ID = 'solana:5eykt4usfv8p8njdtrepy1vzqkqzkvdp';
327
+ for (const token of tokenAccounts) {
328
+ // Skip zero-balance tokens
329
+ if (!token.balance || token.balance === 0)
330
+ continue;
331
+ const tokenCaip = `${SOLANA_NETWORK_ID}/token:${token.mint}`;
332
+ // Look up metadata from pioneer-discovery
333
+ const tokenInfo = assetData[tokenCaip] || assetData[tokenCaip.toLowerCase()] || {};
334
+ // Get price from markets module
335
+ let priceUsd = 0;
336
+ try {
337
+ priceUsd = await this.marketsModule.getAssetPriceByCaip(tokenCaip);
338
+ if (isNaN(priceUsd) || priceUsd < 0)
339
+ priceUsd = 0;
340
+ }
341
+ catch (priceError) {
342
+ // Try with lowercase CAIP
343
+ try {
344
+ priceUsd = await this.marketsModule.getAssetPriceByCaip(tokenCaip.toLowerCase());
345
+ if (isNaN(priceUsd) || priceUsd < 0)
346
+ priceUsd = 0;
347
+ }
348
+ catch {
349
+ // No price available
350
+ }
351
+ }
352
+ const balanceStr = token.uiAmountString || String(token.balance);
353
+ const balanceNum = parseFloat(balanceStr);
354
+ const valueUsd = balanceNum * priceUsd;
355
+ const chartData = {
356
+ caip: tokenCaip,
357
+ pubkey: address,
358
+ networkId: SOLANA_NETWORK_ID,
359
+ symbol: tokenInfo.symbol || token.mint.substring(0, 6),
360
+ name: tokenInfo.name || `SPL Token (${token.mint.substring(0, 8)}...)`,
361
+ balance: balanceStr,
362
+ priceUsd,
363
+ valueUsd,
364
+ icon: tokenInfo.icon || '',
365
+ type: 'token',
366
+ decimal: token.decimals ?? tokenInfo.decimals
367
+ };
368
+ splCharts.push(chartData);
369
+ log.debug(tag, `SPL: ${chartData.symbol} = ${balanceStr} ($${valueUsd.toFixed(2)})`);
370
+ }
371
+ log.info(tag, `Fetched ${splCharts.length} non-zero SPL token balances`);
372
+ return splCharts;
373
+ }
374
+ catch (error) {
375
+ const errorMsg = error.message || String(error);
376
+ log.error(tag, `Error fetching SPL tokens: ${errorMsg}`);
377
+ return splCharts;
378
+ }
379
+ }
298
380
  /**
299
381
  * Fetch portfolio from blockchain APIs
300
382
  *
@@ -410,6 +492,22 @@ class PortfolioCache extends base_cache_1.BaseCache {
410
492
  }
411
493
  }
412
494
  log.info(tag, `Batched fetching complete: ${charts.length}/${pubkeys.length} assets fetched`);
495
+ // INTEGRATED: Fetch SPL token balances for Solana addresses (background operation)
496
+ // SPL tokens are returned as splTokens[] from the balance module's SOLANA case
497
+ const solPubkey = pubkeys.find((p) => p.caip && p.caip.startsWith('solana:') && p.caip.includes('/slip44:501'));
498
+ if (solPubkey) {
499
+ const solAddress = solPubkey.pubkey;
500
+ log.info(tag, `Fetching SPL tokens for Solana address: ${solAddress.substring(0, 10)}...`);
501
+ const splCharts = await this.fetchSplTokens(solAddress);
502
+ // Add SPL tokens to charts (deduplicate by CAIP+pubkey)
503
+ for (const splToken of splCharts) {
504
+ const isDuplicate = charts.some(c => c.caip === splToken.caip && c.pubkey === splToken.pubkey);
505
+ if (!isDuplicate) {
506
+ charts.push(splToken);
507
+ }
508
+ }
509
+ log.info(tag, `Added ${splCharts.length} SPL tokens to portfolio`);
510
+ }
413
511
  // INTEGRATED: Fetch stable coins for EVM addresses (background operation)
414
512
  // This ensures stable coins are ALWAYS in the cache and available instantly
415
513
  const evmPubkey = pubkeys.find((p) => p.caip && p.caip.startsWith('eip155:'));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pioneer-platform/pioneer-cache",
3
- "version": "3.0.4",
3
+ "version": "3.0.7",
4
4
  "description": "Unified caching system for Pioneer platform with Redis backend",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -15,10 +15,10 @@
15
15
  "license": "MIT",
16
16
  "dependencies": {
17
17
  "@pioneer-platform/loggerdog": "8.11.0",
18
+ "@pioneer-platform/pioneer-caip": "9.27.10",
18
19
  "@pioneer-platform/redis-queue": "8.12.21",
19
20
  "@pioneer-platform/default-redis": "8.11.11",
20
- "@pioneer-platform/pioneer-discovery": "10.0.6",
21
- "@pioneer-platform/pioneer-caip": "9.27.10"
21
+ "@pioneer-platform/pioneer-discovery": "10.0.9"
22
22
  },
23
23
  "devDependencies": {
24
24
  "@types/jest": "^29.5.0",
@@ -387,6 +387,98 @@ export class PortfolioCache extends BaseCache<PortfolioData> {
387
387
  }
388
388
  }
389
389
 
390
+ /**
391
+ * Fetch SPL token balances for a Solana address
392
+ * INTEGRATED: Part of portfolio background refresh
393
+ */
394
+ private async fetchSplTokens(address: string): Promise<ChartData[]> {
395
+ const tag = this.TAG + 'fetchSplTokens | ';
396
+ const splCharts: ChartData[] = [];
397
+
398
+ try {
399
+ // Get Solana network module (from global or require)
400
+ const globalNetworks = (global as any).pioneerNetworks;
401
+ const solana = globalNetworks?.solana || require('@pioneer-platform/solana-network');
402
+
403
+ if (!solana || !solana.getTokenBalances) {
404
+ log.warn(tag, 'Solana network module not available, skipping SPL token fetch');
405
+ return splCharts;
406
+ }
407
+
408
+ const tokenAccounts = await Promise.race([
409
+ solana.getTokenBalances(address),
410
+ new Promise((_, reject) => setTimeout(() => reject(new Error('timeout')), 10000))
411
+ ]).catch((err: any) => {
412
+ log.warn(tag, `SPL token fetch failed: ${err.message}`);
413
+ return [];
414
+ }) as any[];
415
+
416
+ if (!tokenAccounts || tokenAccounts.length === 0) {
417
+ log.debug(tag, 'No SPL token accounts found');
418
+ return splCharts;
419
+ }
420
+
421
+ log.info(tag, `Found ${tokenAccounts.length} SPL token accounts, enriching with metadata...`);
422
+
423
+ const assetData = require('@pioneer-platform/pioneer-discovery').assetData;
424
+ const SOLANA_NETWORK_ID = 'solana:5eykt4usfv8p8njdtrepy1vzqkqzkvdp';
425
+
426
+ for (const token of tokenAccounts) {
427
+ // Skip zero-balance tokens
428
+ if (!token.balance || token.balance === 0) continue;
429
+
430
+ const tokenCaip = `${SOLANA_NETWORK_ID}/token:${token.mint}`;
431
+
432
+ // Look up metadata from pioneer-discovery
433
+ const tokenInfo = assetData[tokenCaip] || assetData[tokenCaip.toLowerCase()] || {};
434
+
435
+ // Get price from markets module
436
+ let priceUsd = 0;
437
+ try {
438
+ priceUsd = await this.marketsModule.getAssetPriceByCaip(tokenCaip);
439
+ if (isNaN(priceUsd) || priceUsd < 0) priceUsd = 0;
440
+ } catch (priceError) {
441
+ // Try with lowercase CAIP
442
+ try {
443
+ priceUsd = await this.marketsModule.getAssetPriceByCaip(tokenCaip.toLowerCase());
444
+ if (isNaN(priceUsd) || priceUsd < 0) priceUsd = 0;
445
+ } catch {
446
+ // No price available
447
+ }
448
+ }
449
+
450
+ const balanceStr = token.uiAmountString || String(token.balance);
451
+ const balanceNum = parseFloat(balanceStr);
452
+ const valueUsd = balanceNum * priceUsd;
453
+
454
+ const chartData: ChartData = {
455
+ caip: tokenCaip,
456
+ pubkey: address,
457
+ networkId: SOLANA_NETWORK_ID,
458
+ symbol: tokenInfo.symbol || token.mint.substring(0, 6),
459
+ name: tokenInfo.name || `SPL Token (${token.mint.substring(0, 8)}...)`,
460
+ balance: balanceStr,
461
+ priceUsd,
462
+ valueUsd,
463
+ icon: tokenInfo.icon || '',
464
+ type: 'token',
465
+ decimal: token.decimals ?? tokenInfo.decimals
466
+ };
467
+
468
+ splCharts.push(chartData);
469
+ log.debug(tag, `SPL: ${chartData.symbol} = ${balanceStr} ($${valueUsd.toFixed(2)})`);
470
+ }
471
+
472
+ log.info(tag, `Fetched ${splCharts.length} non-zero SPL token balances`);
473
+ return splCharts;
474
+
475
+ } catch (error: any) {
476
+ const errorMsg = error.message || String(error);
477
+ log.error(tag, `Error fetching SPL tokens: ${errorMsg}`);
478
+ return splCharts;
479
+ }
480
+ }
481
+
390
482
  /**
391
483
  * Fetch portfolio from blockchain APIs
392
484
  *
@@ -523,6 +615,28 @@ export class PortfolioCache extends BaseCache<PortfolioData> {
523
615
 
524
616
  log.info(tag, `Batched fetching complete: ${charts.length}/${pubkeys.length} assets fetched`);
525
617
 
618
+ // INTEGRATED: Fetch SPL token balances for Solana addresses (background operation)
619
+ // SPL tokens are returned as splTokens[] from the balance module's SOLANA case
620
+ const solPubkey = pubkeys.find(
621
+ (p: any) => p.caip && p.caip.startsWith('solana:') && p.caip.includes('/slip44:501')
622
+ );
623
+ if (solPubkey) {
624
+ const solAddress = solPubkey.pubkey;
625
+ log.info(tag, `Fetching SPL tokens for Solana address: ${solAddress.substring(0, 10)}...`);
626
+ const splCharts = await this.fetchSplTokens(solAddress);
627
+
628
+ // Add SPL tokens to charts (deduplicate by CAIP+pubkey)
629
+ for (const splToken of splCharts) {
630
+ const isDuplicate = charts.some(c =>
631
+ c.caip === splToken.caip && c.pubkey === splToken.pubkey
632
+ );
633
+ if (!isDuplicate) {
634
+ charts.push(splToken);
635
+ }
636
+ }
637
+ log.info(tag, `Added ${splCharts.length} SPL tokens to portfolio`);
638
+ }
639
+
526
640
  // INTEGRATED: Fetch stable coins for EVM addresses (background operation)
527
641
  // This ensures stable coins are ALWAYS in the cache and available instantly
528
642
  const evmPubkey = pubkeys.find(