@moonwell-fi/moonwell-sdk
Version:
TypeScript Interface for Moonwell
273 lines • 10.4 kB
JavaScript
import axios from "axios";
import dayjs from "dayjs";
import utc from "dayjs/plugin/utc.js";
import { Amount, getEnvironmentFromArgs, isStartOfDay, } from "../../../common/index.js";
import { getSubgraph } from "../../morpho/utils/graphql.js";
dayjs.extend(utc);
export async function getMarketSnapshots(client, args) {
const environment = getEnvironmentFromArgs(client, args);
if (!environment) {
return [];
}
if (args?.type === "core") {
return fetchCoreMarketSnapshots(args.marketId, environment);
}
else {
if (environment.custom.morpho?.minimalDeployment === false) {
return fetchIsolatedMarketSnapshots(args.marketId, environment);
}
else {
return fetchIsolatedMarketSnapshotsSubgraph(args.marketId, environment);
}
}
}
async function fetchCoreMarketSnapshots(marketAddress, environment) {
const dailyData = [];
let hasNextPage = true;
let endCursor;
while (hasNextPage) {
const result = await axios.post(environment.indexerUrl, {
query: `
query {
marketDailySnapshots (
limit: 1000,
orderBy: "timestamp"
orderDirection: "desc"
where: {marketAddress: "${marketAddress.toLowerCase()}", chainId: ${environment.chainId}}
${endCursor ? `after: "${endCursor}"` : ""}
) {
items {
totalBorrows
totalBorrowsUSD
totalSupplies
totalSuppliesUSD
totalLiquidity
totalLiquidityUSD
baseSupplyApy
baseBorrowApy
timestamp
}
pageInfo {
hasNextPage
endCursor
}
}
}
`,
});
dailyData.push(...result.data.data.marketDailySnapshots.items.filter((f) => isStartOfDay(f.timestamp)));
hasNextPage = result.data.data.marketDailySnapshots.pageInfo.hasNextPage;
endCursor = result.data.data.marketDailySnapshots.pageInfo.endCursor;
}
if (dailyData.length > 0) {
return dailyData.map((point) => {
const supplied = Number(point.totalSupplies);
const borrow = Number(point.totalBorrows);
const borrowUsd = Number(point.totalBorrowsUSD);
const suppliedUsd = Number(point.totalSuppliesUSD);
const liquidity = Math.max(point.totalLiquidity, 0);
const liquidityUsd = Math.max(point.totalLiquidityUSD, 0);
const price = suppliedUsd / supplied;
const result = {
marketId: marketAddress.toLowerCase(),
chainId: environment.chainId,
timestamp: point.timestamp * 1000,
totalSupply: supplied,
totalSupplyUsd: suppliedUsd,
totalBorrows: borrow,
totalBorrowsUsd: borrowUsd,
totalLiquidity: liquidity,
totalLiquidityUsd: liquidityUsd,
baseSupplyApy: point.baseSupplyApy,
baseBorrowApy: point.baseBorrowApy,
collateralTokenPrice: price,
loanTokenPrice: price,
};
return result;
});
}
else {
return [];
}
}
async function fetchMorphoGraphQL(query, operationName, variables) {
try {
const response = await axios.post("https://blue-api.morpho.org/graphql", { query: query, operationName, variables }, { timeout: 5000 });
if (response.status !== 200 || response.data.errors) {
console.log(`Non-200 (${response.statusText}
}) or other error from Morpho GraphQL! - ${JSON.stringify(response.data)}`);
return undefined;
}
return response.data.data;
}
catch (error) {
return undefined;
}
}
async function fetchIsolatedMarketSnapshots(marketAddress, environment) {
const operationName = "getMarketTotalTimeseries";
const variables = {
options: {
startTimestamp: dayjs.utc().subtract(1, "year").unix(),
interval: "DAY",
},
};
const query = `
query getMarketTotalTimeseries($options: TimeseriesOptions) {
marketTotalTimeseries: marketByUniqueKey(
chainId: ${environment.chainId}
uniqueKey: "${marketAddress.toLowerCase()}"
) {
uniqueKey
loanAsset {
priceUsd
decimals
}
collateralAsset {
priceUsd
decimals
}
historicalState {
supplyApy(options: $options) {
x
y
}
borrowApy(options: $options) {
x
y
}
borrowAssets(options: $options) {
x
y
}
borrowAssetsUsd(options: $options) {
x
y
}
supplyAssets(options: $options) {
x
y
}
supplyAssetsUsd(options: $options) {
x
y
}
liquidityAssets(options: $options) {
x
y
}
liquidityAssetsUsd(options: $options) {
x
y
}
}
}
}`;
const result = await fetchMorphoGraphQL(query, operationName, variables);
if (result) {
try {
const markets = result.marketTotalTimeseries.historicalState.borrowAssets.map((borrowAssets, index) => {
const loanDecimals = result.marketTotalTimeseries.loanAsset.decimals;
const borrowAssetsUsd = result.marketTotalTimeseries.historicalState.borrowAssetsUsd[index];
const supplyAssets = result.marketTotalTimeseries.historicalState.supplyAssets[index];
const supplyAssetsUsd = result.marketTotalTimeseries.historicalState.supplyAssetsUsd[index];
const liquidityAssets = result.marketTotalTimeseries.historicalState.liquidityAssets[index];
const liquidityAssetsUsd = result.marketTotalTimeseries.historicalState.liquidityAssetsUsd[index];
const supplyApy = result.marketTotalTimeseries.historicalState.supplyApy[index];
const borrowApy = result.marketTotalTimeseries.historicalState.borrowApy[index];
const collateralTokenPrice = result.marketTotalTimeseries.collateralAsset.priceUsd;
const loanTokenPrice = result.marketTotalTimeseries.loanAsset.priceUsd;
const totalSupply = (new Amount(BigInt(supplyAssets.y), Number(loanDecimals)).value *
loanTokenPrice) /
collateralTokenPrice;
const totalBorrows = new Amount(BigInt(borrowAssets.y), Number(loanDecimals)).value;
const totalLiquidity = (new Amount(BigInt(liquidityAssets.y), Number(loanDecimals))
.value *
loanTokenPrice) /
collateralTokenPrice;
return {
chainId: environment.chainId,
timestamp: borrowAssets.x * 1000,
marketId: marketAddress.toLowerCase(),
totalBorrows,
totalBorrowsUsd: Number(borrowAssetsUsd.y),
totalSupply,
totalSupplyUsd: Number(supplyAssetsUsd.y),
totalLiquidity,
totalLiquidityUsd: Number(liquidityAssetsUsd.y),
baseSupplyApy: supplyApy.y,
baseBorrowApy: borrowApy.y,
loanTokenPrice,
collateralTokenPrice,
};
});
return markets;
}
catch (ex) {
return [];
}
}
else {
return [];
}
}
async function fetchIsolatedMarketSnapshotsSubgraph(marketAddress, environment) {
const query = `
{
marketDailySnapshots (where:{market:"${marketAddress.toLowerCase()}"}, orderBy:timestamp, orderDirection:desc, first: 365) {
market {
maximumLTV
inputToken {
decimals
}
borrowedToken {
decimals
}
}
variableBorrowedTokenBalance
outputTokenPriceUSD
inputTokenPriceUSD
inputTokenBalance
timestamp
}
}
`;
const result = await getSubgraph(environment, query);
if (result) {
try {
const markets = result.marketDailySnapshots.map((item) => {
const supplyDecimals = item.market.borrowedToken.decimals;
const supplyAssets = item.inputTokenBalance;
const totalSupplyInLoanToken = new Amount(BigInt(supplyAssets), Number(supplyDecimals)).value;
const totalSupply = totalSupplyInLoanToken / Number(item.inputTokenPriceUSD);
const borrowAssets = item.variableBorrowedTokenBalance;
const totalBorrows = new Amount(BigInt(borrowAssets), Number(supplyDecimals)).value;
const totalLiquidityInLoanToken = Math.max(totalSupplyInLoanToken - totalBorrows, 0);
const totalLiquidity = totalLiquidityInLoanToken / Number(item.inputTokenPriceUSD);
return {
chainId: environment.chainId,
timestamp: item.timestamp * 1000,
marketId: marketAddress.toLowerCase(),
totalBorrows,
totalBorrowsUsd: Number(item.outputTokenPriceUSD) * totalBorrows,
totalSupply,
totalSupplyUsd: Number(item.inputTokenPriceUSD) * totalSupply,
totalLiquidity,
totalLiquidityUsd: Number(item.outputTokenPriceUSD) * totalLiquidityInLoanToken,
baseSupplyApy: 0,
baseBorrowApy: 0,
loanTokenPrice: Number(item.outputTokenPriceUSD),
collateralTokenPrice: Number(item.inputTokenPriceUSD),
};
});
return markets;
}
catch (ex) {
return [];
}
}
else {
return [];
}
}
//# sourceMappingURL=getMarketSnapshots.js.map