UNPKG

@d8x/perpetuals-sdk

Version:

Node TypeScript SDK for D8X Perpetual Futures

147 lines (130 loc) 4.96 kB
import { probToPrice } from "./d8XMath"; import type { PredMktPriceInfo, PriceFeedConfig, PriceFeedJson } from "./nodeSDKTypes"; interface PolyConfig { sym: string; idDec: string; id: string; } /** * PolyMktsPxFeed gets prices from the official polymarket api * and applies the 1+px transformation * */ export default class PolyMktsPxFeed { private ids: Map<string, PolyConfig>; private oracleEndpoint: string; constructor(config: PriceFeedConfig) { this.ids = new Map<string, PolyConfig>(); this.oracleEndpoint = ""; for (let k = 0; k < config.endpoints.length; k++) { if (config.endpoints[k].type == "polymarket") { let endp = config.endpoints[k].writeEndpoints; this.oracleEndpoint = endp[Math.floor(Math.random() * endp.length)]; break; } } if (this.oracleEndpoint == "") { throw Error("no polymarkets write endpoint defined in priceFeedConfig"); } for (let k = 0; k < config.ids.length; k++) { if (config.ids[k].type == "polymarket") { const sym = config.ids[k].symbol; const idDec = PolyMktsPxFeed.hexToDecimalString(config.ids[k].id); const el: PolyConfig = { sym: sym, id: config.ids[k].id, idDec: idDec, }; this.ids.set(sym, el); } } this.oracleEndpoint = this.oracleEndpoint.replace(/\/$/, "") + "/v3/updates/price/latest?encoding=base64&ids[]="; } public isPolyMktsSym(sym: string) { return this.ids.get(sym) == undefined; } // returns index price, ema price, conf in tbps, parameters for order book // for the provided symbols public async fetchPricesForSyms(syms: string[]): Promise<PredMktPriceInfo[]> { let ids = new Array<string>(); for (let k = 0; k < syms.length; k++) { const mkt = this.ids.get(syms[k]); if (mkt == undefined) { throw new Error(`symbol not in polymarket universe: ${syms[k]}`); } ids.push(mkt.id); } const chunkSize = 10; const chunks: string[][] = []; for (let i = 0; i < ids.length; i += chunkSize) { chunks.push(ids.slice(i, i + chunkSize)); } const results: PredMktPriceInfo[] = []; for (const chunk of chunks) { const res = await this.fetchPrices(chunk); results.push(...res); } return results; } // fetch price of the form 1+p from oracle, also fetches ema public async fetchPrices(tokenIdHex: string[]): Promise<PredMktPriceInfo[]> { // build query let query = this.oracleEndpoint + tokenIdHex[0]; for (let k = 1; k < tokenIdHex.length; k++) { query += "&ids[]=" + tokenIdHex[k]; } let response = await fetch(query); if (response.status !== 200) { throw new Error(`unexpected status code: ${response.status}`); } if (!response.ok) { throw new Error(`failed to fetch posts (${response.status}): ${response.statusText} ${query}`); } const data = await response.json(); let res = new Array<PredMktPriceInfo>(); for (let k = 0; k < tokenIdHex.length; k++) { const parsed = data.parsed.find(({ id }: { id: string }) => id.toLowerCase() == tokenIdHex[k].toLowerCase()) as | { price: PriceFeedJson; ema_price: PriceFeedJson; metadata: { market_closed: boolean } } | undefined; const emaObj = parsed?.ema_price; const pxObj = parsed?.price; const marketClosed = Boolean(parsed?.metadata.market_closed); const s2 = pxObj == undefined || pxObj.price == "NaN" ? 0 : Number(pxObj.price) * Math.pow(10, pxObj.expo); const ema = emaObj == undefined || emaObj.price == "NaN" ? 0 : Number(emaObj.price) * Math.pow(10, emaObj.expo); const params = emaObj?.conf == undefined ? 0n : BigInt(emaObj.conf); const conf = pxObj?.conf == undefined ? 0n : BigInt(pxObj.conf); const info: PredMktPriceInfo = { s2, ema, s2MktClosed: marketClosed || s2 == 0 || ema == 0, conf, predMktCLOBParams: params, }; res.push(info); } return res; } public async fetchPriceFromAPI(tokenIdDec: string): Promise<number> { const query = "https://clob.polymarket.com/midpoint?token_id=" + tokenIdDec; let response = await fetch(query); if (response.status !== 200) { throw new Error(`unexpected status code: ${response.status}`); } if (!response.ok) { throw new Error(`failed to fetch posts (${response.status}): ${response.statusText} ${query}`); } const data = await response.json(); const px = Number(data.mid); return probToPrice(px); } static hexToDecimalString(hexString: string): string { // Remove the "0x" prefix if it exists if (hexString.startsWith("0x")) { hexString = hexString.slice(2); } // Convert the hex string to a BigInt const bigIntValue = BigInt("0x" + hexString); // Convert the BigInt to a decimal string return bigIntValue.toString(10); } }