lending-apy-fetcher-ts
Version:
TypeScript library for fetching APYs from DeFi lending protocols
122 lines (102 loc) • 3.17 kB
text/typescript
import { Protocol, ApyData, ApyResult, GroupedApys, BestApyResult, ApyFetcherError } from './types';
export class ApyFetcher {
private protocols: Protocol[] = [];
constructor(protocols?: Protocol[]) {
this.initializeProtocols(protocols || []);
}
private initializeProtocols(protocols: Protocol[]): void {
this.protocols.push(...protocols);
}
/**
* Fetch APYs from all configured protocols
* @param gracefulDegradation - Continue with other protocols if one fails
*/
async fetchApys(gracefulDegradation: boolean = true): Promise<ApyData[]> {
const results = await this.fetchApysWithResults(gracefulDegradation);
// Aggregate all successful results
const allApys: ApyData[] = [];
for (const result of results) {
if (result.success && result.data) {
allApys.push(...result.data);
}
}
return allApys;
}
/**
* Fetch APYs with detailed results per protocol
*/
async fetchApysWithResults(gracefulDegradation: boolean = true): Promise<ApyResult[]> {
const promises = this.protocols.map(async (protocol): Promise<ApyResult> => {
try {
const data = await protocol.fetchApys();
return {
success: true,
data,
protocol: protocol.name,
};
} catch (error) {
const apyError = error instanceof ApyFetcherError
? error
: new ApyFetcherError(`Failed to fetch from ${protocol.name}`, protocol.name, error as Error);
if (!gracefulDegradation) {
throw apyError;
}
return {
success: false,
error: apyError,
protocol: protocol.name,
};
}
});
return Promise.all(promises);
}
/**
* Get APYs grouped by token symbol
*/
async getApysByToken(gracefulDegradation: boolean = true): Promise<GroupedApys> {
const apys = await this.fetchApys(gracefulDegradation);
const grouped: GroupedApys = {};
for (const apy of apys) {
if (!grouped[apy.token_symbol]) {
grouped[apy.token_symbol] = [];
}
grouped[apy.token_symbol].push(apy);
}
// Sort each group by APY descending
for (const symbol in grouped) {
grouped[symbol].sort((a, b) => b.apy - a.apy);
}
return grouped;
}
/**
* Get the best APY for a specific token across all protocols
*/
async getBestApyForToken(tokenSymbol: string, gracefulDegradation: boolean = true): Promise<BestApyResult | null> {
const apys = await this.fetchApys(gracefulDegradation);
const tokenApys = apys.filter(apy =>
apy.token_symbol.toLowerCase() === tokenSymbol.toLowerCase()
);
if (tokenApys.length === 0) {
return null;
}
// Sort by APY descending
tokenApys.sort((a, b) => b.apy - a.apy);
return {
token_symbol: tokenSymbol,
apy: tokenApys[0].apy,
network: tokenApys[0].network,
};
}
/**
* Get all available protocols
*/
getProtocols(): Protocol[] {
return [...this.protocols];
}
/**
* Add a custom protocol
*/
addProtocol(protocol: Protocol): void {
this.protocols.push(protocol);
}
}