From 3beefb40647b7f9f6df2ce8304b0e198e29dc5c6 Mon Sep 17 00:00:00 2001 From: Unique Divine <51418232+Unique-Divine@users.noreply.github.com> Date: Wed, 22 Feb 2023 23:28:45 -0600 Subject: [PATCH] feat(indexer-nibi): Add and test query for markPriceCandles (#99) * feat(indexer-nibi): updates mark price candle with passing integration test * test(indexer-nibi): improve test coverage and remove zombie code * increment version minor * docs,refactor(indexer-nibi): functions docs and code cleanup * docs: CHANGELOG --- packages/indexer-nibi/CHANGELOG.md | 4 + packages/indexer-nibi/QUERIES.md | 330 ++++++++++++++++++ packages/indexer-nibi/package.json | 2 +- packages/indexer-nibi/src/constant.ts | 9 - packages/indexer-nibi/src/enum.ts | 12 + packages/indexer-nibi/src/gql.ts | 76 ++++ .../indexer-nibi/src/heart-monitor.test.ts | 104 ++++-- packages/indexer-nibi/src/heart-monitor.ts | 112 ++---- packages/indexer-nibi/src/index.ts | 2 +- .../src/query/markPriceCandles.ts | 99 ++++++ packages/indexer-nibi/src/types.ts | 24 +- 11 files changed, 647 insertions(+), 127 deletions(-) create mode 100644 packages/indexer-nibi/QUERIES.md delete mode 100644 packages/indexer-nibi/src/constant.ts create mode 100644 packages/indexer-nibi/src/enum.ts create mode 100644 packages/indexer-nibi/src/gql.ts create mode 100644 packages/indexer-nibi/src/query/markPriceCandles.ts diff --git a/packages/indexer-nibi/CHANGELOG.md b/packages/indexer-nibi/CHANGELOG.md index bf3fc304..bf6c5a79 100644 --- a/packages/indexer-nibi/CHANGELOG.md +++ b/packages/indexer-nibi/CHANGELOG.md @@ -7,6 +7,10 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline - . +## v0.4.0 + +- [[#99]](https://github.com/NibiruChain/ts-sdk/pull/99) feat: add `markPriceCandles` query + ## v0.3.4 - [[#79]](https://github.com/NibiruChain/ts-sdk/pull/79) feat: add `CandleStickPeriod` enums diff --git a/packages/indexer-nibi/QUERIES.md b/packages/indexer-nibi/QUERIES.md new file mode 100644 index 00000000..051d3295 --- /dev/null +++ b/packages/indexer-nibi/QUERIES.md @@ -0,0 +1,330 @@ +# Queries + +- [Queries](#queries) + - [balances](#balances) + - [fundingRates](#fundingrates) + - [liquidations](#liquidations) + - [markPriceCandles](#markpricecandles) + - [`order: MarkPriceCandlesOrder`](#order-markpricecandlesorder) + - [markPrices](#markprices) + - [oraclePrices](#oracleprices) + - [positionChanges](#positionchanges) + - [positions](#positions) + - [statsVolume](#statsvolume) + - [transfers](#transfers) + - [txMessages](#txmessages) + - [txMessagesFailed](#txmessagesfailed) + - [unbondings](#unbondings) + - [validators](#validators) + - [vpoolConfigs](#vpoolconfigs) + + +## balances + +TODO docs + +```graphql +balances( + where: BalancesFilter + order: BalancesOrder + orderDesc: Boolean + limit: Int): [Balances] +``` + + +TODO docs + +```graphql +blockEvents( + where: BlockEventsFilter + order: BlockEventsOrder + orderDesc: Boolean + limit: Int): [BlockEvents] +``` + +TODO docs + +```graphql +blockEventsFailed( + where: BlockEventsFailedFilter + order: BlockEventsFailedOrder + orderDesc: Boolean + limit: Int): [BlockEventsFailed] +``` + +TODO docs + +```graphql +delegations( + where: DelegationsFilter + order: DelegationsOrder + orderDesc: Boolean + limit: Int): [Delegations] +``` + +#### Query Example - delegations + +```graphql +{ + # TODO missing fields: where, order + delegations(orderDesc: true, limit: 3) { + validatorAddress + delegatorAddress + shares + balance + block + blockTs + } +} +``` + +#### Response Example - delegations + +```json +{ + "data": { + "delegations": [ + { + "validatorAddress": "nibivaloper1d7zygazerfwx4l362tnpcp0ramzm97xvv9ryxr", + "delegatorAddress": "nibi1zaavvzxez0elundtn32qnk9lkm8kmcsz44g7xl", + "shares": 6, + "balance": "{\"amount\":6,\"denom\":\"unibi\"}", + "block": 791341, + "blockTs": "2023-02-22T22:12:18.507059+00:00" + }, + { + "validatorAddress": "nibivaloper1d7zygazerfwx4l362tnpcp0ramzm97xvv9ryxr", + "delegatorAddress": "nibi1d7zygazerfwx4l362tnpcp0ramzm97xv97dlj7", + "shares": 500000000, + "balance": "{\"amount\":500000000,\"denom\":\"unibi\"}", + "block": 791341, + "blockTs": "2023-02-22T22:12:18.507059+00:00" + }, + { + "validatorAddress": "nibivaloper1d7zygazerfwx4l362tnpcp0ramzm97xvv9ryxr", + "delegatorAddress": "nibi1jle8khj3aennq24zx6g93aam9rt0fqhgyp4h52", + "shares": 52, + "balance": "{\"amount\":52,\"denom\":\"unibi\"}", + "block": 791341, + "blockTs": "2023-02-22T22:12:18.507059+00:00" + } + ] + } +} +``` + +## fundingRates +TODO docs + +```graphql +fundingRates( + where: FundingRatesFilter + order: FundingRatesOrder + orderDesc: Boolean + limit: Int): [FundingRates] +``` +## liquidations +TODO docs + +```graphql +liquidations( + where: LiquidationsFilter + order: LiquidationsOrder + orderDesc: Boolean + limit: Int): [Liquidations] +``` +## markPriceCandles + +TODO docs + +```graphql +markPriceCandles( + where: MarkPriceCandlesFilter + order: MarkPriceCandlesOrder + orderDesc: Boolean + limit: Int): [MarkPriceCandles] +``` + +#### In English + +- The above code block means that there is a query function called `markPriceCandles` with four arguments, `where`, `order`, `orderDesc`, and `limit`, that returns `[MarkPriceCandles]`. +- The `[MarkPriceCandles]` type means that "list of `MarkPriceCandles` objects". + +#### `MarkPriceCandles` + +```graphql +type MarkPriceCandles { + pair: String + open: Int + close: Int + low: Int + high: Int + period: Float + periodStartTs: DateTime +} +``` + +ANKI `Boolean` in GraphQL means `true` or `false`. + +The `where` parameter has options for filtering based on conditions for each field. The filter condition must match the type of the corresponding field. Examples include: +```graphql +where: { pairEq: "ubtc:unusd" } +where: { lowGt: 100 } +where: { periodStartTsLt: "2023-02-22" } +``` + +Every field, e.g. `pair`, `open`, `close`, `period`, has filter options. In the case of `MarkPriceCandles`, every field has methods for: +- `=` : `Eq` : equals +- `>` : `Gt` : greater than +- `<` : `Lt` : less than +- `>=` : `Gte` : greater than or equal +- `<=` : `Lte` : less than or equal + +Except, `pair`, which only has the `pairEq` method. + + +### `order: MarkPriceCandlesOrder` + +```graphql +markPriceCandles( + where: MarkPriceCandlesFilter + order: MarkPriceCandlesOrder + orderDesc: Boolean + limit: Int): [MarkPriceCandles] +``` + + +```graphql +type MarkPriceCandlesOrder { + pair + open + close + low + high + period + period_start_ts +} +``` + +This means we can use one of these values with the `order` argument in the query like follows: +```graphql +order: pair +order: close +order: period_start_ts +``` + + +## markPrices + +TODO docs + +```graphql +markPrices( + where: MarkPricesFilter + order: MarkPricesOrder + orderDesc: Boolean + limit: Int): [MarkPrices] +``` +## oraclePrices +TODO docs + +```graphql +oraclePrices( + where: OraclePricesFilter + order: OraclePricesOrder + orderDesc: Boolean + limit: Int): [OraclePrices] +``` +## positionChanges +TODO docs + +```graphql +positionChanges( + where: PositionChangesFilter + order: PositionChangesOrder + orderDesc: Boolean + limit: Int): [PositionChanges] +``` +## positions +TODO docs + +```graphql +positions( + where: PositionsFilter + order: PositionsOrder + orderDesc: Boolean + limit: Int): [Positions] +``` +## statsVolume +TODO docs + +```graphql +statsVolume( + where: StatsVolumeFilter + order: StatsVolumeOrder + orderDesc: Boolean + limit: Int): [StatsVolume] +``` +## transfers +TODO docs + +```graphql +transfers( + where: TransfersFilter + order: TransfersOrder + orderDesc: Boolean + limit: Int): [Transfers] +``` +## txMessages +TODO docs + +```graphql +txMessages( + where: TxMessagesFilter + order: TxMessagesOrder + orderDesc: Boolean + limit: Int): [TxMessages] +``` +## txMessagesFailed +TODO docs + +```graphql +txMessagesFailed( + where: TxMessagesFailedFilter + order: TxMessagesFailedOrder + orderDesc: Boolean + limit: Int): [TxMessagesFailed] +``` +## unbondings + +TODO docs + +```graphql +unbondings( + where: UnbondingsFilter + order: UnbondingsOrder + orderDesc: Boolean + limit: Int): [Unbondings] +``` +## validators + +TODO docs + +```graphql +validators( + where: ValidatorsFilter + order: ValidatorsOrder + orderDesc: Boolean + limit: Int): [Validators] +``` +## vpoolConfigs + +TODO docs + +```graphql +vpoolConfigs( + where: VpoolConfigsFilter + order: VpoolConfigsOrder + orderDesc: Boolean + limit: Int): [VpoolConfigs] +``` diff --git a/packages/indexer-nibi/package.json b/packages/indexer-nibi/package.json index 252b9a6b..08351df2 100644 --- a/packages/indexer-nibi/package.json +++ b/packages/indexer-nibi/package.json @@ -1,7 +1,7 @@ { "name": "@nibiruchain/indexer-nibi", "description": "GraphQL API client for the Nibiru Chain indexer (heart-monitor)", - "version": "0.3.4", + "version": "0.4.0", "license": "MIT", "main": "dist/index.js", "types": "dist/index.d.ts", diff --git a/packages/indexer-nibi/src/constant.ts b/packages/indexer-nibi/src/constant.ts deleted file mode 100644 index cd496ddb..00000000 --- a/packages/indexer-nibi/src/constant.ts +++ /dev/null @@ -1,9 +0,0 @@ -export enum CandleStickPeriod { - MIN_1 = 60, - MIN_5 = 300, - MIN_15 = 900, - HOUR_1 = 3600, - HOUR_6 = 21600, - DAY_1 = 86400, - WEEK_1 = 604800, -} diff --git a/packages/indexer-nibi/src/enum.ts b/packages/indexer-nibi/src/enum.ts new file mode 100644 index 00000000..b3332f22 --- /dev/null +++ b/packages/indexer-nibi/src/enum.ts @@ -0,0 +1,12 @@ +/** CandlePeriod: Each value in the `enum` describes a time window in units of + * seconds. A value of 3600 corresponds to a candle that occurs over one hour. + */ +export enum CandlePeriod { + MIN_1 = 60, + MIN_5 = 300, + MIN_15 = 900, + HOUR_1 = 3600, + HOUR_6 = 21600, + DAY_1 = 86400, + WEEK_1 = 604800, +} diff --git a/packages/indexer-nibi/src/gql.ts b/packages/indexer-nibi/src/gql.ts new file mode 100644 index 00000000..d9d65ffb --- /dev/null +++ b/packages/indexer-nibi/src/gql.ts @@ -0,0 +1,76 @@ +import fetch from "cross-fetch" + +/** + * The workhorse function that fetches data from the GraphQL endpoint. + * + * ### Args: + * @param {string} gqlQuery - raw GraphQL query string + * @param {string} gqlEndpt - URL for the GraphQL endpoint. + * @returns {Promise} + */ +export async function doGqlQuery(gqlQuery: string, gqlEndpt: string): Promise { + const encodedGqlQuery = encodeURI(gqlQuery) + const fetchString = `${gqlEndpt}?query=${encodedGqlQuery}` + const rawResp = await fetch(fetchString) + return cleanResponse(rawResp) +} + +export async function cleanResponse(rawResp: Response): Promise { + const respJson: any = await rawResp.json().catch((err) => { + console.error(err) + }) + console.debug("DEBUG respJson: %o", respJson) + + if (!rawResp.ok || respJson === undefined) { + throw new Error(`${respJson}`) + } else if (respJson.data !== undefined) { + return respJson.data + } else if (respJson !== undefined) { + return respJson + } else { + return respJson + } +} + +/** + * arg: Returns the string format for an "argument" in a GraphQL query. + * + * @param {string} name - name of the argument + * @param {*} value - value of the argument + * @returns {string} + */ +export const arg = (name: string, value: any): string => `${name}: ${value}` + +/** createGqlEndpt: Returns the URL of a heart monitor endpoint based on the + * standard 'chainNickname' included as part of the Tendermint RPC endpoint and + * LCD/Rest endpoint. + * + * Example: The chain ID "nibiru-testnet-2" has the chain nickname, "testnet", + * and chain number, "2". The combination of the nickname and number is what + * we'd use as prefix in the hm-graphql URL. + */ +const createGqlEndpt = (chain: string): string => + `https://hm-graphql.${chain}.nibiru.fi/graphql` + +export function gqlEndptFromTmRpc(endptTm: string): string | null { + const endptTmParts: string[] = endptTm.split(".") + // rpcIdx: the index of the substring that includes rpc + let rpcIdx: number = -1 + endptTmParts.forEach((part, idx) => { + if (part.includes("rpc")) { + rpcIdx = idx + } + }) + + // nicknameIdx: the index of the substring that includes the chain nickname + const nicknameIdx = rpcIdx + 1 + const invalidRpcIdx: boolean = rpcIdx === -1 + const invalidNicknameIdx: boolean = nicknameIdx === endptTmParts.length + if (invalidRpcIdx || invalidNicknameIdx) { + return null + } + + const chainNickname = endptTmParts[nicknameIdx] + const gqlEndpt = createGqlEndpt(chainNickname) + return gqlEndpt +} diff --git a/packages/indexer-nibi/src/heart-monitor.test.ts b/packages/indexer-nibi/src/heart-monitor.test.ts index a9d86788..279229e3 100644 --- a/packages/indexer-nibi/src/heart-monitor.test.ts +++ b/packages/indexer-nibi/src/heart-monitor.test.ts @@ -1,6 +1,7 @@ /* eslint-disable jest/no-conditional-expect */ -import { CandleStickPeriod } from "./constant" import { HeartMonitor } from "./heart-monitor" +import { CandlePeriod } from "./enum" +import { gqlEndptFromTmRpc } from "./gql" const fromBlock = 1 const toBlock = 10 @@ -9,6 +10,86 @@ const pair = "ubtc:unusd" const heartMonitor = new HeartMonitor() +describe("Heart Monitor constructor", () => { + interface TestCase { + name: string + in?: string | { endptTm: string } | undefined + expected: string + } + + const { defaultGqlEndpt } = new HeartMonitor() + + const tests: TestCase[] = [ + { name: "undefined", in: undefined, expected: defaultGqlEndpt }, + { name: "valid string", in: "abc123", expected: "abc123" }, + { + name: "invalid string", + in: undefined, + expected: "https://hm-graphql.devnet-2.nibiru.fi/graphql", + }, + { + name: "chain", + in: { endptTm: "https://rpc.itn-1.nibiru.fi" }, + expected: "https://hm-graphql.itn-1.nibiru.fi/graphql", + }, + ] + + test.each(tests)("$name", (tc) => { + const hm = new HeartMonitor(tc.in) + expect(hm.gqlEndpt).toBe(tc.expected) + }) +}) + +describe("gqlEndptFromTmRpc", () => { + interface TestCase { + in: string + want: string | null + } + + const tests: TestCase[] = [ + { + in: "https://rpc.devnet-2.nibiru.fi", + want: "https://hm-graphql.devnet-2.nibiru.fi/graphql", + }, + { in: "----rpc.itn-1.-----", want: "https://hm-graphql.itn-1.nibiru.fi/graphql" }, + { in: "", want: null }, + { in: "rpctestnet-nodots", want: null }, + { + in: "rpc.testnet-nodots", + want: "https://hm-graphql.testnet-nodots.nibiru.fi/graphql", + }, + ] + + test.each(tests)("%s", (tc: TestCase) => { + const got = gqlEndptFromTmRpc(tc.in) + expect(got).toBe(tc.want) + }) +}) + +test("markPriceCandles", async () => { + const nowTimestamp = Date.now() + const endDate = new Date(nowTimestamp) + const startDate = new Date(nowTimestamp - 1000 * 7 * 24 * 60 * 60) + const resp = await heartMonitor.markPriceCandles({ + pair, + period: CandlePeriod.MIN_5, + limit: 3, + startTs: startDate.toISOString(), + endTs: endDate.toISOString(), + }) + expect(resp).toHaveProperty("markPriceCandles") + + if (resp.markPriceCandles.length > 0) { + const [candle] = resp.markPriceCandles + const fields = ["pair", "open", "close", "high", "low", "period", "periodStartTs"] + fields.forEach((field: string) => { + expect(candle).toHaveProperty(field) + }) + } +}) + +/* + test("useQueryMarkPrices", async () => { const resp = await heartMonitor.useQueryMarkPrices({ pair, @@ -93,23 +174,4 @@ test("useQueryRecentTrades", async () => { } }) -test("useMarkPriceCandleSticks", async () => { - const nowTimestamp = Date.now() - const endDate = new Date(nowTimestamp) - const startDate = new Date(nowTimestamp - 1000 * 7 * 24 * 60 * 60) - const resp = await heartMonitor.useMarkPriceCandleSticks({ - pair, - period: CandleStickPeriod.MIN_5, - startDate: startDate.toISOString(), - endDate: endDate.toISOString(), - }) - expect(resp).toHaveProperty("markPriceCandlesticks") - - if (resp.markPriceCandlesticks.length > 0) { - const [posChange] = resp.markPriceCandlesticks - const props = ["pair", "open", "close", "high", "low", "period", "periodStart"] - props.forEach((prop: string) => { - expect(posChange).toHaveProperty(prop) - }) - } -}) +*/ diff --git a/packages/indexer-nibi/src/heart-monitor.ts b/packages/indexer-nibi/src/heart-monitor.ts index b6147f58..2e8fef26 100644 --- a/packages/indexer-nibi/src/heart-monitor.ts +++ b/packages/indexer-nibi/src/heart-monitor.ts @@ -1,33 +1,18 @@ -import fetch from "cross-fetch" +import { gqlEndptFromTmRpc } from "./gql" import { - GqlMarkPriceCandleSticksInputs, - GqlMarkPricesInputs, - GqlRecentTradesInputs, - TypeBlockMarkPrice, - TypeMarkPrice, - TypeMarkPriceCandleStick, - TypePosChange, -} from "./types" - -async function cleanResponse(rawResp: Response): Promise { - const respJson: any = await rawResp.json().catch((err) => { - console.error(err) - }) - - if (!rawResp.ok || respJson === undefined) { - throw new Error(`${respJson}`) - } else if (respJson.data !== undefined) { - return respJson.data - } else if (respJson !== undefined) { - return respJson - } else { - return respJson - } -} + GqlInMarkPriceCandle, + GqlOutMarkPriceCandle, + markPriceCandles, +} from "./query/markPriceCandles" +/** IHeartMonitor is an interface for a Heart Monitor GraphQL API. + * Each of its methods corresponds to a query function. */ export interface IHeartMonitor { - doGqlQuery: (gqlQuery: string) => Promise + readonly markPriceCandles: ( + args: GqlInMarkPriceCandle, + ) => Promise + /* readonly useQueryBlockMarkPrices: (args: { pair: string fromBlock: number @@ -50,39 +35,43 @@ export interface IHeartMonitor { pair: string lastN: number }) => Promise<{ recentTrades: TypePosChange[] }> - - readonly useMarkPriceCandleSticks: (args: { - pair: string - period: number - startDate: string - endDate: string - }) => Promise<{ markPriceCandlesticks: TypeMarkPriceCandleStick[] }> + */ } +/** HeartMonitor is an API for "Heart Monitor" that indexes the Nibiru blockchain + * and stores the data in strucutred tables. Each of the `HeartMonitor`'s methods + * corresponds to a query function. */ export class HeartMonitor implements IHeartMonitor { gqlEndpt: string - constructor(gqlEndpt?: string) { - this.gqlEndpt = gqlEndpt ?? "https://hm-graphql.testnet-2.nibiru.fi/graphql" - } - - /** - * The workhorse function that fetches data from the GraphQL endpoint. - * - * @param {string} gqlQuery - * @returns {Promise} - */ - doGqlQuery = async (gqlQuery: string): Promise => { - const encodedGqlQuery = encodeURI(gqlQuery) - const fetchString = `${this.gqlEndpt}?query=${encodedGqlQuery}` - const rawResp = await fetch(fetchString) - return cleanResponse(rawResp) + defaultGqlEndpt: string = "https://hm-graphql.devnet-2.nibiru.fi/graphql" + + constructor(gqlEndpt?: string | { endptTm: string }) { + const chain = gqlEndpt as { endptTm: string } + if (gqlEndpt === undefined) { + this.gqlEndpt = this.defaultGqlEndpt + } else if (typeof gqlEndpt === "string") { + this.gqlEndpt = gqlEndpt + } else if (chain?.endptTm !== undefined) { + const endptFromRpc: string | null = gqlEndptFromTmRpc(chain?.endptTm) + this.gqlEndpt = endptFromRpc !== null ? endptFromRpc : this.defaultGqlEndpt + } else { + this.gqlEndpt = this.defaultGqlEndpt + } } // ------------------------------------------------------------ // hooks // ------------------------------------------------------------ + markPriceCandles = async ( + args: GqlInMarkPriceCandle, + ): Promise => markPriceCandles(args, this.gqlEndpt) + + /* + // ------------------------------------------------------------ + // inactive + useQueryMarkPrices = async (args: { pair: string fromBlock: number @@ -166,30 +155,5 @@ export class HeartMonitor implements IHeartMonitor { }` return this.doGqlQuery(gqlQuery(args)) } - - useMarkPriceCandleSticks = async (args: { - pair: string - period: number - startDate: string - endDate: string - }): Promise<{ markPriceCandlesticks: TypeMarkPriceCandleStick[] }> => { - const gqlQuery = ({ - pair, - period, - startDate, - endDate, - }: GqlMarkPriceCandleSticksInputs): string => - `{ - markPriceCandlesticks(pair:"${pair}", period:${period}, startDate: "${startDate}", endDate: "${endDate}") { - pair - open - close - high - low - period - periodStart - } - }` - return this.doGqlQuery(gqlQuery(args)) - } + */ } diff --git a/packages/indexer-nibi/src/index.ts b/packages/indexer-nibi/src/index.ts index badba703..16a3b933 100644 --- a/packages/indexer-nibi/src/index.ts +++ b/packages/indexer-nibi/src/index.ts @@ -20,4 +20,4 @@ */ export * from "./heart-monitor" export * from "./types" -export * from "./constant" +export * from "./enum" diff --git a/packages/indexer-nibi/src/query/markPriceCandles.ts b/packages/indexer-nibi/src/query/markPriceCandles.ts new file mode 100644 index 00000000..2dd9d34c --- /dev/null +++ b/packages/indexer-nibi/src/query/markPriceCandles.ts @@ -0,0 +1,99 @@ +import { CandlePeriod } from "../enum" +import { doGqlQuery, arg } from "../gql" + +// ------------------------------------------------ +// MarkPriceCandle +// ------------------------------------------------ + +/** + * MarkPriceCandle: A single candlestick aggregation of mark price data. + * - pair: identifier for a market pair, e.g. "ueth:unusd". + * - open: Price at the beginning of the period + * - close: Price at the end of the period + * - high: Highest price in the period + * - low: Lowest price in the period + * - period: + */ +export interface MarkPriceCandle { + pair: string + open: number + close: number + high: number + low: number + period: number + periodStartTs: string +} + +/** GqlOutMarkPriceCandle: Output response for the MarkPriceCandle query */ +export interface GqlOutMarkPriceCandle { + markPriceCandles: MarkPriceCandle[] +} + +/** GqlInMarkPriceCandle: Input arguments for the MarkPriceCandle query */ +export interface GqlInMarkPriceCandle { + pair: string + period: CandlePeriod + limit: number + startTs?: string + endTs?: string + orderBy?: MarkPriceCandleOrderBy | string + orderDescending?: boolean // defaults to true +} + +export enum MarkPriceCandleOrderBy { + pair = "pair", + open = "open", + close = "close", + low = "low", + high = "high", + period = "period", + period_start_ts = "period_start_ts", +} + +export const markPriceCandles = async ( + args: GqlInMarkPriceCandle, + endpt: string, +): Promise => { + if (args.orderDescending === undefined) args.orderDescending = true + if (args.orderBy === undefined) args.orderBy = MarkPriceCandleOrderBy.period_start_ts + + const gqlQuery = ({ + pair, + period, + startTs, + endTs, + limit, + orderBy, + orderDescending, + }: GqlInMarkPriceCandle): string => { + const argWhere = (): string => { + const whereConditions: string[] = [] + whereConditions.push(`pairEq: "${pair}"`) + whereConditions.push(`periodEq: ${period}`) + if (startTs) whereConditions.push(`periodStartTsGte: "${startTs}"`) + if (endTs) whereConditions.push(`periodStartTsLt: "${endTs}"`) + const argWhereBody: string = whereConditions.join(", ") + return `where: { ${argWhereBody} }` + } + + const queryArgList: string[] = [ + argWhere(), + arg("limit", limit), + arg("order", orderBy), + arg("orderDesc", orderDescending), + ] + const queryArgs: string = queryArgList.join(", ") + return `{ + markPriceCandles(${queryArgs}) { + pair + open + close + low + high + period + periodStartTs + } + }` + } + return doGqlQuery(gqlQuery(args), endpt) +} diff --git a/packages/indexer-nibi/src/types.ts b/packages/indexer-nibi/src/types.ts index 5a6937a1..9dacc5be 100644 --- a/packages/indexer-nibi/src/types.ts +++ b/packages/indexer-nibi/src/types.ts @@ -31,15 +31,9 @@ export interface TypePosChange { transactionFee: string } -export interface TypeMarkPriceCandleStick { - pair: string - open: number - close: number - high: number - low: number - period: number - periodStart: string -} +// ------------------------------------------------ +// MarkPrice +// ------------------------------------------------ export interface IGqlMarkPrices { markPrices: TypeMarkPrice[] @@ -51,19 +45,7 @@ export interface GqlMarkPricesInputs { fromBlock: number toBlock: number } - export interface GqlRecentTradesInputs { pair: string lastN: number } - -export interface IGqlMarkPriceCandleSticks { - markPriceCandlesticks: TypeMarkPriceCandleStick[] -} - -export interface GqlMarkPriceCandleSticksInputs { - pair: string - period: number - startDate: string - endDate: string -}