@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;