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