@steerprotocol/sdk 1.29.3 → 1.30.1

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.
@@ -17,6 +17,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
17
17
  exports.VaultClient = void 0;
18
18
  const api_sdk_1 = require("@steerprotocol/api-sdk");
19
19
  const const_1 = require("../const");
20
+ const ammConfig_js_1 = require("../const/amm/configs/ammConfig.js");
20
21
  const protocol_1 = require("../const/amm/utils/protocol");
21
22
  const chain_1 = require("../const/chain");
22
23
  const subgraph_1 = require("../const/subgraph");
@@ -162,8 +163,95 @@ class VaultClient extends SubgraphClient_js_1.SubgraphClient {
162
163
  this.subgraphVaultClient = new SubgraphVaultClient_1.SubgraphVaultClient();
163
164
  this.subgraphStudioKey = subgraphStudioKey || '';
164
165
  }
166
+ normalizeProtocolValue(value) {
167
+ return value.toLowerCase().replace(/[^a-z0-9]/g, '');
168
+ }
169
+ resolveProtocolEnum(protocol) {
170
+ if (!protocol) {
171
+ return null;
172
+ }
173
+ const normalizedProtocol = this.normalizeProtocolValue(protocol);
174
+ const matchedProtocol = Object.values(chain_1.Protocol).find(protocolValue => this.normalizeProtocolValue(protocolValue) === normalizedProtocol);
175
+ return matchedProtocol || null;
176
+ }
177
+ getProtocolBeaconNames(protocol) {
178
+ const resolvedProtocol = this.resolveProtocolEnum(protocol);
179
+ if (!resolvedProtocol) {
180
+ return [];
181
+ }
182
+ const protocolConfig = (0, ammConfig_js_1.getAmmConfig)(this.subgraphStudioKey)[resolvedProtocol];
183
+ const beaconNames = [];
184
+ if (protocolConfig?.beaconContract) {
185
+ beaconNames.push(protocolConfig.beaconContract);
186
+ }
187
+ if (protocolConfig?.beaconContractSushiManaged) {
188
+ beaconNames.push(protocolConfig.beaconContractSushiManaged);
189
+ }
190
+ if (resolvedProtocol === chain_1.Protocol.Blackhole) {
191
+ beaconNames.push(chain_1.MultiPositionManagers.MultiPositionBlackholeOld);
192
+ }
193
+ const beaconAlias = (0, const_1.getBeaconNameByProtocol)(resolvedProtocol);
194
+ if (beaconAlias) {
195
+ beaconNames.push(beaconAlias);
196
+ }
197
+ return [...new Set(beaconNames.filter(name => name.length > 0))];
198
+ }
199
+ getPrimaryProtocolBeaconName(protocol) {
200
+ const resolvedProtocol = this.resolveProtocolEnum(protocol);
201
+ if (!resolvedProtocol) {
202
+ return null;
203
+ }
204
+ const protocolConfig = (0, ammConfig_js_1.getAmmConfig)(this.subgraphStudioKey)[resolvedProtocol];
205
+ return protocolConfig?.beaconContract || null;
206
+ }
207
+ buildApiVaultFilter(filter) {
208
+ if (!filter) {
209
+ return undefined;
210
+ }
211
+ const { protocol, ...rest } = filter;
212
+ const apiFilter = { ...rest };
213
+ if (!apiFilter.beaconName && protocol) {
214
+ const primaryBeaconName = this.getPrimaryProtocolBeaconName(protocol);
215
+ if (primaryBeaconName) {
216
+ apiFilter.beaconName = primaryBeaconName;
217
+ }
218
+ }
219
+ return apiFilter;
220
+ }
221
+ vaultMatchesProtocolFilter(vault, protocolFilter, resolvedProtocol) {
222
+ const normalizedFilter = this.normalizeProtocolValue(protocolFilter);
223
+ const normalizedBeaconName = this.normalizeProtocolValue(vault.beaconName);
224
+ const resolvedVaultProtocol = (0, const_1.getProtocolTypeByBeacon)(vault.beaconName);
225
+ const directCandidates = [vault.protocol, vault.protocolBaseType, resolvedVaultProtocol || ''];
226
+ const hasDirectMatch = directCandidates.some(candidate => {
227
+ if (!candidate) {
228
+ return false;
229
+ }
230
+ return this.normalizeProtocolValue(candidate) === normalizedFilter;
231
+ });
232
+ if (hasDirectMatch) {
233
+ return true;
234
+ }
235
+ const expectedBeacon = resolvedProtocol
236
+ ? (0, const_1.getBeaconNameByProtocol)(resolvedProtocol)
237
+ : protocolFilter;
238
+ const normalizedExpectedBeacon = this.normalizeProtocolValue(expectedBeacon);
239
+ return normalizedExpectedBeacon.length > 0 && normalizedBeaconName.includes(normalizedExpectedBeacon);
240
+ }
241
+ applyProtocolFilter(vaultsConnection, protocol) {
242
+ if (!protocol) {
243
+ return vaultsConnection;
244
+ }
245
+ const resolvedProtocol = this.resolveProtocolEnum(protocol);
246
+ const filteredEdges = vaultsConnection.edges.filter(edge => this.vaultMatchesProtocolFilter(edge.node, protocol, resolvedProtocol));
247
+ return {
248
+ ...vaultsConnection,
249
+ edges: filteredEdges
250
+ };
251
+ }
165
252
  /**
166
253
  * Gets vaults with pagination support
254
+ * Fetches ALL data from both API (database) and subgraph in parallel, merges without duplicates, then paginates
167
255
  * @param filter - Optional filter criteria
168
256
  * @param first - Number of items to fetch (default: 50)
169
257
  * @param after - Cursor for pagination (null for first page)
@@ -198,90 +286,388 @@ class VaultClient extends SubgraphClient_js_1.SubgraphClient {
198
286
  * ```
199
287
  */
200
288
  async getVaults(filter, first = 50, after) {
201
- if (filter?.chainId !== chain_1.ChainId.Avalanche) {
202
- try {
203
- // First try the API client
204
- const response = await this.apiClient.vaults({
205
- filter,
206
- first,
207
- after
208
- });
209
- if (response.data?.vaults) {
210
- // Transform the response to match our interface
211
- const transformedData = {
212
- edges: response.data.vaults.edges.map(edge => ({
213
- cursor: edge.cursor,
214
- node: {
215
- id: edge.node.id,
216
- chainId: edge.node.chainId,
217
- vaultAddress: edge.node.vaultAddress,
218
- protocol: edge.node.protocol,
219
- beaconName: edge.node.beaconName,
220
- protocolBaseType: edge.node.protocolBaseType,
221
- name: edge.node.name || '',
222
- feeApr: edge.node.feeApr || undefined,
223
- stakingApr: edge.node.stakingApr || undefined,
224
- merklApr: edge.node.merklApr || undefined,
225
- pool: {
226
- id: edge.node.pool?.id || '',
227
- poolAddress: edge.node.pool?.poolAddress || '',
228
- feeTier: edge.node.pool?.feeTier || '',
229
- tick: undefined, // Not available in API response
230
- liquidity: undefined, // Not available in API response
231
- volumeUSD: undefined, // Not available in API response
232
- totalValueLockedUSD: undefined // Not available in API response
233
- },
234
- token0: {
235
- id: edge.node.token0?.id || '',
236
- symbol: edge.node.token0?.symbol || '',
237
- name: edge.node.token0?.name || '',
238
- decimals: edge.node.token0?.decimals || 0,
239
- address: edge.node.token0?.address || '',
240
- chainId: edge.node.token0?.chainId || 0
241
- },
242
- token1: {
243
- id: edge.node.token1?.id || '',
244
- symbol: edge.node.token1?.symbol || '',
245
- name: edge.node.token1?.name || '',
246
- decimals: edge.node.token1?.decimals || 0,
247
- address: edge.node.token1?.address || '',
248
- chainId: edge.node.token1?.chainId || 0
249
- }
250
- }
251
- })),
252
- pageInfo: {
253
- hasNextPage: response.data.vaults.pageInfo.hasNextPage,
254
- endCursor: response.data.vaults.pageInfo.endCursor ?? null
255
- },
256
- totalCount: response.data.vaults.totalCount
257
- };
289
+ const apiFilter = this.buildApiVaultFilter(filter);
290
+ // Fetch ALL vaults from both sources in parallel (no pagination at source level)
291
+ const [apiResult, subgraphResult] = await Promise.allSettled([
292
+ this.getAllVaultsFromApi(apiFilter),
293
+ filter?.chainId !== chain_1.ChainId.Avalanche ? this.getAllVaultsFromSubgraph(filter) : Promise.reject(new Error('Avalanche not supported'))
294
+ ]);
295
+ // Extract successful results
296
+ const apiVaults = apiResult.status === 'fulfilled' && apiResult.value.success && apiResult.value.data
297
+ ? apiResult.value.data
298
+ : [];
299
+ const subgraphVaults = subgraphResult.status === 'fulfilled' && subgraphResult.value.success && subgraphResult.value.data
300
+ ? subgraphResult.value.data
301
+ : [];
302
+ // If both failed, return error
303
+ if (apiVaults.length === 0 && subgraphVaults.length === 0) {
304
+ const apiError = apiResult.status === 'rejected' ? apiResult.reason : null;
305
+ const subgraphError = subgraphResult.status === 'rejected' ? subgraphResult.reason : null;
306
+ return {
307
+ data: null,
308
+ status: 500,
309
+ success: false,
310
+ error: `Both API and subgraph failed. API: ${apiError?.message || 'Unknown error'}. Subgraph: ${subgraphError?.message || 'Unknown error'}`
311
+ };
312
+ }
313
+ // Merge results and remove duplicates based on vaultAddress
314
+ const mergedVaults = this.mergeVaultResults(apiVaults, subgraphVaults);
315
+ // Apply protocol filter to merged results
316
+ const filteredVaults = mergedVaults.filter(edge => !filter?.protocol || this.vaultMatchesProtocolFilter(edge.node, filter.protocol, this.resolveProtocolEnum(filter.protocol)));
317
+ // Apply pagination to the complete merged and filtered dataset
318
+ const paginatedVaults = this.paginateVaults(filteredVaults, first, after);
319
+ return {
320
+ data: paginatedVaults,
321
+ status: 200,
322
+ success: true
323
+ };
324
+ }
325
+ /**
326
+ * Fetches ALL vaults from API (database) by auto-paginating
327
+ * @private
328
+ */
329
+ async getAllVaultsFromApi(apiFilter) {
330
+ try {
331
+ const allVaults = [];
332
+ let hasNextPage = true;
333
+ let cursor = null;
334
+ const batchSize = 100; // Fetch in batches of 100
335
+ while (hasNextPage) {
336
+ const response = await this.getVaultsFromApi(apiFilter, batchSize, cursor);
337
+ if (!response.success || !response.data) {
338
+ // If we already have some vaults, return them; otherwise return error
339
+ if (allVaults.length > 0) {
340
+ break;
341
+ }
258
342
  return {
259
- data: transformedData,
343
+ data: null,
260
344
  status: response.status,
261
- success: true
345
+ success: false,
346
+ error: response.error || 'Failed to fetch vaults from API'
262
347
  };
263
348
  }
349
+ allVaults.push(...response.data.edges);
350
+ hasNextPage = response.data.pageInfo.hasNextPage;
351
+ cursor = response.data.pageInfo.endCursor;
352
+ }
353
+ return {
354
+ data: allVaults,
355
+ status: 200,
356
+ success: true
357
+ };
358
+ }
359
+ catch (error) {
360
+ console.warn('Failed to fetch all vaults from API:', error);
361
+ return {
362
+ data: null,
363
+ status: 500,
364
+ success: false,
365
+ error: error instanceof Error ? error.message : 'Failed to fetch all vaults from API'
366
+ };
367
+ }
368
+ }
369
+ /**
370
+ * Fetches vaults from API (database) with pagination
371
+ * @private
372
+ */
373
+ async getVaultsFromApi(apiFilter, first = 50, after) {
374
+ try {
375
+ const response = await this.apiClient.vaults({
376
+ filter: apiFilter,
377
+ first,
378
+ after
379
+ });
380
+ if (!response.data?.vaults) {
381
+ return {
382
+ data: null,
383
+ status: response.status,
384
+ success: false,
385
+ error: 'No data returned from API'
386
+ };
387
+ }
388
+ // Transform the response to match our interface
389
+ const transformedData = {
390
+ edges: response.data.vaults.edges.map(edge => ({
391
+ cursor: edge.cursor,
392
+ node: {
393
+ id: edge.node.id,
394
+ chainId: edge.node.chainId,
395
+ vaultAddress: edge.node.vaultAddress,
396
+ protocol: edge.node.protocol,
397
+ beaconName: edge.node.beaconName,
398
+ protocolBaseType: edge.node.protocolBaseType,
399
+ name: edge.node.name || '',
400
+ feeApr: edge.node.feeApr || undefined,
401
+ stakingApr: edge.node.stakingApr || undefined,
402
+ merklApr: edge.node.merklApr || undefined,
403
+ pool: {
404
+ id: edge.node.pool?.id || '',
405
+ poolAddress: edge.node.pool?.poolAddress || '',
406
+ feeTier: edge.node.pool?.feeTier || '',
407
+ tick: undefined, // Not available in API response
408
+ liquidity: undefined, // Not available in API response
409
+ volumeUSD: undefined, // Not available in API response
410
+ totalValueLockedUSD: undefined // Not available in API response
411
+ },
412
+ token0: {
413
+ id: edge.node.token0?.id || '',
414
+ symbol: edge.node.token0?.symbol || '',
415
+ name: edge.node.token0?.name || '',
416
+ decimals: edge.node.token0?.decimals || 0,
417
+ address: edge.node.token0?.address || '',
418
+ chainId: edge.node.token0?.chainId || 0
419
+ },
420
+ token1: {
421
+ id: edge.node.token1?.id || '',
422
+ symbol: edge.node.token1?.symbol || '',
423
+ name: edge.node.token1?.name || '',
424
+ decimals: edge.node.token1?.decimals || 0,
425
+ address: edge.node.token1?.address || '',
426
+ chainId: edge.node.token1?.chainId || 0
427
+ }
428
+ }
429
+ })),
430
+ pageInfo: {
431
+ hasNextPage: response.data.vaults.pageInfo.hasNextPage,
432
+ endCursor: response.data.vaults.pageInfo.endCursor ?? null
433
+ },
434
+ totalCount: response.data.vaults.totalCount
435
+ };
436
+ return {
437
+ data: transformedData,
438
+ status: response.status,
439
+ success: true
440
+ };
441
+ }
442
+ catch (error) {
443
+ console.warn('API client failed:', error);
444
+ return {
445
+ data: null,
446
+ status: 500,
447
+ success: false,
448
+ error: error instanceof Error ? error.message : 'API request failed'
449
+ };
450
+ }
451
+ }
452
+ /**
453
+ * Merges vault results from API and subgraph, removing duplicates
454
+ * Prioritizes API data when duplicates are found
455
+ * Generates consistent cursors for the merged dataset
456
+ * @private
457
+ */
458
+ mergeVaultResults(apiVaults, subgraphVaults) {
459
+ const vaultMap = new Map();
460
+ // Add API vaults first (they take priority)
461
+ apiVaults.forEach(edge => {
462
+ const key = edge.node.vaultAddress.toLowerCase();
463
+ vaultMap.set(key, edge);
464
+ });
465
+ // Add subgraph vaults only if not already present
466
+ subgraphVaults.forEach(edge => {
467
+ const key = edge.node.vaultAddress.toLowerCase();
468
+ if (!vaultMap.has(key)) {
469
+ vaultMap.set(key, edge);
470
+ }
471
+ else {
472
+ // Merge additional data from subgraph if available (like pool details)
473
+ const existing = vaultMap.get(key);
474
+ const merged = {
475
+ ...existing,
476
+ node: {
477
+ ...existing.node,
478
+ // Merge pool data - prefer subgraph data for pool details if API doesn't have it
479
+ pool: {
480
+ id: existing.node.pool.id || edge.node.pool.id,
481
+ poolAddress: existing.node.pool.poolAddress || edge.node.pool.poolAddress,
482
+ feeTier: existing.node.pool.feeTier || edge.node.pool.feeTier,
483
+ tick: existing.node.pool.tick || edge.node.pool.tick,
484
+ liquidity: existing.node.pool.liquidity || edge.node.pool.liquidity,
485
+ volumeUSD: existing.node.pool.volumeUSD || edge.node.pool.volumeUSD,
486
+ totalValueLockedUSD: existing.node.pool.totalValueLockedUSD || edge.node.pool.totalValueLockedUSD
487
+ },
488
+ // Prefer API APR data, but use subgraph if API doesn't have it
489
+ feeApr: existing.node.feeApr ?? edge.node.feeApr,
490
+ stakingApr: existing.node.stakingApr ?? edge.node.stakingApr,
491
+ merklApr: existing.node.merklApr ?? edge.node.merklApr,
492
+ // Subgraph-only fields: always take from subgraph when available
493
+ positions: existing.node.positions ?? edge.node.positions,
494
+ tickRange: existing.node.tickRange ?? edge.node.tickRange,
495
+ fees: existing.node.fees ?? edge.node.fees,
496
+ }
497
+ };
498
+ vaultMap.set(key, merged);
264
499
  }
265
- catch (apiError) {
266
- console.warn('API client failed, falling back to subgraph:', apiError);
500
+ });
501
+ // Convert to array and regenerate cursors for consistent pagination
502
+ const mergedArray = Array.from(vaultMap.values());
503
+ return mergedArray.map((edge, index) => ({
504
+ ...edge,
505
+ cursor: `merged_${edge.node.chainId}_${index}_${edge.node.vaultAddress.toLowerCase()}`
506
+ }));
507
+ }
508
+ /**
509
+ * Applies pagination to vault results
510
+ * @private
511
+ */
512
+ paginateVaults(vaults, first, after) {
513
+ let startIndex = 0;
514
+ // If cursor is provided, find the starting position
515
+ if (after) {
516
+ const cursorIndex = vaults.findIndex(edge => edge.cursor === after);
517
+ if (cursorIndex !== -1) {
518
+ startIndex = cursorIndex + 1;
267
519
  }
268
520
  }
269
- // Fallback to subgraph if API fails or returns no data
521
+ // Get the slice of vaults for this page
522
+ const paginatedEdges = vaults.slice(startIndex, startIndex + first);
523
+ const hasNextPage = startIndex + first < vaults.length;
524
+ const endCursor = paginatedEdges.length > 0 ? paginatedEdges[paginatedEdges.length - 1].cursor : null;
525
+ return {
526
+ edges: paginatedEdges,
527
+ pageInfo: {
528
+ hasNextPage,
529
+ endCursor
530
+ },
531
+ totalCount: vaults.length
532
+ };
533
+ }
534
+ /**
535
+ * Fetches ALL vaults from subgraph (no pagination)
536
+ * @param filter - Optional filter criteria
537
+ * @returns Promise resolving to all vaults data from subgraph
538
+ * @private
539
+ */
540
+ async getAllVaultsFromSubgraph(filter) {
270
541
  try {
271
- return await this.getVaultsFromSubgraph(filter, first, after);
542
+ // Extract chainId from filter
543
+ const chainId = filter?.chainId;
544
+ if (!chainId) {
545
+ throw new Error('ChainId is required for subgraph');
546
+ }
547
+ // Get chain enum from chainId
548
+ const chain = (0, const_1.chainIdToName)(chainId);
549
+ if (!chain) {
550
+ throw new Error(`Unsupported chainId: ${chainId}`);
551
+ }
552
+ // Get subgraph URL for this chain
553
+ const subgraphUrl = subgraph_1.steerSubgraphConfig[chain];
554
+ if (!subgraphUrl) {
555
+ throw new Error(`No subgraph configured for chain: ${chain}`);
556
+ }
557
+ const beaconNames = this.getProtocolBeaconNames(filter?.protocol);
558
+ if (filter?.beaconName) {
559
+ beaconNames.push(filter.beaconName);
560
+ }
561
+ const uniqueBeaconNames = [...new Set(beaconNames.filter(name => name.length > 0))];
562
+ // Fetch all vaults from subgraph
563
+ const subgraphVaults = await this.subgraphVaultClient.getAllVaultsFromSubgraph({
564
+ subgraphUrl,
565
+ chainId,
566
+ showDeprecated: false,
567
+ showCurrentProtocol: uniqueBeaconNames.length > 0,
568
+ beaconNames: uniqueBeaconNames
569
+ });
570
+ // Get all supported protocols for this chain
571
+ const supportedProtocols = (0, protocol_1.getProtocolsForChainId)(chainId, this.subgraphStudioKey);
572
+ // Fetch APR data for all protocols in parallel
573
+ const aprPromises = supportedProtocols.map(protocol => this.getAprs({ chainId, protocol }).catch(error => {
574
+ console.warn(`Failed to fetch APR for protocol ${protocol}:`, error);
575
+ return { success: false, data: null };
576
+ }));
577
+ const aprResults = await Promise.all(aprPromises);
578
+ // Create a map of vault address to APR for quick lookup
579
+ const aprMap = new Map();
580
+ aprResults && aprResults.forEach(aprResult => {
581
+ if (aprResult.success && aprResult.data) {
582
+ aprResult?.data?.vaults && aprResult.data.vaults.forEach(vaultApr => {
583
+ aprMap.set(vaultApr.vaultAddress.toLowerCase(), vaultApr.apr.apr);
584
+ });
585
+ }
586
+ });
587
+ // Transform to VaultEdge array with APR data
588
+ const vaultEdges = subgraphVaults.map((vault, index) => {
589
+ // Parse positions and compute tick range
590
+ const positions = vault.positions?.map(pos => ({
591
+ id: pos.id,
592
+ upperTick: parseInt(pos.upperTick),
593
+ lowerTick: parseInt(pos.lowerTick),
594
+ relativeWeight: pos.relativeWeight
595
+ }));
596
+ // Compute tick range from positions
597
+ let tickRange;
598
+ if (positions && positions.length > 0) {
599
+ const lowerTicks = positions.map(p => p.lowerTick);
600
+ const upperTicks = positions.map(p => p.upperTick);
601
+ tickRange = {
602
+ minLowerTick: Math.min(...lowerTicks),
603
+ maxUpperTick: Math.max(...upperTicks)
604
+ };
605
+ }
606
+ return {
607
+ cursor: `subgraph_${chainId}_${index}`,
608
+ node: {
609
+ id: vault.id,
610
+ chainId: chainId,
611
+ vaultAddress: vault.id,
612
+ protocol: vault.beaconName || '',
613
+ beaconName: vault.beaconName || '',
614
+ protocolBaseType: vault.beaconName || '',
615
+ name: `${vault.token0Symbol}/${vault.token1Symbol}`,
616
+ feeApr: aprMap.get(vault.id.toLowerCase()),
617
+ stakingApr: undefined,
618
+ merklApr: undefined,
619
+ positions,
620
+ tickRange,
621
+ fees: vault.fees0 && vault.fees1 ? {
622
+ fees0: vault.fees0,
623
+ fees1: vault.fees1
624
+ } : undefined,
625
+ pool: {
626
+ id: vault.pool || '',
627
+ poolAddress: vault.pool || '',
628
+ feeTier: vault.feeTier || '',
629
+ tick: undefined,
630
+ liquidity: undefined,
631
+ volumeUSD: undefined,
632
+ totalValueLockedUSD: undefined
633
+ },
634
+ token0: {
635
+ id: vault.token0,
636
+ symbol: vault.token0Symbol,
637
+ name: vault.token0Symbol, // Use symbol as name since subgraph doesn't provide name
638
+ decimals: parseInt(vault.token0Decimals) || 18,
639
+ address: vault.token0,
640
+ chainId: chainId
641
+ },
642
+ token1: {
643
+ id: vault.token1,
644
+ symbol: vault.token1Symbol,
645
+ name: vault.token1Symbol, // Use symbol as name since subgraph doesn't provide name
646
+ decimals: parseInt(vault.token1Decimals) || 18,
647
+ address: vault.token1,
648
+ chainId: chainId
649
+ }
650
+ }
651
+ };
652
+ });
653
+ return {
654
+ data: vaultEdges,
655
+ status: 200,
656
+ success: true
657
+ };
272
658
  }
273
- catch (subgraphError) {
274
- console.error('Both API and subgraph failed:', subgraphError);
659
+ catch (error) {
660
+ console.error('Subgraph vault fetch failed:', error);
275
661
  return {
276
662
  data: null,
277
663
  status: 500,
278
664
  success: false,
279
- error: subgraphError instanceof Error ? subgraphError.message : 'Both API and subgraph requests failed'
665
+ error: error instanceof Error ? error.message : 'Failed to fetch vaults from subgraph'
280
666
  };
281
667
  }
282
668
  }
283
669
  /**
284
- * Fallback method to fetch vaults from subgraph
670
+ * Fallback method to fetch vaults from subgraph with pagination
285
671
  * @param filter - Optional filter criteria
286
672
  * @param first - Number of items to fetch (default: 50)
287
673
  * @param after - Cursor for pagination (null for first page)
@@ -305,24 +691,18 @@ class VaultClient extends SubgraphClient_js_1.SubgraphClient {
305
691
  if (!subgraphUrl) {
306
692
  throw new Error(`No subgraph configured for chain: ${chain}`);
307
693
  }
308
- const beaconNames = [];
309
- if (filter?.protocol) {
310
- const beacon = (0, const_1.getBeaconNameByProtocol)(filter.protocol);
311
- beaconNames.push(beacon);
312
- if (filter?.protocol === chain_1.Protocol.Blackhole) {
313
- beaconNames.push(chain_1.MultiPositionManagers.MultiPositionBlackholeOld);
314
- }
315
- }
694
+ const beaconNames = this.getProtocolBeaconNames(filter?.protocol);
316
695
  if (filter?.beaconName) {
317
696
  beaconNames.push(filter.beaconName);
318
697
  }
698
+ const uniqueBeaconNames = [...new Set(beaconNames.filter(name => name.length > 0))];
319
699
  // Fetch all vaults from subgraph
320
700
  const subgraphVaults = await this.subgraphVaultClient.getAllVaultsFromSubgraph({
321
701
  subgraphUrl,
322
702
  chainId,
323
703
  showDeprecated: false,
324
- showCurrentProtocol: false,
325
- beaconNames: beaconNames
704
+ showCurrentProtocol: uniqueBeaconNames.length > 0,
705
+ beaconNames: uniqueBeaconNames
326
706
  });
327
707
  // Get all supported protocols for this chain
328
708
  const supportedProtocols = (0, protocol_1.getProtocolsForChainId)(chainId, this.subgraphStudioKey);
@@ -353,8 +733,9 @@ class VaultClient extends SubgraphClient_js_1.SubgraphClient {
353
733
  }
354
734
  }));
355
735
  }
736
+ const filteredVaultsConnection = this.applyProtocolFilter(vaultsConnection, filter?.protocol);
356
737
  return {
357
- data: vaultsConnection,
738
+ data: filteredVaultsConnection,
358
739
  status: 200,
359
740
  success: true
360
741
  };