UNPKG

@drift-labs/sdk

Version:
162 lines (161 loc) 6.75 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.PhoenixSubscriber = void 0; const web3_js_1 = require("@solana/web3.js"); const phoenix_sdk_1 = require("@ellipsis-labs/phoenix-sdk"); const numericConstants_1 = require("../constants/numericConstants"); const anchor_1 = require("@coral-xyz/anchor"); const phoenix_1 = require("../decode/phoenix"); class PhoenixSubscriber { constructor(config) { var _a; this.connection = config.connection; this.programId = config.programId; this.marketAddress = config.marketAddress; if (config.accountSubscription.type === 'polling') { this.subscriptionType = 'polling'; this.accountLoader = config.accountSubscription.accountLoader; } else { this.subscriptionType = 'websocket'; } this.lastSlot = 0; this.lastUnixTimestamp = 0; this.fastDecode = (_a = config.fastDecode) !== null && _a !== void 0 ? _a : true; } async subscribe() { if (this.subscribed) { return; } this.market = await phoenix_sdk_1.Market.loadFromAddress({ connection: this.connection, address: this.marketAddress, }); const clock = (0, phoenix_sdk_1.deserializeClockData)((await this.connection.getAccountInfo(web3_js_1.SYSVAR_CLOCK_PUBKEY, 'confirmed')) .data); this.lastUnixTimestamp = (0, phoenix_sdk_1.toNum)(clock.unixTimestamp); if (this.subscriptionType === 'websocket') { this.marketCallbackId = this.connection.onAccountChange(this.marketAddress, (accountInfo, _ctx) => { try { if (this.fastDecode) { this.market.data = (0, phoenix_1.fastDecode)(accountInfo.data); } else { this.market = this.market.reload(accountInfo.data); } } catch { console.error('Failed to reload Phoenix market data'); } }); this.clockCallbackId = this.connection.onAccountChange(web3_js_1.SYSVAR_CLOCK_PUBKEY, (accountInfo, ctx) => { try { this.lastSlot = ctx.slot; const clock = (0, phoenix_sdk_1.deserializeClockData)(accountInfo.data); this.lastUnixTimestamp = (0, phoenix_sdk_1.toNum)(clock.unixTimestamp); } catch { console.error('Failed to reload clock data'); } }); } else { this.marketCallbackId = await this.accountLoader.addAccount(this.marketAddress, (buffer, slot) => { try { this.lastSlot = slot; if (buffer) { if (this.fastDecode) { this.market.data = (0, phoenix_1.fastDecode)(buffer); } else { this.market = this.market.reload(buffer); } } } catch { console.error('Failed to reload Phoenix market data'); } }); this.clockCallbackId = await this.accountLoader.addAccount(web3_js_1.SYSVAR_CLOCK_PUBKEY, (buffer, slot) => { try { this.lastSlot = slot; const clock = (0, phoenix_sdk_1.deserializeClockData)(buffer); this.lastUnixTimestamp = (0, phoenix_sdk_1.toNum)(clock.unixTimestamp); } catch { console.error('Failed to reload clock data'); } }); } this.subscribed = true; } getBestBid() { const ladder = (0, phoenix_sdk_1.getMarketLadder)(this.market, this.lastSlot, this.lastUnixTimestamp, 1); const bestBid = ladder.bids[0]; if (!bestBid) { return undefined; } return this.convertPriceInTicksToPricePrecision(bestBid.priceInTicks); } getBestAsk() { const ladder = (0, phoenix_sdk_1.getMarketLadder)(this.market, this.lastSlot, this.lastUnixTimestamp, 1); const bestAsk = ladder.asks[0]; if (!bestAsk) { return undefined; } return this.convertPriceInTicksToPricePrecision(bestAsk.priceInTicks); } convertPriceInTicksToPricePrecision(priceInTicks) { const quotePrecision = new anchor_1.BN(Math.pow(10, this.market.data.header.quoteParams.decimals)); const denom = quotePrecision.muln(this.market.data.header.rawBaseUnitsPerBaseUnit); const price = priceInTicks .muln(this.market.data.quoteLotsPerBaseUnitPerTick) .muln((0, phoenix_sdk_1.toNum)(this.market.data.header.quoteLotSize)); return price.mul(numericConstants_1.PRICE_PRECISION).div(denom); } convertSizeInBaseLotsToMarketPrecision(sizeInBaseLots) { return sizeInBaseLots.muln((0, phoenix_sdk_1.toNum)(this.market.data.header.baseLotSize)); } getL2Bids() { return this.getL2Levels('bids'); } getL2Asks() { return this.getL2Levels('asks'); } *getL2Levels(side) { const ladder = (0, phoenix_sdk_1.getMarketLadder)(this.market, this.lastSlot, this.lastUnixTimestamp, 20); for (let i = 0; i < ladder[side].length; i++) { const { priceInTicks, sizeInBaseLots } = ladder[side][i]; try { const size = this.convertSizeInBaseLotsToMarketPrecision(sizeInBaseLots); const updatedPrice = this.convertPriceInTicksToPricePrecision(priceInTicks); yield { price: updatedPrice, size, sources: { phoenix: size, }, }; } catch { continue; } } } async unsubscribe() { if (!this.subscribed) { return; } // remove listeners if (this.subscriptionType === 'websocket') { await this.connection.removeAccountChangeListener(this.marketCallbackId); await this.connection.removeAccountChangeListener(this.clockCallbackId); } else { this.accountLoader.removeAccount(this.marketAddress, this.marketCallbackId); this.accountLoader.removeAccount(web3_js_1.SYSVAR_CLOCK_PUBKEY, this.clockCallbackId); } this.subscribed = false; } } exports.PhoenixSubscriber = PhoenixSubscriber;