@moonwell-fi/moonwell-sdk
Version:
TypeScript Interface for Moonwell
237 lines (211 loc) • 6.85 kB
text/typescript
import type { Address } from "viem";
import type { MoonwellClient } from "../../../client/createMoonwellClient.js";
import { Amount } from "../../../common/amount.js";
import { getEnvironmentFromArgs } from "../../../common/index.js";
import type {
MorphoMarketParameterType,
NetworkParameterType,
} from "../../../common/types.js";
import type { Chain } from "../../../environments/index.js";
import { fetchIsolatedMarketSnapshots } from "../../core/markets/getMarketSnapshots.js";
import { fetchMarketSnapshotsFromIndexer } from "./lunarIndexerTransform.js";
export type MarketSnapshot = {
timestamp: number;
blockNumber: bigint;
totalSupplyAssets: Amount;
totalBorrowAssets: Amount;
totalLiquidity: Amount;
totalReallocatableLiquidity: Amount;
totalSupplyAssetsUsd: number;
totalBorrowAssetsUsd: number;
totalLiquidityUsd: number;
totalReallocatableLiquidityUsd: number;
loanTokenPrice: number;
collateralTokenPrice: number;
supplyApy: number;
borrowApy: number;
lltv: number;
};
export type GetMarketSnapshotsParameters<
environments,
network extends Chain | undefined,
> = NetworkParameterType<environments, network> &
MorphoMarketParameterType<network> & {
startTime?: number;
endTime?: number;
limit?: number;
cursor?: string;
granularity?: "1h" | "6h" | "1d";
};
export type GetMarketSnapshotsReturnType = Promise<{
snapshots: MarketSnapshot[];
nextCursor?: string;
}>;
export async function getMarketSnapshots<
environments,
Network extends Chain | undefined,
>(
client: MoonwellClient,
args: GetMarketSnapshotsParameters<environments, Network>,
): GetMarketSnapshotsReturnType {
const environment = getEnvironmentFromArgs(client, args);
if (!environment) {
throw new Error("Environment not found");
}
// Resolve marketId and token config before the URL check so they're available
// for both the indexer path and the fallback path.
let { marketId, market } = args as unknown as {
marketId: Address;
market: string;
};
if (!marketId) {
marketId = environment.config.morphoMarkets[market].id;
}
const marketConfig = Object.values(environment.config.morphoMarkets).find(
(m) => m.id.toLowerCase() === marketId.toLowerCase(),
);
if (!marketConfig) {
throw new Error(`Market ${marketId} not found in configuration`);
}
const loanToken = environment.config.tokens[marketConfig.loanToken];
const lunarIndexerUrl = environment.lunarIndexerUrl;
if (!lunarIndexerUrl) {
// Fallback to Blue API → subgraph via the core fetchIsolatedMarketSnapshots.
// blockNumber is not available from these paths; set to 0n as a sentinel value.
const coreSnapshots = await fetchIsolatedMarketSnapshots(
marketId,
environment,
undefined,
args.startTime,
args.endTime,
);
const snapshots: MarketSnapshot[] = coreSnapshots.map((s) => ({
timestamp: s.timestamp,
blockNumber: 0n,
totalSupplyAssets: new Amount(
BigInt(Math.round(s.totalSupply * 10 ** loanToken.decimals)),
loanToken.decimals,
),
totalBorrowAssets: new Amount(
BigInt(Math.round(s.totalBorrows * 10 ** loanToken.decimals)),
loanToken.decimals,
),
totalLiquidity: new Amount(
BigInt(Math.round(s.totalLiquidity * 10 ** loanToken.decimals)),
loanToken.decimals,
),
totalReallocatableLiquidity: new Amount(
BigInt(
Math.round(s.totalReallocatableLiquidity * 10 ** loanToken.decimals),
),
loanToken.decimals,
),
totalSupplyAssetsUsd: s.totalSupplyUsd,
totalBorrowAssetsUsd: s.totalBorrowsUsd,
totalLiquidityUsd: s.totalLiquidityUsd,
totalReallocatableLiquidityUsd: s.totalReallocatableLiquidityUsd,
loanTokenPrice: s.loanTokenPrice,
collateralTokenPrice: s.collateralTokenPrice,
supplyApy: s.baseSupplyApy,
borrowApy: s.baseBorrowApy,
lltv: 0,
}));
return { snapshots };
}
// Build options object with only defined values
const options: {
startTime?: number;
endTime?: number;
limit?: number;
cursor?: string;
granularity?: "1h" | "6h" | "1d";
} = {};
if (args.startTime !== undefined) {
options.startTime = args.startTime;
}
if (args.endTime !== undefined) {
options.endTime = args.endTime;
}
if (args.limit !== undefined) {
options.limit = args.limit;
}
if (args.cursor !== undefined) {
options.cursor = args.cursor;
}
if (args.granularity !== undefined) {
options.granularity = args.granularity;
}
const response = await fetchMarketSnapshotsFromIndexer(
lunarIndexerUrl,
environment.chainId,
marketId,
options,
);
// Transform snapshots
const snapshots: MarketSnapshot[] = response.results.map((snapshot) => {
const totalSupplyAssets = new Amount(
BigInt(
Math.floor(
Number.parseFloat(snapshot.totalSupplyAssets) *
10 ** loanToken.decimals,
),
),
loanToken.decimals,
);
const totalBorrowAssets = new Amount(
BigInt(
Math.floor(
Number.parseFloat(snapshot.totalBorrowAssets) *
10 ** loanToken.decimals,
),
),
loanToken.decimals,
);
const totalLiquidity = new Amount(
BigInt(
Math.floor(
Number.parseFloat(snapshot.totalLiquidity) * 10 ** loanToken.decimals,
),
),
loanToken.decimals,
);
const totalReallocatableLiquidity = new Amount(
BigInt(
Math.floor(
Number.parseFloat(snapshot.totalReallocatableLiquidity ?? "0") *
10 ** loanToken.decimals,
),
),
loanToken.decimals,
);
return {
timestamp: snapshot.timestamp,
blockNumber: BigInt(snapshot.blockNumber),
totalSupplyAssets,
totalBorrowAssets,
totalLiquidity,
totalReallocatableLiquidity,
totalSupplyAssetsUsd: Number.parseFloat(snapshot.totalSupplyAssetsUsd),
totalBorrowAssetsUsd: Number.parseFloat(snapshot.totalBorrowAssetsUsd),
totalLiquidityUsd: Number.parseFloat(snapshot.totalLiquidityUsd),
totalReallocatableLiquidityUsd: Number.parseFloat(
snapshot.totalReallocatableLiquidityUsd ?? "0",
),
loanTokenPrice: Number.parseFloat(snapshot.loanTokenPrice),
collateralTokenPrice: Number.parseFloat(snapshot.collateralTokenPrice),
supplyApy: Number.parseFloat(snapshot.supplyApy),
borrowApy: Number.parseFloat(snapshot.borrowApy),
lltv: Number.parseFloat(snapshot.lltv),
};
});
const result: {
snapshots: MarketSnapshot[];
nextCursor?: string;
} = {
snapshots,
};
if (response.nextCursor !== undefined) {
result.nextCursor = response.nextCursor;
}
return result;
}