@drift-labs/sdk-browser
Version:
SDK for Drift Protocol
244 lines (243 loc) • 12.6 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.grpcDriftClientAccountSubscriberV2 = void 0;
const webSocketDriftClientAccountSubscriber_1 = require("./webSocketDriftClientAccountSubscriber");
const web3_js_1 = require("@solana/web3.js");
const config_1 = require("../config");
const pda_1 = require("../addresses/pda");
const grpcAccountSubscriber_1 = require("./grpcAccountSubscriber");
const grpcMultiAccountSubscriber_1 = require("./grpcMultiAccountSubscriber");
const oracleId_1 = require("../oracles/oracleId");
class grpcDriftClientAccountSubscriberV2 extends webSocketDriftClientAccountSubscriber_1.WebSocketDriftClientAccountSubscriber {
constructor(grpcConfigs, program, perpMarketIndexes, spotMarketIndexes, oracleInfos, shouldFindAllMarketsAndOracles, delistedMarketSetting, resubOpts) {
super(program, perpMarketIndexes, spotMarketIndexes, oracleInfos, shouldFindAllMarketsAndOracles, delistedMarketSetting, resubOpts);
this.perpMarketIndexToAccountPubkeyMap = new Map();
this.spotMarketIndexToAccountPubkeyMap = new Map();
this.grpcConfigs = grpcConfigs;
}
async subscribe() {
if (this.isSubscribed) {
return true;
}
if (this.isSubscribing) {
return await this.subscriptionPromise;
}
this.isSubscribing = true;
this.subscriptionPromise = new Promise((res) => {
this.subscriptionPromiseResolver = res;
});
if (this.shouldFindAllMarketsAndOracles) {
const { perpMarketIndexes, perpMarketAccounts, spotMarketIndexes, spotMarketAccounts, oracleInfos, } = await (0, config_1.findAllMarketAndOracles)(this.program);
this.perpMarketIndexes = perpMarketIndexes;
this.spotMarketIndexes = spotMarketIndexes;
this.oracleInfos = oracleInfos;
// front run and set the initial data here to save extra gma call in set initial data
this.initialPerpMarketAccountData = new Map(perpMarketAccounts.map((market) => [market.marketIndex, market]));
this.initialSpotMarketAccountData = new Map(spotMarketAccounts.map((market) => [market.marketIndex, market]));
}
const statePublicKey = await (0, pda_1.getDriftStateAccountPublicKey)(this.program.programId);
// create and activate main state account subscription
this.stateAccountSubscriber =
await grpcAccountSubscriber_1.grpcAccountSubscriber.create(this.grpcConfigs, 'state', this.program, statePublicKey, undefined, undefined);
await this.stateAccountSubscriber.subscribe((data) => {
this.eventEmitter.emit('stateAccountUpdate', data);
this.eventEmitter.emit('update');
});
// set initial data to avoid spamming getAccountInfo calls in webSocketAccountSubscriber
await this.setInitialData();
// subscribe to perp + spot markets (separate) and oracles
await Promise.all([
this.subscribeToPerpMarketAccounts(),
this.subscribeToSpotMarketAccounts(),
this.subscribeToOracles(),
]);
this.eventEmitter.emit('update');
await this.handleDelistedMarkets();
await Promise.all([this.setPerpOracleMap(), this.setSpotOracleMap()]);
this.subscriptionPromiseResolver(true);
this.isSubscribing = false;
this.isSubscribed = true;
// delete initial data
this.removeInitialData();
return true;
}
getMarketAccountAndSlot(marketIndex) {
var _a;
return (_a = this.perpMarketsSubscriber) === null || _a === void 0 ? void 0 : _a.getAccountData(this.perpMarketIndexToAccountPubkeyMap.get(marketIndex));
}
getSpotMarketAccountAndSlot(marketIndex) {
var _a;
return (_a = this.spotMarketsSubscriber) === null || _a === void 0 ? void 0 : _a.getAccountData(this.spotMarketIndexToAccountPubkeyMap.get(marketIndex));
}
async setPerpOracleMap() {
var _a;
const perpMarketsMap = (_a = this.perpMarketsSubscriber) === null || _a === void 0 ? void 0 : _a.getAccountDataMap();
const perpMarkets = Array.from(perpMarketsMap.values());
const addOraclePromises = [];
for (const perpMarket of perpMarkets) {
if (!perpMarket || !perpMarket.data) {
continue;
}
const perpMarketAccount = perpMarket.data;
const perpMarketIndex = perpMarketAccount.marketIndex;
const oracle = perpMarketAccount.amm.oracle;
const oracleId = (0, oracleId_1.getOracleId)(oracle, perpMarket.data.amm.oracleSource);
if (!this.oracleSubscribers.has(oracleId)) {
addOraclePromises.push(this.addOracle({
publicKey: oracle,
source: perpMarket.data.amm.oracleSource,
}));
}
this.perpOracleMap.set(perpMarketIndex, oracle);
this.perpOracleStringMap.set(perpMarketIndex, oracleId);
}
await Promise.all(addOraclePromises);
}
async setSpotOracleMap() {
var _a;
const spotMarketsMap = (_a = this.spotMarketsSubscriber) === null || _a === void 0 ? void 0 : _a.getAccountDataMap();
const spotMarkets = Array.from(spotMarketsMap.values());
const addOraclePromises = [];
for (const spotMarket of spotMarkets) {
if (!spotMarket || !spotMarket.data) {
continue;
}
const spotMarketAccount = spotMarket.data;
const spotMarketIndex = spotMarketAccount.marketIndex;
const oracle = spotMarketAccount.oracle;
const oracleId = (0, oracleId_1.getOracleId)(oracle, spotMarketAccount.oracleSource);
if (!this.oracleSubscribers.has(oracleId)) {
addOraclePromises.push(this.addOracle({
publicKey: oracle,
source: spotMarketAccount.oracleSource,
}));
}
this.spotOracleMap.set(spotMarketIndex, oracle);
this.spotOracleStringMap.set(spotMarketIndex, oracleId);
}
await Promise.all(addOraclePromises);
}
async subscribeToPerpMarketAccounts() {
const perpMarketIndexToAccountPubkeys = await Promise.all(this.perpMarketIndexes.map(async (marketIndex) => [
marketIndex,
await (0, pda_1.getPerpMarketPublicKey)(this.program.programId, marketIndex),
]));
for (const [marketIndex, accountPubkey,] of perpMarketIndexToAccountPubkeys) {
this.perpMarketIndexToAccountPubkeyMap.set(marketIndex, accountPubkey.toBase58());
}
const perpMarketPubkeys = perpMarketIndexToAccountPubkeys.map(([_, accountPubkey]) => accountPubkey);
this.perpMarketsSubscriber =
await grpcMultiAccountSubscriber_1.grpcMultiAccountSubscriber.create(this.grpcConfigs, 'perpMarket', this.program, undefined, this.resubOpts, undefined, async () => {
var _a;
try {
if ((_a = this.resubOpts) === null || _a === void 0 ? void 0 : _a.logResubMessages) {
console.log('[grpcDriftClientAccountSubscriberV2] perp markets subscriber unsubscribed; resubscribing');
}
await this.subscribeToPerpMarketAccounts();
}
catch (e) {
console.error('Perp markets resubscribe failed:', e);
}
});
for (const data of this.initialPerpMarketAccountData.values()) {
this.perpMarketsSubscriber.setAccountData(data.pubkey, data);
}
await this.perpMarketsSubscriber.subscribe(perpMarketPubkeys, (_accountId, data) => {
this.eventEmitter.emit('perpMarketAccountUpdate', data);
this.eventEmitter.emit('update');
});
return true;
}
async subscribeToSpotMarketAccounts() {
const spotMarketIndexToAccountPubkeys = await Promise.all(this.spotMarketIndexes.map(async (marketIndex) => [
marketIndex,
await (0, pda_1.getSpotMarketPublicKey)(this.program.programId, marketIndex),
]));
for (const [marketIndex, accountPubkey,] of spotMarketIndexToAccountPubkeys) {
this.spotMarketIndexToAccountPubkeyMap.set(marketIndex, accountPubkey.toBase58());
}
const spotMarketPubkeys = spotMarketIndexToAccountPubkeys.map(([_, accountPubkey]) => accountPubkey);
this.spotMarketsSubscriber =
await grpcMultiAccountSubscriber_1.grpcMultiAccountSubscriber.create(this.grpcConfigs, 'spotMarket', this.program, undefined, this.resubOpts, undefined, async () => {
var _a;
try {
if ((_a = this.resubOpts) === null || _a === void 0 ? void 0 : _a.logResubMessages) {
console.log('[grpcDriftClientAccountSubscriberV2] spot markets subscriber unsubscribed; resubscribing');
}
await this.subscribeToSpotMarketAccounts();
}
catch (e) {
console.error('Spot markets resubscribe failed:', e);
}
});
for (const data of this.initialSpotMarketAccountData.values()) {
this.spotMarketsSubscriber.setAccountData(data.pubkey, data);
}
await this.spotMarketsSubscriber.subscribe(spotMarketPubkeys, (_accountId, data) => {
this.eventEmitter.emit('spotMarketAccountUpdate', data);
this.eventEmitter.emit('update');
});
return true;
}
async subscribeToOracles() {
// Build list of unique oracle pubkeys and a lookup for sources
const uniqueOraclePubkeys = new Map();
for (const info of this.oracleInfos) {
const id = (0, oracleId_1.getOracleId)(info.publicKey, info.source);
if (!uniqueOraclePubkeys.has(id) &&
!info.publicKey.equals(web3_js_1.PublicKey.default)) {
uniqueOraclePubkeys.set(id, info);
}
}
const oraclePubkeys = Array.from(uniqueOraclePubkeys.values()).map((i) => i.publicKey);
const pubkeyToSource = new Map(Array.from(uniqueOraclePubkeys.values()).map((i) => [
i.publicKey.toBase58(),
i.source,
]));
this.oracleMultiSubscriber =
await grpcMultiAccountSubscriber_1.grpcMultiAccountSubscriber.create(this.grpcConfigs, 'oracle', this.program, (buffer, pubkey) => {
if (!pubkey) {
throw new Error('Oracle pubkey missing in decode');
}
const source = pubkeyToSource.get(pubkey);
const client = this.oracleClientCache.get(source, this.program.provider.connection, this.program);
return client.getOraclePriceDataFromBuffer(buffer);
}, this.resubOpts, undefined, async () => {
var _a;
try {
if ((_a = this.resubOpts) === null || _a === void 0 ? void 0 : _a.logResubMessages) {
console.log('[grpcDriftClientAccountSubscriberV2] oracle subscriber unsubscribed; resubscribing');
}
await this.subscribeToOracles();
}
catch (e) {
console.error('Oracle resubscribe failed:', e);
}
});
for (const data of this.initialOraclePriceData.entries()) {
const { publicKey } = (0, oracleId_1.getPublicKeyAndSourceFromOracleId)(data[0]);
this.oracleMultiSubscriber.setAccountData(publicKey, data[1]);
}
await this.oracleMultiSubscriber.subscribe(oraclePubkeys, (accountId, data) => {
const source = pubkeyToSource.get(accountId.toBase58());
this.eventEmitter.emit('oraclePriceUpdate', accountId, source, data);
this.eventEmitter.emit('update');
});
return true;
}
async unsubscribeFromOracles() {
if (this.oracleMultiSubscriber) {
await this.oracleMultiSubscriber.unsubscribe();
this.oracleMultiSubscriber = undefined;
return;
}
await super.unsubscribeFromOracles();
}
async unsubscribe() {
if (this.isSubscribed) {
return;
}
await this.stateAccountSubscriber.unsubscribe();
}
}
exports.grpcDriftClientAccountSubscriberV2 = grpcDriftClientAccountSubscriberV2;