UNPKG

@drift-labs/sdk-browser

Version:
244 lines (243 loc) 12.6 kB
"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;