@drift-labs/sdk-browser
Version:
SDK for Drift Protocol
178 lines (177 loc) • 7.32 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ConstituentMap = void 0;
const pollingConstituentAccountSubscriber_1 = require("./pollingConstituentAccountSubscriber");
const webSocketConstituentAccountSubscriber_1 = require("./webSocketConstituentAccountSubscriber");
const memcmp_1 = require("../memcmp");
const zstddec_1 = require("zstddec");
const pda_1 = require("../addresses/pda");
const MAX_CONSTITUENT_SIZE_BYTES = 480; // TODO: update this when account is finalized
class ConstituentMap {
constructor(config) {
var _a, _b;
this.constituentMap = new Map();
this.constituentIndexToKeyMap = new Map();
this.spotMarketIndexToKeyMap = new Map();
this.driftClient = config.driftClient;
this.additionalFilters = config.additionalFilters;
this.commitment = config.subscriptionConfig.commitment;
this.connection = config.connection || this.driftClient.connection;
this.lpPoolId = (_a = config.lpPoolId) !== null && _a !== void 0 ? _a : 0;
this.decoder = (_b = config.decoder) !== null && _b !== void 0 ? _b : 'base64+zstd';
if (config.subscriptionConfig.type === 'polling') {
this.constituentAccountSubscriber =
new pollingConstituentAccountSubscriber_1.PollingConstituentAccountSubscriber(this, this.driftClient.program, config.subscriptionConfig.frequency, config.subscriptionConfig.commitment, this.getFilters());
}
else if (config.subscriptionConfig.type === 'websocket') {
this.constituentAccountSubscriber =
new webSocketConstituentAccountSubscriber_1.WebSocketConstituentAccountSubscriber(this, this.driftClient.program, config.subscriptionConfig.resubTimeoutMs, config.subscriptionConfig.commitment, this.getFilters());
}
// Listen for account updates from the subscriber
this.constituentAccountSubscriber.eventEmitter.on('onAccountUpdate', (account, pubkey, slot) => {
this.updateConstituentAccount(pubkey.toString(), account, slot);
});
}
getFilters() {
const filters = [
(0, memcmp_1.getConstituentFilter)(),
(0, memcmp_1.getConstituentLpPoolFilter)((0, pda_1.getLpPoolPublicKey)(this.driftClient.program.programId, this.lpPoolId)),
];
if (this.additionalFilters) {
filters.push(...this.additionalFilters);
}
return filters;
}
decode(name, buffer) {
return this.driftClient.program.account.constituent.coder.accounts.decodeUnchecked(name, buffer);
}
async sync() {
try {
const rpcRequestArgs = [
this.driftClient.program.programId.toBase58(),
{
commitment: this.commitment,
filters: this.getFilters(),
encoding: this.decoder,
withContext: true,
},
];
// @ts-ignore
const rpcJSONResponse = await this.connection._rpcRequest('getProgramAccounts', rpcRequestArgs);
const rpcResponseAndContext = rpcJSONResponse.result;
const slot = rpcResponseAndContext.context.slot;
const promises = rpcResponseAndContext.value.map(async (programAccount) => {
let buffer;
if (this.decoder === 'base64+zstd') {
const compressedUserData = Buffer.from(programAccount.account.data[0], 'base64');
const decoder = new zstddec_1.ZSTDDecoder();
await decoder.init();
buffer = Buffer.from(decoder.decode(compressedUserData, MAX_CONSTITUENT_SIZE_BYTES));
}
else {
buffer = Buffer.from(programAccount.account.data[0], 'base64');
}
const key = programAccount.pubkey.toString();
const currAccountWithSlot = this.getWithSlot(key);
if (currAccountWithSlot) {
if (slot >= currAccountWithSlot.slot) {
const constituentAcc = this.decode('Constituent', buffer);
this.updateConstituentAccount(key, constituentAcc, slot);
}
}
else {
const constituentAcc = this.decode('Constituent', buffer);
this.updateConstituentAccount(key, constituentAcc, slot);
}
});
await Promise.all(promises);
}
catch (error) {
console.log(`ConstituentMap.sync() error: ${error.message}`);
}
}
async subscribe() {
await this.constituentAccountSubscriber.subscribe();
}
async unsubscribe() {
await this.constituentAccountSubscriber.unsubscribe();
this.constituentMap.clear();
}
has(key) {
return this.constituentMap.has(key);
}
get(key) {
var _a;
return (_a = this.constituentMap.get(key)) === null || _a === void 0 ? void 0 : _a.data;
}
getFromConstituentIndex(constituentIndex) {
const key = this.constituentIndexToKeyMap.get(constituentIndex);
return key ? this.get(key) : undefined;
}
getFromSpotMarketIndex(spotMarketIndex) {
const key = this.spotMarketIndexToKeyMap.get(spotMarketIndex);
return key ? this.get(key) : undefined;
}
getWithSlot(key) {
return this.constituentMap.get(key);
}
async mustGet(key) {
if (!this.has(key)) {
await this.sync();
}
const result = this.constituentMap.get(key);
if (!result) {
throw new Error(`ConstituentAccount not found for key: ${key}`);
}
return result.data;
}
async mustGetWithSlot(key) {
if (!this.has(key)) {
await this.sync();
}
const result = this.constituentMap.get(key);
if (!result) {
throw new Error(`ConstituentAccount not found for key: ${key}`);
}
return result;
}
size() {
return this.constituentMap.size;
}
*values() {
for (const dataAndSlot of this.constituentMap.values()) {
yield dataAndSlot.data;
}
}
valuesWithSlot() {
return this.constituentMap.values();
}
*entries() {
for (const [key, dataAndSlot] of this.constituentMap.entries()) {
yield [key, dataAndSlot.data];
}
}
entriesWithSlot() {
return this.constituentMap.entries();
}
updateConstituentAccount(key, constituentAccount, slot) {
const existingData = this.getWithSlot(key);
if (existingData) {
if (slot >= existingData.slot) {
this.constituentMap.set(key, {
data: constituentAccount,
slot,
});
}
}
else {
this.constituentMap.set(key, {
data: constituentAccount,
slot,
});
}
this.constituentIndexToKeyMap.set(constituentAccount.constituentIndex, key);
this.spotMarketIndexToKeyMap.set(constituentAccount.spotMarketIndex, key);
}
}
exports.ConstituentMap = ConstituentMap;