@pioneer-platform/pioneer-cache 1.5.0 → 1.7.0

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/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @pioneer-platform/pioneer-cache
2
2
 
3
+ ## 1.7.0
4
+
5
+ ### Minor Changes
6
+
7
+ - chore: chore: chore: chore: chore: feat(pioneer): implement end-to-end Solana transaction signing
8
+
9
+ ## 1.6.0
10
+
11
+ ### Minor Changes
12
+
13
+ - chore: chore: chore: chore: feat(pioneer): implement end-to-end Solana transaction signing
14
+
3
15
  ## 1.5.0
4
16
 
5
17
  ### Minor Changes
@@ -207,76 +207,94 @@ class PortfolioCache extends base_cache_1.BaseCache {
207
207
  const { pubkeys } = params;
208
208
  log.info(tag, `Fetching portfolio for ${pubkeys.length} pubkeys`);
209
209
  const charts = [];
210
- // Fetch balances for all pubkeys in parallel
211
- const balancePromises = pubkeys.map(async (item) => {
212
- try {
213
- // Extract networkId from CAIP
214
- const networkId = item.caip.split('/')[0];
215
- // Fetch balance
216
- const asset = { caip: item.caip };
217
- const owner = { pubkey: item.pubkey };
218
- const balanceInfo = await this.balanceModule.getBalance(asset, owner);
219
- if (!balanceInfo || !balanceInfo.balance) {
220
- log.debug(tag, `No balance for ${item.caip}/${item.pubkey.substring(0, 10)}...`);
221
- return null;
222
- }
223
- // Skip zero balances
224
- const balanceNum = parseFloat(balanceInfo.balance);
225
- if (isNaN(balanceNum) || balanceNum === 0) {
226
- return null;
227
- }
228
- // Get asset metadata
229
- const assetData = require('@pioneer-platform/pioneer-discovery').assetData;
230
- const normalizedCaip = item.caip.toLowerCase();
231
- let assetInfo = assetData[normalizedCaip] || assetData[item.caip.toUpperCase()] || assetData[item.caip] || {};
232
- // EXPLICIT FIX: Force XRP to always be native (prevents token misclassification)
233
- // GitHub Issue: XRP incorrectly showing as requiring BNB gas for sending
234
- if (normalizedCaip === 'ripple:4109c6f2045fc7eff4cde8f9905d19c2/slip44:144') {
235
- // Clone assetInfo and force type to native
236
- assetInfo = { ...assetInfo, type: 'native' };
237
- if (!assetInfo.symbol) {
238
- log.warn(tag, `XRP asset lookup failed! Using fallback with forced native type.`);
239
- assetInfo.symbol = 'XRP';
240
- assetInfo.name = 'Ripple';
241
- }
242
- log.debug(tag, `XRP explicitly set to type: native`);
243
- }
244
- // Get price
245
- let priceUsd = 0;
210
+ // BATCHING FIX: Process pubkeys in batches to prevent thundering herd
211
+ // Instead of 100+ parallel requests, process 10 at a time
212
+ const BATCH_SIZE = 10;
213
+ const BATCH_DELAY_MS = 100; // 100ms delay between batches
214
+ log.info(tag, `Using batched fetching: ${BATCH_SIZE} pubkeys per batch with ${BATCH_DELAY_MS}ms delay`);
215
+ for (let i = 0; i < pubkeys.length; i += BATCH_SIZE) {
216
+ const batch = pubkeys.slice(i, i + BATCH_SIZE);
217
+ const batchNum = Math.floor(i / BATCH_SIZE) + 1;
218
+ const totalBatches = Math.ceil(pubkeys.length / BATCH_SIZE);
219
+ log.debug(tag, `Processing batch ${batchNum}/${totalBatches} (${batch.length} pubkeys)`);
220
+ // Process this batch in parallel
221
+ const balancePromises = batch.map(async (item) => {
246
222
  try {
247
- priceUsd = await this.marketsModule.getAssetPriceByCaip(item.caip);
248
- if (isNaN(priceUsd) || priceUsd < 0) {
249
- priceUsd = 0;
223
+ // Extract networkId from CAIP
224
+ const networkId = item.caip.split('/')[0];
225
+ // Fetch balance
226
+ const asset = { caip: item.caip };
227
+ const owner = { pubkey: item.pubkey };
228
+ const balanceInfo = await this.balanceModule.getBalance(asset, owner);
229
+ if (!balanceInfo || !balanceInfo.balance) {
230
+ log.debug(tag, `No balance for ${item.caip}/${item.pubkey.substring(0, 10)}...`);
231
+ return null;
250
232
  }
233
+ // Skip zero balances
234
+ const balanceNum = parseFloat(balanceInfo.balance);
235
+ if (isNaN(balanceNum) || balanceNum === 0) {
236
+ return null;
237
+ }
238
+ // Get asset metadata
239
+ const assetData = require('@pioneer-platform/pioneer-discovery').assetData;
240
+ const normalizedCaip = item.caip.toLowerCase();
241
+ let assetInfo = assetData[normalizedCaip] || assetData[item.caip.toUpperCase()] || assetData[item.caip] || {};
242
+ // EXPLICIT FIX: Force XRP to always be native (prevents token misclassification)
243
+ // GitHub Issue: XRP incorrectly showing as requiring BNB gas for sending
244
+ if (normalizedCaip === 'ripple:4109c6f2045fc7eff4cde8f9905d19c2/slip44:144') {
245
+ // Clone assetInfo and force type to native
246
+ assetInfo = { ...assetInfo, type: 'native' };
247
+ if (!assetInfo.symbol) {
248
+ log.warn(tag, `XRP asset lookup failed! Using fallback with forced native type.`);
249
+ assetInfo.symbol = 'XRP';
250
+ assetInfo.name = 'Ripple';
251
+ }
252
+ log.debug(tag, `XRP explicitly set to type: native`);
253
+ }
254
+ // Get price
255
+ let priceUsd = 0;
256
+ try {
257
+ priceUsd = await this.marketsModule.getAssetPriceByCaip(item.caip);
258
+ if (isNaN(priceUsd) || priceUsd < 0) {
259
+ priceUsd = 0;
260
+ }
261
+ }
262
+ catch (priceError) {
263
+ log.warn(tag, `Error fetching price for ${item.caip}:`, priceError);
264
+ }
265
+ const valueUsd = balanceNum * priceUsd;
266
+ const chartData = {
267
+ caip: item.caip,
268
+ pubkey: item.pubkey,
269
+ networkId,
270
+ symbol: assetInfo.symbol || 'UNKNOWN',
271
+ name: assetInfo.name || 'Unknown Asset',
272
+ balance: balanceInfo.balance,
273
+ priceUsd,
274
+ valueUsd,
275
+ icon: assetInfo.icon || '',
276
+ type: assetInfo.type || 'native',
277
+ decimal: assetInfo.decimal
278
+ };
279
+ return chartData;
251
280
  }
252
- catch (priceError) {
253
- log.warn(tag, `Error fetching price for ${item.caip}:`, priceError);
281
+ catch (error) {
282
+ log.error(tag, `Error fetching balance for ${item.caip}/${item.pubkey}:`, error);
283
+ return null;
254
284
  }
255
- const valueUsd = balanceNum * priceUsd;
256
- const chartData = {
257
- caip: item.caip,
258
- pubkey: item.pubkey,
259
- networkId,
260
- symbol: assetInfo.symbol || 'UNKNOWN',
261
- name: assetInfo.name || 'Unknown Asset',
262
- balance: balanceInfo.balance,
263
- priceUsd,
264
- valueUsd,
265
- icon: assetInfo.icon || '',
266
- type: assetInfo.type || 'native',
267
- decimal: assetInfo.decimal
268
- };
269
- return chartData;
270
- }
271
- catch (error) {
272
- log.error(tag, `Error fetching balance for ${item.caip}/${item.pubkey}:`, error);
273
- return null;
285
+ });
286
+ // Await this batch
287
+ const results = await Promise.all(balancePromises);
288
+ // Filter out nulls and add to charts
289
+ const validCharts = results.filter((c) => c !== null);
290
+ charts.push(...validCharts);
291
+ log.debug(tag, `Batch ${batchNum}/${totalBatches} complete: ${validCharts.length}/${batch.length} successful`);
292
+ // Add delay between batches to prevent rate limiting
293
+ if (i + BATCH_SIZE < pubkeys.length) {
294
+ await new Promise(resolve => setTimeout(resolve, BATCH_DELAY_MS));
274
295
  }
275
- });
276
- const results = await Promise.all(balancePromises);
277
- // Filter out nulls
278
- const validCharts = results.filter((c) => c !== null);
279
- charts.push(...validCharts);
296
+ }
297
+ log.info(tag, `Batched fetching complete: ${charts.length}/${pubkeys.length} assets fetched`);
280
298
  // INTEGRATED: Fetch stable coins for EVM addresses (background operation)
281
299
  // This ensures stable coins are ALWAYS in the cache and available instantly
282
300
  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": "1.5.0",
3
+ "version": "1.7.0",
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",
@@ -279,8 +279,22 @@ export class PortfolioCache extends BaseCache<PortfolioData> {
279
279
 
280
280
  const charts: ChartData[] = [];
281
281
 
282
- // Fetch balances for all pubkeys in parallel
283
- const balancePromises = pubkeys.map(async (item: { pubkey: string; caip: string }) => {
282
+ // BATCHING FIX: Process pubkeys in batches to prevent thundering herd
283
+ // Instead of 100+ parallel requests, process 10 at a time
284
+ const BATCH_SIZE = 10;
285
+ const BATCH_DELAY_MS = 100; // 100ms delay between batches
286
+
287
+ log.info(tag, `Using batched fetching: ${BATCH_SIZE} pubkeys per batch with ${BATCH_DELAY_MS}ms delay`);
288
+
289
+ for (let i = 0; i < pubkeys.length; i += BATCH_SIZE) {
290
+ const batch = pubkeys.slice(i, i + BATCH_SIZE);
291
+ const batchNum = Math.floor(i / BATCH_SIZE) + 1;
292
+ const totalBatches = Math.ceil(pubkeys.length / BATCH_SIZE);
293
+
294
+ log.debug(tag, `Processing batch ${batchNum}/${totalBatches} (${batch.length} pubkeys)`);
295
+
296
+ // Process this batch in parallel
297
+ const balancePromises = batch.map(async (item: { pubkey: string; caip: string }) => {
284
298
  try {
285
299
  // Extract networkId from CAIP
286
300
  const networkId = item.caip.split('/')[0];
@@ -352,13 +366,24 @@ export class PortfolioCache extends BaseCache<PortfolioData> {
352
366
  log.error(tag, `Error fetching balance for ${item.caip}/${item.pubkey}:`, error);
353
367
  return null;
354
368
  }
355
- });
369
+ });
356
370
 
357
- const results = await Promise.all(balancePromises);
371
+ // Await this batch
372
+ const results = await Promise.all(balancePromises);
373
+
374
+ // Filter out nulls and add to charts
375
+ const validCharts = results.filter((c): c is ChartData => c !== null);
376
+ charts.push(...validCharts);
377
+
378
+ log.debug(tag, `Batch ${batchNum}/${totalBatches} complete: ${validCharts.length}/${batch.length} successful`);
379
+
380
+ // Add delay between batches to prevent rate limiting
381
+ if (i + BATCH_SIZE < pubkeys.length) {
382
+ await new Promise(resolve => setTimeout(resolve, BATCH_DELAY_MS));
383
+ }
384
+ }
358
385
 
359
- // Filter out nulls
360
- const validCharts = results.filter((c): c is ChartData => c !== null);
361
- charts.push(...validCharts);
386
+ log.info(tag, `Batched fetching complete: ${charts.length}/${pubkeys.length} assets fetched`);
362
387
 
363
388
  // INTEGRATED: Fetch stable coins for EVM addresses (background operation)
364
389
  // This ensures stable coins are ALWAYS in the cache and available instantly