@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.
- package/dist/cjs/base/VaultClient.js +430 -78
- package/dist/cjs/base/VaultClient.js.map +1 -1
- package/dist/cjs/const/subgraph.js +1 -1
- package/dist/cjs/const/subgraph.js.map +1 -1
- package/dist/cjs/utils/SubgraphVaultClient.js +11 -1
- package/dist/cjs/utils/SubgraphVaultClient.js.map +1 -1
- package/dist/esm/base/VaultClient.js +431 -79
- package/dist/esm/base/VaultClient.js.map +1 -1
- package/dist/esm/const/subgraph.js +1 -1
- package/dist/esm/const/subgraph.js.map +1 -1
- package/dist/esm/utils/SubgraphVaultClient.js +11 -1
- package/dist/esm/utils/SubgraphVaultClient.js.map +1 -1
- package/dist/types/base/VaultClient.d.ts +43 -1
- package/dist/types/base/VaultClient.d.ts.map +1 -1
- package/dist/types/utils/SubgraphVaultClient.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/base/VaultClient.protocol-filter.test.ts +179 -0
- package/src/__tests__/base/VaultClient.test.ts +4 -3
- package/src/base/VaultClient.ts +520 -83
- package/src/const/subgraph.ts +1 -1
- package/src/utils/SubgraphVaultClient.ts +12 -1
|
@@ -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,359 @@ class VaultClient extends SubgraphClient_js_1.SubgraphClient {
|
|
|
198
286
|
* ```
|
|
199
287
|
*/
|
|
200
288
|
async getVaults(filter, first = 50, after) {
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
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:
|
|
343
|
+
data: null,
|
|
260
344
|
status: response.status,
|
|
261
|
-
success:
|
|
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
|
+
}
|
|
493
|
+
};
|
|
494
|
+
vaultMap.set(key, merged);
|
|
264
495
|
}
|
|
265
|
-
|
|
266
|
-
|
|
496
|
+
});
|
|
497
|
+
// Convert to array and regenerate cursors for consistent pagination
|
|
498
|
+
const mergedArray = Array.from(vaultMap.values());
|
|
499
|
+
return mergedArray.map((edge, index) => ({
|
|
500
|
+
...edge,
|
|
501
|
+
cursor: `merged_${edge.node.chainId}_${index}_${edge.node.vaultAddress.toLowerCase()}`
|
|
502
|
+
}));
|
|
503
|
+
}
|
|
504
|
+
/**
|
|
505
|
+
* Applies pagination to vault results
|
|
506
|
+
* @private
|
|
507
|
+
*/
|
|
508
|
+
paginateVaults(vaults, first, after) {
|
|
509
|
+
let startIndex = 0;
|
|
510
|
+
// If cursor is provided, find the starting position
|
|
511
|
+
if (after) {
|
|
512
|
+
const cursorIndex = vaults.findIndex(edge => edge.cursor === after);
|
|
513
|
+
if (cursorIndex !== -1) {
|
|
514
|
+
startIndex = cursorIndex + 1;
|
|
267
515
|
}
|
|
268
516
|
}
|
|
269
|
-
//
|
|
517
|
+
// Get the slice of vaults for this page
|
|
518
|
+
const paginatedEdges = vaults.slice(startIndex, startIndex + first);
|
|
519
|
+
const hasNextPage = startIndex + first < vaults.length;
|
|
520
|
+
const endCursor = paginatedEdges.length > 0 ? paginatedEdges[paginatedEdges.length - 1].cursor : null;
|
|
521
|
+
return {
|
|
522
|
+
edges: paginatedEdges,
|
|
523
|
+
pageInfo: {
|
|
524
|
+
hasNextPage,
|
|
525
|
+
endCursor
|
|
526
|
+
},
|
|
527
|
+
totalCount: vaults.length
|
|
528
|
+
};
|
|
529
|
+
}
|
|
530
|
+
/**
|
|
531
|
+
* Fetches ALL vaults from subgraph (no pagination)
|
|
532
|
+
* @param filter - Optional filter criteria
|
|
533
|
+
* @returns Promise resolving to all vaults data from subgraph
|
|
534
|
+
* @private
|
|
535
|
+
*/
|
|
536
|
+
async getAllVaultsFromSubgraph(filter) {
|
|
270
537
|
try {
|
|
271
|
-
|
|
538
|
+
// Extract chainId from filter
|
|
539
|
+
const chainId = filter?.chainId;
|
|
540
|
+
if (!chainId) {
|
|
541
|
+
throw new Error('ChainId is required for subgraph');
|
|
542
|
+
}
|
|
543
|
+
// Get chain enum from chainId
|
|
544
|
+
const chain = (0, const_1.chainIdToName)(chainId);
|
|
545
|
+
if (!chain) {
|
|
546
|
+
throw new Error(`Unsupported chainId: ${chainId}`);
|
|
547
|
+
}
|
|
548
|
+
// Get subgraph URL for this chain
|
|
549
|
+
const subgraphUrl = subgraph_1.steerSubgraphConfig[chain];
|
|
550
|
+
if (!subgraphUrl) {
|
|
551
|
+
throw new Error(`No subgraph configured for chain: ${chain}`);
|
|
552
|
+
}
|
|
553
|
+
const beaconNames = this.getProtocolBeaconNames(filter?.protocol);
|
|
554
|
+
if (filter?.beaconName) {
|
|
555
|
+
beaconNames.push(filter.beaconName);
|
|
556
|
+
}
|
|
557
|
+
const uniqueBeaconNames = [...new Set(beaconNames.filter(name => name.length > 0))];
|
|
558
|
+
// Fetch all vaults from subgraph
|
|
559
|
+
const subgraphVaults = await this.subgraphVaultClient.getAllVaultsFromSubgraph({
|
|
560
|
+
subgraphUrl,
|
|
561
|
+
chainId,
|
|
562
|
+
showDeprecated: false,
|
|
563
|
+
showCurrentProtocol: uniqueBeaconNames.length > 0,
|
|
564
|
+
beaconNames: uniqueBeaconNames
|
|
565
|
+
});
|
|
566
|
+
// Get all supported protocols for this chain
|
|
567
|
+
const supportedProtocols = (0, protocol_1.getProtocolsForChainId)(chainId, this.subgraphStudioKey);
|
|
568
|
+
// Fetch APR data for all protocols in parallel
|
|
569
|
+
const aprPromises = supportedProtocols.map(protocol => this.getAprs({ chainId, protocol }).catch(error => {
|
|
570
|
+
console.warn(`Failed to fetch APR for protocol ${protocol}:`, error);
|
|
571
|
+
return { success: false, data: null };
|
|
572
|
+
}));
|
|
573
|
+
const aprResults = await Promise.all(aprPromises);
|
|
574
|
+
// Create a map of vault address to APR for quick lookup
|
|
575
|
+
const aprMap = new Map();
|
|
576
|
+
aprResults && aprResults.forEach(aprResult => {
|
|
577
|
+
if (aprResult.success && aprResult.data) {
|
|
578
|
+
aprResult?.data?.vaults && aprResult.data.vaults.forEach(vaultApr => {
|
|
579
|
+
aprMap.set(vaultApr.vaultAddress.toLowerCase(), vaultApr.apr.apr);
|
|
580
|
+
});
|
|
581
|
+
}
|
|
582
|
+
});
|
|
583
|
+
// Transform to VaultEdge array with APR data
|
|
584
|
+
const vaultEdges = subgraphVaults.map((vault, index) => ({
|
|
585
|
+
cursor: `subgraph_${chainId}_${index}`,
|
|
586
|
+
node: {
|
|
587
|
+
id: vault.id,
|
|
588
|
+
chainId: chainId,
|
|
589
|
+
vaultAddress: vault.id,
|
|
590
|
+
protocol: vault.beaconName || '',
|
|
591
|
+
beaconName: vault.beaconName || '',
|
|
592
|
+
protocolBaseType: vault.beaconName || '',
|
|
593
|
+
name: `${vault.token0Symbol}/${vault.token1Symbol}`,
|
|
594
|
+
feeApr: aprMap.get(vault.id.toLowerCase()),
|
|
595
|
+
stakingApr: undefined,
|
|
596
|
+
merklApr: undefined,
|
|
597
|
+
pool: {
|
|
598
|
+
id: vault.pool || '',
|
|
599
|
+
poolAddress: vault.pool || '',
|
|
600
|
+
feeTier: vault.feeTier || '',
|
|
601
|
+
tick: undefined,
|
|
602
|
+
liquidity: undefined,
|
|
603
|
+
volumeUSD: undefined,
|
|
604
|
+
totalValueLockedUSD: undefined
|
|
605
|
+
},
|
|
606
|
+
token0: {
|
|
607
|
+
id: vault.token0,
|
|
608
|
+
symbol: vault.token0Symbol,
|
|
609
|
+
name: vault.token0Symbol, // Use symbol as name since subgraph doesn't provide name
|
|
610
|
+
decimals: parseInt(vault.token0Decimals) || 18,
|
|
611
|
+
address: vault.token0,
|
|
612
|
+
chainId: chainId
|
|
613
|
+
},
|
|
614
|
+
token1: {
|
|
615
|
+
id: vault.token1,
|
|
616
|
+
symbol: vault.token1Symbol,
|
|
617
|
+
name: vault.token1Symbol, // Use symbol as name since subgraph doesn't provide name
|
|
618
|
+
decimals: parseInt(vault.token1Decimals) || 18,
|
|
619
|
+
address: vault.token1,
|
|
620
|
+
chainId: chainId
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
}));
|
|
624
|
+
return {
|
|
625
|
+
data: vaultEdges,
|
|
626
|
+
status: 200,
|
|
627
|
+
success: true
|
|
628
|
+
};
|
|
272
629
|
}
|
|
273
|
-
catch (
|
|
274
|
-
console.error('
|
|
630
|
+
catch (error) {
|
|
631
|
+
console.error('Subgraph vault fetch failed:', error);
|
|
275
632
|
return {
|
|
276
633
|
data: null,
|
|
277
634
|
status: 500,
|
|
278
635
|
success: false,
|
|
279
|
-
error:
|
|
636
|
+
error: error instanceof Error ? error.message : 'Failed to fetch vaults from subgraph'
|
|
280
637
|
};
|
|
281
638
|
}
|
|
282
639
|
}
|
|
283
640
|
/**
|
|
284
|
-
* Fallback method to fetch vaults from subgraph
|
|
641
|
+
* Fallback method to fetch vaults from subgraph with pagination
|
|
285
642
|
* @param filter - Optional filter criteria
|
|
286
643
|
* @param first - Number of items to fetch (default: 50)
|
|
287
644
|
* @param after - Cursor for pagination (null for first page)
|
|
@@ -305,24 +662,18 @@ class VaultClient extends SubgraphClient_js_1.SubgraphClient {
|
|
|
305
662
|
if (!subgraphUrl) {
|
|
306
663
|
throw new Error(`No subgraph configured for chain: ${chain}`);
|
|
307
664
|
}
|
|
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
|
-
}
|
|
665
|
+
const beaconNames = this.getProtocolBeaconNames(filter?.protocol);
|
|
316
666
|
if (filter?.beaconName) {
|
|
317
667
|
beaconNames.push(filter.beaconName);
|
|
318
668
|
}
|
|
669
|
+
const uniqueBeaconNames = [...new Set(beaconNames.filter(name => name.length > 0))];
|
|
319
670
|
// Fetch all vaults from subgraph
|
|
320
671
|
const subgraphVaults = await this.subgraphVaultClient.getAllVaultsFromSubgraph({
|
|
321
672
|
subgraphUrl,
|
|
322
673
|
chainId,
|
|
323
674
|
showDeprecated: false,
|
|
324
|
-
showCurrentProtocol:
|
|
325
|
-
beaconNames:
|
|
675
|
+
showCurrentProtocol: uniqueBeaconNames.length > 0,
|
|
676
|
+
beaconNames: uniqueBeaconNames
|
|
326
677
|
});
|
|
327
678
|
// Get all supported protocols for this chain
|
|
328
679
|
const supportedProtocols = (0, protocol_1.getProtocolsForChainId)(chainId, this.subgraphStudioKey);
|
|
@@ -353,8 +704,9 @@ class VaultClient extends SubgraphClient_js_1.SubgraphClient {
|
|
|
353
704
|
}
|
|
354
705
|
}));
|
|
355
706
|
}
|
|
707
|
+
const filteredVaultsConnection = this.applyProtocolFilter(vaultsConnection, filter?.protocol);
|
|
356
708
|
return {
|
|
357
|
-
data:
|
|
709
|
+
data: filteredVaultsConnection,
|
|
358
710
|
status: 200,
|
|
359
711
|
success: true
|
|
360
712
|
};
|