@drift-labs/sdk
Version:
SDK for Drift Protocol
162 lines (161 loc) • 6.75 kB
JavaScript
"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;