From 9793502dab197e55274b584e768a56bf7f5eea0f Mon Sep 17 00:00:00 2001 From: Dylan Yang Date: Tue, 11 Oct 2022 18:56:33 +0900 Subject: [PATCH] feat(Markets): markets module supports get all market data by getMarketDataAll function --- src/core/PerpetualProtocol.ts | 5 +- src/core/market/Markets.ts | 135 +++++++++++++++++++++++++++++++++- src/core/market/index.ts | 2 +- 3 files changed, 136 insertions(+), 6 deletions(-) diff --git a/src/core/PerpetualProtocol.ts b/src/core/PerpetualProtocol.ts index 3adc526..3ffe9cc 100644 --- a/src/core/PerpetualProtocol.ts +++ b/src/core/PerpetualProtocol.ts @@ -151,7 +151,10 @@ class PerpetualProtocol { // this.provider = getRetryProvider(providerConfigs) // TODO:clean up console.log("provider", providerConfigs[0].rpcUrl) - this.provider = getProvider({ rpcUrl: providerConfigs[0].rpcUrl }) + // this.provider = getProvider({ rpcUrl: providerConfigs[0].rpcUrl }) + this.provider = getProvider({ + rpcUrl: "https://twilight-small-butterfly.optimism.quiknode.pro/9054785eeae7ef809e1215354d835a0de381ccd4", + }) } async init() { diff --git a/src/core/market/Markets.ts b/src/core/market/Markets.ts index 8350060..7277f7b 100644 --- a/src/core/market/Markets.ts +++ b/src/core/market/Markets.ts @@ -1,9 +1,22 @@ +import Big from "big.js" + +import { ContractName } from "../../contracts" import { ArgumentError, TypeError } from "../../errors" +import { Channel, ChannelEventSource, DEFAULT_PERIOD } from "../../internal" import { Pool } from "../../metadata" -import { assertExist, getTickerSymbol, invariant, isEmptyObject } from "../../utils" -import type { PerpetualProtocol } from "../PerpetualProtocol" -import { Market } from "./Market" +import { + assertExist, + bigNumber2BigAndScaleDown, + fromSqrtX96, + getTickerSymbol, + invariant, + isEmptyObject, + poll, +} from "../../utils" +import { ContractCall, MulticallReader } from "../contractReader/MulticallReader" +import { Market, MarketStatus } from "./Market" +import type { PerpetualProtocol } from "../PerpetualProtocol" export interface MarketMap { [key: string]: Market } @@ -20,11 +33,24 @@ export interface marketInfo { tradingVolume: string // NOTE: Total accumulated from day 1. tradingFee: string // NOTE: Total accumulated from day 1. } -class Markets { + +type MarketsEventName = "updateError" | "updated" + +export interface MarketDataAll { + [key: string]: { + status: MarketStatus + markPrice: Big + indexPrice: Big + indexTwapPrice: Big + } +} + +class Markets extends Channel { private readonly _perp: PerpetualProtocol private readonly _marketMap: MarketMap constructor(perp: PerpetualProtocol) { + super(perp.channelRegistry) this._perp = perp this._marketMap = this._getMarketMap() } @@ -88,9 +114,110 @@ class Markets { return market } + async getMarketsBaseQuoteAmount(marketsInfo: marketInfo[]): Promise { return this._perp.contractReader.getMarketsBaseTokenAndQuoteTokenAmount(marketsInfo) } + + // NOTE: call by Channel -> constructor + protected _getEventSourceMap() { + const fetchAndEmitUpdated = this.getMarketDataAll.bind(this) + const updateDataEventSource = new ChannelEventSource({ + eventSourceStarter: () => { + const { cancel } = poll(fetchAndEmitUpdated, this._perp.moduleConfigs?.market?.period || DEFAULT_PERIOD) + return cancel + }, + initEventEmitter: () => fetchAndEmitUpdated(), + }) + + // TODO: eventName typing protection, should error when invalid eventName is provided + return { + updated: updateDataEventSource, + updateError: updateDataEventSource, + } + } + + protected async getMarketDataAll() { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + const countMap = window.countMap + countMap["getMarketDataAll"] ? (countMap["getMarketDataAll"] += 1) : (countMap["getMarketDataAll"] = 1) + countMap.sum += 1 + + const contracts = this._perp.contracts + const multicallReader = new MulticallReader({ contract: this._perp.contracts.multicall2 }) + // NOTE: We cover those functions of the contract reader (getIndexPrice, getIndexTwapPrice, getMarketPrice, isMarketClosed, isMarketPaused, getMarketStatus, getMarketData) + // NOTE: The function getPriceFeedAggregator is called by LimitOrderBook, we can add it later if it is necessary. + const callsMap: { [key: string]: ContractCall[] } = {} + Object.entries(this._marketMap).forEach(([tickerSymbol, market]) => { + const contractBaseToken = contracts.baseToken.attach(market.baseAddress) + const contractPool = contracts.pool.attach(market.poolAddress) + const calls: ContractCall[] = [ + // NOTE: get index price + { + contract: contractBaseToken, + contractName: ContractName.BASE_TOKEN, + funcName: "getIndexPrice", + funcParams: [0], + }, + // NOTE: get index twap price + { + contract: contractBaseToken, + contractName: ContractName.BASE_TOKEN, + funcName: "getIndexPrice", + funcParams: [15 * 60], + }, + // NOTE: get market price + { + contract: contractPool, + contractName: ContractName.POOL, + funcName: "slot0", + funcParams: [], + }, + // NOTE: get if the base token paused + { + contract: contractBaseToken, + contractName: ContractName.BASE_TOKEN, + funcName: "isPaused", + funcParams: [], + }, + // NOTE: get if the base token closed + { + contract: contractBaseToken, + contractName: ContractName.BASE_TOKEN, + funcName: "isClosed", + funcParams: [], + }, + ] + callsMap[`${tickerSymbol}`] = calls + }) + // NOTE: grad data + const data = await multicallReader.execute(Object.values(callsMap).flat(), { + failFirstByContract: false, + failFirstByClient: false, + }) + + // NOTE: data analysis + // TODO: error handling + const marketDataAll: MarketDataAll = {} + Object.entries(callsMap).forEach(([key, value]) => { + const dataChunk = data.splice(0, value.length) + const indexPrice = bigNumber2BigAndScaleDown(dataChunk[0]) + const indexTwapPrice = bigNumber2BigAndScaleDown(dataChunk[1]) + const markPrice = fromSqrtX96(dataChunk[2].sqrtPriceX96) + const isPaused = dataChunk[3] + const isClosed = dataChunk[4] + marketDataAll[`${key}`] = { + status: isClosed ? MarketStatus.CLOSED : isPaused ? MarketStatus.PAUSED : MarketStatus.ACTIVE, + markPrice, + indexPrice, + indexTwapPrice, + } + }) + + // NOTE: emit market data all + this.emit("updated", marketDataAll) + } } export { Markets } diff --git a/src/core/market/index.ts b/src/core/market/index.ts index 3667ae8..bf82046 100644 --- a/src/core/market/index.ts +++ b/src/core/market/index.ts @@ -1,4 +1,4 @@ -export type { MarketMap, GetMarketParams } from "./Markets" +export type { MarketMap, GetMarketParams, MarketDataAll } from "./Markets" export { Markets } from "./Markets" export { Market, MarketStatus } from "./Market" export { FundingUpdated } from "./FundingUpdated"