diff --git a/dexs/levana/fetch.ts b/dexs/levana/fetch.ts index 80513d7df8..1af0d16da7 100644 --- a/dexs/levana/fetch.ts +++ b/dexs/levana/fetch.ts @@ -1,38 +1,16 @@ import BigNumber from "bignumber.js"; import fetchURL from "../../utils/fetchURL"; -import { ChainId, DateString, MarketAddr, TradeVolumeResp } from "./types"; -const INDEXER_URL = 'https://indexer.levana.finance'; - -export async function fetchVolume(marketAddrs: MarketAddr[], kind: "daily" | "cumulative", startDate: DateString, endDate: DateString) { - const api = kind === "daily" ? "trade-volume" : "cumulative-trade-volume"; - - const url = `${INDEXER_URL}/${api}?scope=daily&start_date=${startDate}&end_date=${endDate}`; - const resp: TradeVolumeResp = (await fetchURL(url)).data; - - if (!resp || !resp[startDate]) { - return 0; - } +import { MarketInfo} from "./types"; - const totalVolume = Object.entries(resp[startDate]).reduce((totalVolume, [marketAddr, volumePerMarket]) => { - return (marketAddrs.includes(marketAddr)) - ? totalVolume.plus(BigNumber(volumePerMarket)) - : totalVolume; - - }, BigNumber(0)) - - return totalVolume.toString(); -} +const INDEXER_URL = 'https://indexer.levana.finance'; -export async function fetchMarketAddrs(chainId: string) { - interface Market { - chain: string; - contract: string; - } +export async function fetchVolume(kind: "daily" | "total", marketInfos: MarketInfo[], timestampSeconds: number) { + const marketsStr = marketInfos.map(({addr}) => `market=${addr}`).join("&"); + const timestamp = new Date(timestampSeconds * 1000).toISOString(); + // it's either 1 day back or "all the days" back + const intervalDays = kind === "daily" ? 1 : Math.floor(timestampSeconds / (24 * 60 * 60)); - const url = `${INDEXER_URL}/markets`; - const markets: [Market] = (await fetchURL(url))?.data; + const url = `${INDEXER_URL}/rolling_trade_volume?${marketsStr}×tamp=${timestamp}&interval_days=${intervalDays}` - return markets - .filter(market => chainId === market.chain) - .map(market => market.contract); -} + return new BigNumber((await fetchURL(url)).data); +} \ No newline at end of file diff --git a/dexs/levana/index.ts b/dexs/levana/index.ts index d14820affd..2b481d0979 100644 --- a/dexs/levana/index.ts +++ b/dexs/levana/index.ts @@ -1,19 +1,19 @@ -import {Adapter, BaseAdapter, FetchResultVolume, SimpleAdapter} from "../../adapters/types"; -import {getTimestampAtStartOfDayUTC, getTimestampAtStartOfPreviousDayUTC} from "../../utils/date"; -import { fetchMarketAddrs, fetchVolume } from "./fetch"; -import { ChainId } from "./types"; -import { dateStr } from "./utils"; +import {FetchResultVolume, SimpleAdapter} from "../../adapters/types"; +import { fetchVolume } from "./fetch"; +import { queryMarketInfos } from "./query"; const adapter: SimpleAdapter = { - // each of these is the time of factory instantiation + // start times are factory instantiation adapter: { osmosis: { fetch: async (timestamp: number): Promise => { - const marketAddrs = await fetchMarketAddrs("osmosis-1"); - const [totalVolume, dailyVolume] = await Promise.all([ - getTotalVolume(marketAddrs, timestamp), - getDailyVolume(marketAddrs, timestamp) + const marketInfos = await queryMarketInfos({chain: "osmosis"}); + + const [dailyVolume, totalVolume] = await Promise.all([ + fetchVolume("daily", marketInfos, timestamp), + fetchVolume("total", marketInfos, timestamp) ]); + return { timestamp, dailyVolume: dailyVolume.toString(), @@ -24,11 +24,13 @@ const adapter: SimpleAdapter = { }, sei: { fetch: async (timestamp: number): Promise => { - const marketAddrs = await fetchMarketAddrs("pacific-1"); - const [totalVolume, dailyVolume] = await Promise.all([ - getTotalVolume(marketAddrs, timestamp), - getDailyVolume(marketAddrs, timestamp) + const marketInfos = await queryMarketInfos({chain: "sei"}); + + const [dailyVolume, totalVolume] = await Promise.all([ + fetchVolume("daily", marketInfos, timestamp), + fetchVolume("total", marketInfos, timestamp) ]); + return { timestamp, dailyVolume: dailyVolume.toString(), @@ -39,11 +41,13 @@ const adapter: SimpleAdapter = { }, injective: { fetch: async (timestamp: number): Promise => { - const marketAddrs = await fetchMarketAddrs("injective-1"); - const [totalVolume, dailyVolume] = await Promise.all([ - getTotalVolume(marketAddrs, timestamp), - getDailyVolume(marketAddrs, timestamp) + const marketInfos = await queryMarketInfos({chain: "injective"}); + + const [dailyVolume, totalVolume] = await Promise.all([ + fetchVolume("daily", marketInfos, timestamp), + fetchVolume("total", marketInfos, timestamp) ]); + return { timestamp, dailyVolume: dailyVolume.toString(), @@ -55,19 +59,4 @@ const adapter: SimpleAdapter = { } } - -async function getTotalVolume(marketAddrs: string[], timestamp: number) { - const startDate = dateStr(getTimestampAtStartOfPreviousDayUTC(timestamp)) - const endDate = dateStr(getTimestampAtStartOfDayUTC(timestamp)); - - return fetchVolume(marketAddrs, "cumulative", startDate, endDate); -} - -async function getDailyVolume(marketAddrs: string[], timestamp: number) { - const startDate = dateStr(getTimestampAtStartOfPreviousDayUTC(timestamp)) - const endDate = dateStr(getTimestampAtStartOfDayUTC(timestamp)); - return fetchVolume(marketAddrs, "daily", startDate, endDate); -} - - export default adapter; diff --git a/dexs/levana/query.ts b/dexs/levana/query.ts new file mode 100644 index 0000000000..82f2e4e771 --- /dev/null +++ b/dexs/levana/query.ts @@ -0,0 +1,64 @@ +import { MarketInfo} from "./types"; +import fetchURL from "../../utils/fetchURL"; +import { Chain, queryContract } from "./utils"; + +const factories:Record = { + osmosis: "osmo1ssw6x553kzqher0earlkwlxasfm2stnl3ms3ma2zz4tnajxyyaaqlucd45", + sei: "sei18rdj3asllguwr6lnyu2sw8p8nut0shuj3sme27ndvvw4gakjnjqqper95h", + injective: "inj1vdu3s39dl8t5l88tyqwuhzklsx9587adv8cnn9" +} + +export async function queryMarketInfos({chain}:{chain: Chain}):Promise { + interface MarketsResp { + markets: string[] + } + interface MarketInfoResp { + market_addr: string, + position_token: string, + liquidity_token_lp: string, + liquidity_token_xlp: string, + } + const factoryAddr = factories[chain] + + const marketIds = [] + while(true) { + const resp:MarketsResp = await queryContract({ + chain, + contract: factoryAddr, + msg: { + markets: { + start_after: marketIds.length ? marketIds[marketIds.length-1] : undefined + } + } + }) + + if(!resp || !resp.markets) { + throw new Error(`failed to get market addresses on chain ${chain}`); + } + + if(!resp.markets.length) { + break; + } + + marketIds.push(...resp.markets); + } + + const queryMarketInfo = (marketId:string) => queryContract({ + chain, + contract: factoryAddr, + msg: { + market_info: { + market_id: marketId + } + } + }).then((resp:MarketInfoResp) => ({ + id: marketId, + addr: resp.market_addr, + positionTokenAddr: resp.position_token, + liquidityTokenLpAddr: resp.liquidity_token_lp, + liquidityTokenXlpAddr: resp.liquidity_token_xlp, + })) + + return await Promise.all(marketIds.map(queryMarketInfo)) + +} diff --git a/dexs/levana/types.ts b/dexs/levana/types.ts index 13b248345a..fd86589228 100644 --- a/dexs/levana/types.ts +++ b/dexs/levana/types.ts @@ -1,6 +1,7 @@ -export type TradeVolumeResp = Record>; - -export type ChainId = string; -export type MarketAddr = string; -export type VolumeString = string; -export type DateString = string; \ No newline at end of file +export interface MarketInfo { + id: string, + addr: string, + positionTokenAddr: string, + liquidityTokenLpAddr: string, + liquidityTokenXlpAddr: string, +} \ No newline at end of file diff --git a/dexs/levana/utils.ts b/dexs/levana/utils.ts index af9fa0e08e..8f86924b4e 100644 --- a/dexs/levana/utils.ts +++ b/dexs/levana/utils.ts @@ -1,4 +1,21 @@ -export function dateStr(timestamp: number): string { - const date = new Date(timestamp * 1000) - return `${date.getUTCFullYear()}-${date.getUTCMonth() + 1}-${date.getUTCDate()}` +import fetchURL from "../../utils/fetchURL"; + +// These utils were adapted from the DefiLlama-Adapters repo +export type Chain = "osmosis" | "injective" | "sei" + +const endpoints:Record = { + osmosis: "https://osmosis-api.polkachu.com", + injective: "https://lcd-injective.whispernode.com:443", + sei: "https://sei-api.polkachu.com", +}; + +export async function queryContract({contract, chain, msg}:{contract: string, chain: Chain, msg: T}) { + const data = Buffer.from(JSON.stringify(msg)).toString("base64"); + + const endpoint = endpoints[chain] + return ( + await fetchURL( + `${endpoint}/cosmwasm/wasm/v1/contract/${contract}/smart/${data}` + ) + ).data.data; } \ No newline at end of file