@drift-labs/sdk-browser
Version:
SDK for Drift Protocol
182 lines (181 loc) • 7.08 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ReferrerMap = void 0;
const web3_js_1 = require("@solana/web3.js");
const pda_1 = require("../addresses/pda");
const memcmp_1 = require("../memcmp");
const bytes_1 = require("@coral-xyz/anchor/dist/cjs/utils/bytes");
const DEFAULT_PUBLIC_KEY = web3_js_1.PublicKey.default.toBase58();
class ReferrerMap {
/**
* Creates a new UserStatsMap instance.
*
* @param {DriftClient} driftClient - The DriftClient instance.
*/
constructor(driftClient, parallelSync) {
/**
* map from authority pubkey to referrer pubkey.
*/
this.authorityReferrerMap = new Map();
/**
* map from referrer pubkey to ReferrerInfo.
* Will be undefined if the referrer is not in the map yet.
*/
this.referrerReferrerInfoMap = new Map();
this.driftClient = driftClient;
this.parallelSync = parallelSync !== undefined ? parallelSync : true;
}
/**
* Subscribe to all UserStats accounts.
*/
async subscribe() {
if (this.size() > 0) {
return;
}
await this.driftClient.subscribe();
await this.sync();
}
has(authorityPublicKey) {
return this.authorityReferrerMap.has(authorityPublicKey);
}
get(authorityPublicKey) {
return this.getReferrer(authorityPublicKey);
}
async addReferrer(authority, referrer) {
if (referrer) {
this.authorityReferrerMap.set(authority, referrer);
}
else if (referrer === undefined) {
const userStatsAccountPublicKey = (0, pda_1.getUserStatsAccountPublicKey)(this.driftClient.program.programId, new web3_js_1.PublicKey(authority));
const buffer = (await this.driftClient.connection.getAccountInfo(userStatsAccountPublicKey, 'processed')).data;
const referrer = bytes_1.bs58.encode(buffer.subarray(40, 72));
this.addReferrer(authority, referrer);
}
}
/**
* Enforce that a UserStats will exist for the given authorityPublicKey,
* reading one from the blockchain if necessary.
* @param authorityPublicKey
* @returns
*/
async mustGet(authorityPublicKey) {
if (!this.has(authorityPublicKey)) {
await this.addReferrer(authorityPublicKey);
}
return this.getReferrer(authorityPublicKey);
}
getReferrer(authorityPublicKey) {
const referrer = this.authorityReferrerMap.get(authorityPublicKey);
if (!referrer) {
// return undefined if the referrer is not in the map
return undefined;
}
if (referrer === DEFAULT_PUBLIC_KEY) {
return undefined;
}
if (this.referrerReferrerInfoMap.has(referrer)) {
return this.referrerReferrerInfoMap.get(referrer);
}
const referrerKey = new web3_js_1.PublicKey(referrer);
const referrerInfo = {
referrer: (0, pda_1.getUserAccountPublicKeySync)(this.driftClient.program.programId, referrerKey, 0),
referrerStats: (0, pda_1.getUserStatsAccountPublicKey)(this.driftClient.program.programId, referrerKey),
};
this.referrerReferrerInfoMap.set(referrer, referrerInfo);
return referrerInfo;
}
size() {
return this.authorityReferrerMap.size;
}
numberOfReferred() {
return Array.from(this.authorityReferrerMap.values()).filter((referrer) => referrer !== DEFAULT_PUBLIC_KEY).length;
}
async sync() {
if (this.fetchPromise) {
return this.fetchPromise;
}
this.fetchPromise = new Promise((resolver) => {
this.fetchPromiseResolver = resolver;
});
try {
if (this.parallelSync) {
await Promise.all([
this.syncAll(),
this.syncReferrer((0, memcmp_1.getUserStatsIsReferredFilter)()),
this.syncReferrer((0, memcmp_1.getUserStatsIsReferredOrReferrerFilter)()),
]);
}
else {
await this.syncAll();
await this.syncReferrer((0, memcmp_1.getUserStatsIsReferredFilter)());
await this.syncReferrer((0, memcmp_1.getUserStatsIsReferredOrReferrerFilter)());
}
}
finally {
this.fetchPromiseResolver();
this.fetchPromise = undefined;
}
}
async syncAll() {
const rpcRequestArgs = [
this.driftClient.program.programId.toBase58(),
{
commitment: this.driftClient.opts.commitment,
filters: [(0, memcmp_1.getUserStatsFilter)()],
encoding: 'base64',
dataSlice: {
offset: 0,
length: 0,
},
withContext: true,
},
];
const rpcJSONResponse =
// @ts-ignore
await this.driftClient.connection._rpcRequest('getProgramAccounts', rpcRequestArgs);
const rpcResponseAndContext = rpcJSONResponse.result;
for (const account of rpcResponseAndContext.value) {
// only add if it isn't already in the map
// so that if syncReferrer already set it, we dont overwrite
if (!this.has(account.pubkey)) {
this.addReferrer(account.pubkey, DEFAULT_PUBLIC_KEY);
}
}
}
async syncReferrer(referrerFilter) {
const rpcRequestArgs = [
this.driftClient.program.programId.toBase58(),
{
commitment: this.driftClient.opts.commitment,
filters: [(0, memcmp_1.getUserStatsFilter)(), referrerFilter],
encoding: 'base64',
dataSlice: {
offset: 0,
length: 72,
},
withContext: true,
},
];
const rpcJSONResponse =
// @ts-ignore
await this.driftClient.connection._rpcRequest('getProgramAccounts', rpcRequestArgs);
const rpcResponseAndContext = rpcJSONResponse.result;
const batchSize = 1000;
for (let i = 0; i < rpcResponseAndContext.value.length; i += batchSize) {
const batch = rpcResponseAndContext.value.slice(i, i + batchSize);
await Promise.all(batch.map(async (programAccount) => {
// @ts-ignore
const buffer = Buffer.from(programAccount.account.data[0], programAccount.account.data[1]);
const authority = bytes_1.bs58.encode(buffer.subarray(8, 40));
const referrer = bytes_1.bs58.encode(buffer.subarray(40, 72));
this.addReferrer(authority, referrer);
}));
await new Promise((resolve) => setTimeout(resolve, 0));
}
}
async unsubscribe() {
this.authorityReferrerMap.clear();
this.referrerReferrerInfoMap.clear();
}
}
exports.ReferrerMap = ReferrerMap;