@moonwell-fi/moonwell-sdk
Version:
TypeScript Interface for Moonwell
162 lines (141 loc) • 4.58 kB
text/typescript
import type { MoonwellClient } from "../../../client/createMoonwellClient.js";
import {
applyGranularity,
calculateTimeRange,
getEnvironmentFromArgs,
toApiGranularity,
} from "../../../common/index.js";
import type {
MorphoVaultParameterType,
NetworkParameterType,
} from "../../../common/types.js";
import type { Chain } from "../../../environments/index.js";
import type { MorphoVaultSnapshot } from "../../../types/morphoVault.js";
import {
fetchVaultSnapshotsFromIndexer,
getV1VaultKey,
transformVaultSnapshotsFromIndexer,
} from "./lunarIndexerTransform.js";
export type GetMorphoVaultSnapshotsParameters<
environments,
network extends Chain | undefined,
> = NetworkParameterType<environments, network> &
MorphoVaultParameterType<network> & {
period?: "1M" | "3M" | "1Y" | "ALL";
startTime?: number;
endTime?: number;
};
export type GetMorphoVaultSnapshotsReturnType = Promise<MorphoVaultSnapshot[]>;
export async function getMorphoVaultSnapshots<
environments,
Network extends Chain | undefined,
>(
client: MoonwellClient,
args: GetMorphoVaultSnapshotsParameters<environments, Network>,
): GetMorphoVaultSnapshotsReturnType {
const environment = getEnvironmentFromArgs(client, args);
if (!environment) {
return [];
}
const {
vaultAddress: requestedVaultAddress,
period,
startTime: customStartTime,
endTime: customEndTime,
} = args as GetMorphoVaultSnapshotsParameters<environments, undefined>;
// For V2 vaults, fetch snapshots using the paired V1 address.
// Historical snapshots are indexed against V1 since that is where assets are held.
// The original (V2) address is preserved so that returned snapshots carry the
// address the caller requested, not the internal V1 address used for fetching.
let fetchAddress = requestedVaultAddress;
const matchedEntry = Object.entries(environment.config.vaults).find(
([, vaultConfig]) =>
environment.config.tokens[
vaultConfig.vaultToken as string
]?.address?.toLowerCase() === requestedVaultAddress.toLowerCase(),
);
if (matchedEntry !== undefined) {
const [matchedVaultKey] = matchedEntry;
const v1VaultKey = getV1VaultKey(environment, matchedVaultKey);
if (v1VaultKey) {
const v1Token = environment.config.tokens[v1VaultKey];
if (v1Token?.address) {
fetchAddress = v1Token.address;
}
}
}
const lunarIndexerUrl = environment.lunarIndexerUrl;
if (!lunarIndexerUrl) {
return [];
}
try {
const snapshots = await fetchVaultSnapshotsFromLunarIndexer(
fetchAddress,
environment.chainId,
lunarIndexerUrl,
period,
customStartTime,
customEndTime,
);
// Restore the originally-requested vault address on every snapshot so
// callers keying by address see the V2 address they asked for.
if (fetchAddress.toLowerCase() !== requestedVaultAddress.toLowerCase()) {
return snapshots.map((s) => ({
...s,
vaultAddress: requestedVaultAddress.toLowerCase(),
}));
}
return snapshots;
} catch (error) {
console.warn(
`[getMorphoVaultSnapshots] Lunar Indexer failed for chain ${environment.chainId}:`,
error,
);
environment.onError?.(error, {
source: "morpho-vault-snapshots",
chainId: environment.chainId,
});
return [];
}
}
async function fetchVaultSnapshotsFromLunarIndexer(
vaultAddress: string,
chainId: number,
lunarIndexerUrl: string,
period?: "1M" | "3M" | "1Y" | "ALL",
customStartTime?: number,
customEndTime?: number,
): Promise<MorphoVaultSnapshot[]> {
const vaultId = `${chainId}-${vaultAddress.toLowerCase()}`;
const allSnapshots: MorphoVaultSnapshot[] = [];
let cursor: string | null = null;
const MAX_PAGES = 100;
let page = 0;
const { startTime, endTime, granularity } = calculateTimeRange(
period,
customStartTime,
customEndTime,
);
do {
const response = await fetchVaultSnapshotsFromIndexer(
lunarIndexerUrl,
vaultId,
{
limit: 1000,
granularity: toApiGranularity(granularity),
startTime,
endTime,
...(cursor && { cursor }),
},
);
const transformed = transformVaultSnapshotsFromIndexer(
response.results,
chainId,
);
allSnapshots.push(...transformed);
cursor = response.nextCursor;
page++;
} while (cursor !== null && page < MAX_PAGES);
allSnapshots.sort((a, b) => a.timestamp - b.timestamp);
return applyGranularity(allSnapshots, granularity);
}