UNPKG

@drift-labs/sdk

Version:
933 lines • 280 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.DriftClient = void 0; const anchor = __importStar(require("@coral-xyz/anchor")); const anchor_1 = require("@coral-xyz/anchor"); const bs58_1 = __importDefault(require("bs58")); const spl_token_1 = require("@solana/spl-token"); const types_1 = require("./types"); const drift_json_1 = __importDefault(require("./idl/drift.json")); const web3_js_1 = require("@solana/web3.js"); const events_1 = require("events"); const pda_1 = require("./addresses/pda"); const types_2 = require("./accounts/types"); const numericConstants_1 = require("./constants/numericConstants"); const position_1 = require("./math/position"); const spotBalance_1 = require("./math/spotBalance"); const userName_1 = require("./userName"); const pollingDriftClientAccountSubscriber_1 = require("./accounts/pollingDriftClientAccountSubscriber"); const retryTxSender_1 = require("./tx/retryTxSender"); const user_1 = require("./user"); const config_1 = require("./config"); const spotMarkets_1 = require("./constants/spotMarkets"); const userStats_1 = require("./userStats"); const spotPosition_1 = require("./math/spotPosition"); const market_1 = require("./math/market"); const fetch_1 = require("./accounts/fetch"); const spotMarket_1 = require("./math/spotMarket"); const memcmp_1 = require("./memcmp"); const marinade_1 = require("./marinade"); const orderParams_1 = require("./orderParams"); const utils_1 = require("./math/utils"); const txParamProcessor_1 = require("./tx/txParamProcessor"); const oracles_1 = require("./math/oracles"); const txHandler_1 = require("./tx/txHandler"); const pyth_solana_receiver_1 = require("@pythnetwork/pyth-solana-receiver"); const price_service_sdk_1 = require("@pythnetwork/price-service-sdk"); const address_1 = require("@pythnetwork/pyth-solana-receiver/lib/address"); const pythOracleUtils_1 = require("./util/pythOracleUtils"); const ed25519Utils_1 = require("./util/ed25519Utils"); const utils_2 = require("./tx/utils"); const pyth_solana_receiver_json_1 = __importDefault(require("./idl/pyth_solana_receiver.json")); const on_demand_1 = require("@switchboard-xyz/on-demand"); const grpcDriftClientAccountSubscriber_1 = require("./accounts/grpcDriftClientAccountSubscriber"); const tweetnacl_1 = __importDefault(require("tweetnacl")); const oracleId_1 = require("./oracles/oracleId"); const sha256_1 = require("@noble/hashes/sha256"); const utils_3 = require("./oracles/utils"); const webSocketDriftClientAccountSubscriber_1 = require("./accounts/webSocketDriftClientAccountSubscriber"); /** * # DriftClient * This class is the main way to interact with Drift Protocol. It allows you to subscribe to the various accounts where the Market's state is stored, as well as: opening positions, liquidating, settling funding, depositing & withdrawing, and more. */ class DriftClient { get isSubscribed() { return this._isSubscribed && this.accountSubscriber.isSubscribed; } set isSubscribed(val) { this._isSubscribed = val; } constructor(config) { var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23; this.users = new Map(); this._isSubscribed = false; this.perpMarketLastSlotCache = new Map(); this.spotMarketLastSlotCache = new Map(); this.mustIncludePerpMarketIndexes = new Set(); this.mustIncludeSpotMarketIndexes = new Set(); this.connection = config.connection; this.wallet = config.wallet; this.env = (_a = config.env) !== null && _a !== void 0 ? _a : 'mainnet-beta'; this.opts = config.opts || { ...config_1.DEFAULT_CONFIRMATION_OPTS, }; this.useHotWalletAdmin = (_b = config.useHotWalletAdmin) !== null && _b !== void 0 ? _b : false; if ((_c = config === null || config === void 0 ? void 0 : config.connection) === null || _c === void 0 ? void 0 : _c.commitment) { // At the moment this ensures that our transaction simulations (which use Connection object) will use the same commitment level as our Transaction blockhashes (which use these opts) this.opts.commitment = config.connection.commitment; this.opts.preflightCommitment = config.connection.commitment; } this.provider = new anchor_1.AnchorProvider(config.connection, // @ts-ignore config.wallet, this.opts); this.program = new anchor_1.Program(drift_json_1.default, (_d = config.programID) !== null && _d !== void 0 ? _d : new web3_js_1.PublicKey(config_1.DRIFT_PROGRAM_ID), this.provider, config.coder); this.authority = (_e = config.authority) !== null && _e !== void 0 ? _e : this.wallet.publicKey; this.activeSubAccountId = (_f = config.activeSubAccountId) !== null && _f !== void 0 ? _f : 0; this.skipLoadUsers = (_g = config.skipLoadUsers) !== null && _g !== void 0 ? _g : false; this.txVersion = (_h = config.txVersion) !== null && _h !== void 0 ? _h : this.getTxVersionForNewWallet(config.wallet); this.txParams = { computeUnits: (_k = (_j = config.txParams) === null || _j === void 0 ? void 0 : _j.computeUnits) !== null && _k !== void 0 ? _k : 600000, computeUnitsPrice: (_m = (_l = config.txParams) === null || _l === void 0 ? void 0 : _l.computeUnitsPrice) !== null && _m !== void 0 ? _m : 0, }; this.txHandler = (_o = config === null || config === void 0 ? void 0 : config.txHandler) !== null && _o !== void 0 ? _o : new txHandler_1.TxHandler({ connection: this.connection, // @ts-ignore wallet: this.provider.wallet, confirmationOptions: this.opts, opts: { returnBlockHeightsWithSignedTxCallbackData: config.enableMetricsEvents, onSignedCb: this.handleSignedTransaction.bind(this), preSignedCb: this.handlePreSignedTransaction.bind(this), }, config: config.txHandlerConfig, }); if (config.includeDelegates && config.subAccountIds) { throw new Error('Can only pass one of includeDelegates or subAccountIds. If you want to specify subaccount ids for multiple authorities, pass authoritySubaccountMap instead'); } if (config.authoritySubAccountMap && config.subAccountIds) { throw new Error('Can only pass one of authoritySubaccountMap or subAccountIds'); } if (config.authoritySubAccountMap && config.includeDelegates) { throw new Error('Can only pass one of authoritySubaccountMap or includeDelegates'); } this.authoritySubAccountMap = config.authoritySubAccountMap ? config.authoritySubAccountMap : config.subAccountIds ? new Map([[this.authority.toString(), config.subAccountIds]]) : new Map(); this.includeDelegates = (_p = config.includeDelegates) !== null && _p !== void 0 ? _p : false; if (((_q = config.accountSubscription) === null || _q === void 0 ? void 0 : _q.type) === 'polling') { this.userAccountSubscriptionConfig = { type: 'polling', accountLoader: config.accountSubscription.accountLoader, }; this.userStatsAccountSubscriptionConfig = { type: 'polling', accountLoader: config.accountSubscription.accountLoader, }; } else if (((_r = config.accountSubscription) === null || _r === void 0 ? void 0 : _r.type) === 'grpc') { this.userAccountSubscriptionConfig = { type: 'grpc', resubTimeoutMs: (_s = config.accountSubscription) === null || _s === void 0 ? void 0 : _s.resubTimeoutMs, logResubMessages: (_t = config.accountSubscription) === null || _t === void 0 ? void 0 : _t.logResubMessages, grpcConfigs: (_u = config.accountSubscription) === null || _u === void 0 ? void 0 : _u.grpcConfigs, }; this.userStatsAccountSubscriptionConfig = { type: 'grpc', grpcConfigs: (_v = config.accountSubscription) === null || _v === void 0 ? void 0 : _v.grpcConfigs, resubTimeoutMs: (_w = config.accountSubscription) === null || _w === void 0 ? void 0 : _w.resubTimeoutMs, logResubMessages: (_x = config.accountSubscription) === null || _x === void 0 ? void 0 : _x.logResubMessages, }; } else { this.userAccountSubscriptionConfig = { type: 'websocket', resubTimeoutMs: (_y = config.accountSubscription) === null || _y === void 0 ? void 0 : _y.resubTimeoutMs, logResubMessages: (_z = config.accountSubscription) === null || _z === void 0 ? void 0 : _z.logResubMessages, commitment: (_0 = config.accountSubscription) === null || _0 === void 0 ? void 0 : _0.commitment, programUserAccountSubscriber: (_1 = config.accountSubscription) === null || _1 === void 0 ? void 0 : _1.programUserAccountSubscriber, }; this.userStatsAccountSubscriptionConfig = { type: 'websocket', resubTimeoutMs: (_2 = config.accountSubscription) === null || _2 === void 0 ? void 0 : _2.resubTimeoutMs, logResubMessages: (_3 = config.accountSubscription) === null || _3 === void 0 ? void 0 : _3.logResubMessages, commitment: (_4 = config.accountSubscription) === null || _4 === void 0 ? void 0 : _4.commitment, }; } if (config.userStats) { this.userStats = new userStats_1.UserStats({ driftClient: this, userStatsAccountPublicKey: (0, pda_1.getUserStatsAccountPublicKey)(this.program.programId, this.authority), accountSubscription: this.userAccountSubscriptionConfig, }); } this.marketLookupTable = config.marketLookupTable; if (!this.marketLookupTable) { this.marketLookupTable = new web3_js_1.PublicKey(config_1.configs[this.env].MARKET_LOOKUP_TABLE); } this.marketLookupTables = config.marketLookupTables; if (!this.marketLookupTables) { this.marketLookupTables = config_1.configs[this.env].MARKET_LOOKUP_TABLES.map((tableAddr) => new web3_js_1.PublicKey(tableAddr)); } const delistedMarketSetting = config.delistedMarketSetting || types_2.DelistedMarketSetting.Unsubscribe; const noMarketsAndOraclesSpecified = config.perpMarketIndexes === undefined && config.spotMarketIndexes === undefined && config.oracleInfos === undefined; if (((_5 = config.accountSubscription) === null || _5 === void 0 ? void 0 : _5.type) === 'polling') { this.accountSubscriber = new pollingDriftClientAccountSubscriber_1.PollingDriftClientAccountSubscriber(this.program, config.accountSubscription.accountLoader, (_6 = config.perpMarketIndexes) !== null && _6 !== void 0 ? _6 : [], (_7 = config.spotMarketIndexes) !== null && _7 !== void 0 ? _7 : [], (_8 = config.oracleInfos) !== null && _8 !== void 0 ? _8 : [], noMarketsAndOraclesSpecified, delistedMarketSetting); } else if (((_9 = config.accountSubscription) === null || _9 === void 0 ? void 0 : _9.type) === 'grpc') { this.accountSubscriber = new grpcDriftClientAccountSubscriber_1.gprcDriftClientAccountSubscriber(config.accountSubscription.grpcConfigs, this.program, (_10 = config.perpMarketIndexes) !== null && _10 !== void 0 ? _10 : [], (_11 = config.spotMarketIndexes) !== null && _11 !== void 0 ? _11 : [], (_12 = config.oracleInfos) !== null && _12 !== void 0 ? _12 : [], noMarketsAndOraclesSpecified, delistedMarketSetting, { resubTimeoutMs: (_13 = config.accountSubscription) === null || _13 === void 0 ? void 0 : _13.resubTimeoutMs, logResubMessages: (_14 = config.accountSubscription) === null || _14 === void 0 ? void 0 : _14.logResubMessages, }); } else { const accountSubscriberClass = (_16 = (_15 = config.accountSubscription) === null || _15 === void 0 ? void 0 : _15.driftClientAccountSubscriber) !== null && _16 !== void 0 ? _16 : webSocketDriftClientAccountSubscriber_1.WebSocketDriftClientAccountSubscriber; this.accountSubscriber = new accountSubscriberClass(this.program, (_17 = config.perpMarketIndexes) !== null && _17 !== void 0 ? _17 : [], (_18 = config.spotMarketIndexes) !== null && _18 !== void 0 ? _18 : [], (_19 = config.oracleInfos) !== null && _19 !== void 0 ? _19 : [], noMarketsAndOraclesSpecified, delistedMarketSetting, { resubTimeoutMs: (_20 = config.accountSubscription) === null || _20 === void 0 ? void 0 : _20.resubTimeoutMs, logResubMessages: (_21 = config.accountSubscription) === null || _21 === void 0 ? void 0 : _21.logResubMessages, }, (_22 = config.accountSubscription) === null || _22 === void 0 ? void 0 : _22.commitment); } this.eventEmitter = this.accountSubscriber.eventEmitter; this.metricsEventEmitter = new events_1.EventEmitter(); if (config.enableMetricsEvents) { this.enableMetricsEvents = true; } this.txSender = (_23 = config.txSender) !== null && _23 !== void 0 ? _23 : new retryTxSender_1.RetryTxSender({ connection: this.connection, wallet: this.wallet, opts: this.opts, txHandler: this.txHandler, }); this.sbOnDemandProgramdId = config_1.configs[this.env].SB_ON_DEMAND_PID; } getUserMapKey(subAccountId, authority) { return `${subAccountId}_${authority.toString()}`; } createUser(subAccountId, accountSubscriptionConfig, authority) { const userAccountPublicKey = (0, pda_1.getUserAccountPublicKeySync)(this.program.programId, authority !== null && authority !== void 0 ? authority : this.authority, subAccountId); return new user_1.User({ driftClient: this, userAccountPublicKey, accountSubscription: accountSubscriptionConfig, }); } async subscribe() { let subscribePromises = [this.addAndSubscribeToUsers()].concat(this.accountSubscriber.subscribe()); if (this.userStats !== undefined) { subscribePromises = subscribePromises.concat(this.userStats.subscribe()); } this.isSubscribed = (await Promise.all(subscribePromises)).reduce((success, prevSuccess) => success && prevSuccess); return this.isSubscribed; } subscribeUsers() { return [...this.users.values()].map((user) => user.subscribe()); } /** * Forces the accountSubscriber to fetch account updates from rpc */ async fetchAccounts() { let promises = [...this.users.values()] .map((user) => user.fetchAccounts()) .concat(this.accountSubscriber.fetch()); if (this.userStats) { promises = promises.concat(this.userStats.fetchAccounts()); } await Promise.all(promises); } async unsubscribe() { let unsubscribePromises = this.unsubscribeUsers().concat(this.accountSubscriber.unsubscribe()); if (this.userStats !== undefined) { unsubscribePromises = unsubscribePromises.concat(this.userStats.unsubscribe()); } await Promise.all(unsubscribePromises); this.isSubscribed = false; } unsubscribeUsers() { return [...this.users.values()].map((user) => user.unsubscribe()); } async getStatePublicKey() { if (this.statePublicKey) { return this.statePublicKey; } this.statePublicKey = await (0, pda_1.getDriftStateAccountPublicKey)(this.program.programId); return this.statePublicKey; } getSignerPublicKey() { if (this.signerPublicKey) { return this.signerPublicKey; } this.signerPublicKey = (0, pda_1.getDriftSignerPublicKey)(this.program.programId); return this.signerPublicKey; } getStateAccount() { return this.accountSubscriber.getStateAccountAndSlot().data; } /** * Forces a fetch to rpc before returning accounts. Useful for anchor tests. */ async forceGetStateAccount() { await this.accountSubscriber.fetch(); return this.accountSubscriber.getStateAccountAndSlot().data; } getPerpMarketAccount(marketIndex) { var _a; return (_a = this.accountSubscriber.getMarketAccountAndSlot(marketIndex)) === null || _a === void 0 ? void 0 : _a.data; } /** * Forces a fetch to rpc before returning accounts. Useful for anchor tests. * @param marketIndex */ async forceGetPerpMarketAccount(marketIndex) { var _a, _b; await this.accountSubscriber.fetch(); let data = (_a = this.accountSubscriber.getMarketAccountAndSlot(marketIndex)) === null || _a === void 0 ? void 0 : _a.data; let i = 0; while (data === undefined && i < 10) { await this.accountSubscriber.fetch(); data = (_b = this.accountSubscriber.getMarketAccountAndSlot(marketIndex)) === null || _b === void 0 ? void 0 : _b.data; i++; } return data; } getPerpMarketAccounts() { return this.accountSubscriber .getMarketAccountsAndSlots() .filter((value) => value !== undefined) .map((value) => value.data); } getSpotMarketAccount(marketIndex) { var _a; return (_a = this.accountSubscriber.getSpotMarketAccountAndSlot(marketIndex)) === null || _a === void 0 ? void 0 : _a.data; } /** * Forces a fetch to rpc before returning accounts. Useful for anchor tests. * @param marketIndex */ async forceGetSpotMarketAccount(marketIndex) { var _a; await this.accountSubscriber.fetch(); return (_a = this.accountSubscriber.getSpotMarketAccountAndSlot(marketIndex)) === null || _a === void 0 ? void 0 : _a.data; } getSpotMarketAccounts() { return this.accountSubscriber .getSpotMarketAccountsAndSlots() .filter((value) => value !== undefined) .map((value) => value.data); } getQuoteSpotMarketAccount() { return this.accountSubscriber.getSpotMarketAccountAndSlot(numericConstants_1.QUOTE_SPOT_MARKET_INDEX).data; } getOraclePriceDataAndSlot(oraclePublicKey, oracleSource) { return this.accountSubscriber.getOraclePriceDataAndSlot((0, oracleId_1.getOracleId)(oraclePublicKey, oracleSource)); } async getSerumV3FulfillmentConfig(serumMarket) { const address = await (0, pda_1.getSerumFulfillmentConfigPublicKey)(this.program.programId, serumMarket); return (await this.program.account.serumV3FulfillmentConfig.fetch(address)); } async getSerumV3FulfillmentConfigs() { const accounts = await this.program.account.serumV3FulfillmentConfig.all(); return accounts.map((account) => account.account); } async getPhoenixV1FulfillmentConfig(phoenixMarket) { const address = await (0, pda_1.getPhoenixFulfillmentConfigPublicKey)(this.program.programId, phoenixMarket); return (await this.program.account.phoenixV1FulfillmentConfig.fetch(address)); } async getPhoenixV1FulfillmentConfigs() { const accounts = await this.program.account.phoenixV1FulfillmentConfig.all(); return accounts.map((account) => account.account); } async getOpenbookV2FulfillmentConfig(openbookMarket) { const address = (0, pda_1.getOpenbookV2FulfillmentConfigPublicKey)(this.program.programId, openbookMarket); return (await this.program.account.openbookV2FulfillmentConfig.fetch(address)); } async getOpenbookV2FulfillmentConfigs() { const accounts = await this.program.account.openbookV2FulfillmentConfig.all(); return accounts.map((account) => account.account); } /** @deprecated use fetchAllLookupTableAccounts() */ async fetchMarketLookupTableAccount() { if (this.lookupTableAccount) return this.lookupTableAccount; if (!this.marketLookupTable) { console.log('Market lookup table address not set'); return; } const lookupTableAccount = (await this.connection.getAddressLookupTable(this.marketLookupTable)).value; this.lookupTableAccount = lookupTableAccount; return lookupTableAccount; } async fetchAllLookupTableAccounts() { if (this.lookupTableAccounts) return this.lookupTableAccounts; if (!this.marketLookupTables) { console.log('Market lookup table address not set'); return; } const lookupTableAccountResults = await Promise.all(this.marketLookupTables.map((lookupTable) => this.connection.getAddressLookupTable(lookupTable))); const lookupTableAccounts = lookupTableAccountResults.map((result) => result.value); this.lookupTableAccounts = lookupTableAccounts; return lookupTableAccounts; } getTxVersionForNewWallet(newWallet) { var _a, _b, _c; if (!(newWallet === null || newWallet === void 0 ? void 0 : newWallet.supportedTransactionVersions)) return 0; // Assume versioned txs supported if wallet doesn't have a supportedTransactionVersions property const walletSupportsVersionedTxns = ((_a = newWallet.supportedTransactionVersions) === null || _a === void 0 ? void 0 : _a.has(0)) || ((_c = (_b = newWallet.supportedTransactionVersions) === null || _b === void 0 ? void 0 : _b.size) !== null && _c !== void 0 ? _c : 0) > 1; return walletSupportsVersionedTxns ? 0 : 'legacy'; } /** * Update the wallet to use for drift transactions and linked user account * @param newWallet * @param subAccountIds * @param activeSubAccountId * @param includeDelegates */ async updateWallet(newWallet, subAccountIds, activeSubAccountId, includeDelegates, authoritySubaccountMap) { var _a; const newProvider = new anchor_1.AnchorProvider(this.connection, // @ts-ignore newWallet, this.opts); const newProgram = new anchor_1.Program(drift_json_1.default, this.program.programId, newProvider); this.skipLoadUsers = false; // Update provider for txSender with new wallet details this.txSender.wallet = newWallet; this.wallet = newWallet; this.txHandler.updateWallet(newWallet); this.provider = newProvider; this.program = newProgram; this.authority = newWallet.publicKey; this.activeSubAccountId = activeSubAccountId; this.userStatsAccountPublicKey = undefined; this.includeDelegates = includeDelegates !== null && includeDelegates !== void 0 ? includeDelegates : false; this.txVersion = this.getTxVersionForNewWallet(this.wallet); if (includeDelegates && subAccountIds) { throw new Error('Can only pass one of includeDelegates or subAccountIds. If you want to specify subaccount ids for multiple authorities, pass authoritySubaccountMap instead'); } if (authoritySubaccountMap && subAccountIds) { throw new Error('Can only pass one of authoritySubaccountMap or subAccountIds'); } if (authoritySubaccountMap && includeDelegates) { throw new Error('Can only pass one of authoritySubaccountMap or includeDelegates'); } this.authoritySubAccountMap = authoritySubaccountMap ? authoritySubaccountMap : subAccountIds ? new Map([[this.authority.toString(), subAccountIds]]) : new Map(); /* Reset user stats account */ if ((_a = this.userStats) === null || _a === void 0 ? void 0 : _a.isSubscribed) { await this.userStats.unsubscribe(); } this.userStats = undefined; this.userStats = new userStats_1.UserStats({ driftClient: this, userStatsAccountPublicKey: this.getUserStatsAccountPublicKey(), accountSubscription: this.userStatsAccountSubscriptionConfig, }); const subscriptionPromises = [this.userStats.subscribe()]; let success = true; if (this.isSubscribed) { const reSubscribeUsersPromise = async () => { await Promise.all(this.unsubscribeUsers()); this.users.clear(); success = await this.addAndSubscribeToUsers(); }; subscriptionPromises.push(reSubscribeUsersPromise()); } await Promise.all(subscriptionPromises); return success; } /** * Update the subscribed accounts to a given authority, while leaving the * connected wallet intact. This allows a user to emulate another user's * account on the UI and sign permissionless transactions with their own wallet. * @param emulateAuthority */ async emulateAccount(emulateAuthority) { var _a; this.skipLoadUsers = false; // Update provider for txSender with new wallet details this.authority = emulateAuthority; this.userStatsAccountPublicKey = undefined; this.includeDelegates = true; this.txVersion = this.getTxVersionForNewWallet(this.wallet); this.authoritySubAccountMap = new Map(); /* Reset user stats account */ if ((_a = this.userStats) === null || _a === void 0 ? void 0 : _a.isSubscribed) { await this.userStats.unsubscribe(); } this.userStats = undefined; this.userStats = new userStats_1.UserStats({ driftClient: this, userStatsAccountPublicKey: this.getUserStatsAccountPublicKey(), accountSubscription: this.userStatsAccountSubscriptionConfig, }); await this.userStats.subscribe(); let success = true; if (this.isSubscribed) { await Promise.all(this.unsubscribeUsers()); this.users.clear(); success = await this.addAndSubscribeToUsers(emulateAuthority); } return success; } async switchActiveUser(subAccountId, authority) { var _a; const authorityChanged = authority && !((_a = this.authority) === null || _a === void 0 ? void 0 : _a.equals(authority)); this.activeSubAccountId = subAccountId; this.authority = authority !== null && authority !== void 0 ? authority : this.authority; this.userStatsAccountPublicKey = (0, pda_1.getUserStatsAccountPublicKey)(this.program.programId, this.authority); /* If changing the user authority ie switching from delegate to non-delegate account, need to re-subscribe to the user stats account */ if (authorityChanged && this.userStats) { if (this.userStats.isSubscribed) { await this.userStats.unsubscribe(); } this.userStats = new userStats_1.UserStats({ driftClient: this, userStatsAccountPublicKey: this.userStatsAccountPublicKey, accountSubscription: this.userAccountSubscriptionConfig, }); this.userStats.subscribe(); } } async addUser(subAccountId, authority, userAccount) { authority = authority !== null && authority !== void 0 ? authority : this.authority; const userKey = this.getUserMapKey(subAccountId, authority); if (this.users.has(userKey) && this.users.get(userKey).isSubscribed) { return true; } const user = this.createUser(subAccountId, this.userAccountSubscriptionConfig, authority); const result = await user.subscribe(userAccount); if (result) { this.users.set(userKey, user); return true; } else { return false; } } /** * Adds and subscribes to users based on params set by the constructor or by updateWallet. */ async addAndSubscribeToUsers(authority) { var _a, _b, _c, _d, _e, _f, _g; // save the rpc calls if driftclient is initialized without a real wallet if (this.skipLoadUsers) return true; let result = true; if (this.authoritySubAccountMap && this.authoritySubAccountMap.size > 0) { this.authoritySubAccountMap.forEach(async (value, key) => { for (const subAccountId of value) { result = result && (await this.addUser(subAccountId, new web3_js_1.PublicKey(key))); } }); if (this.activeSubAccountId == undefined) { this.switchActiveUser((_a = [...this.authoritySubAccountMap.values()][0][0]) !== null && _a !== void 0 ? _a : 0, new web3_js_1.PublicKey((_b = [...this.authoritySubAccountMap.keys()][0]) !== null && _b !== void 0 ? _b : this.authority.toString())); } } else { let userAccounts = []; let delegatedAccounts = []; const userAccountsPromise = this.getUserAccountsForAuthority(authority !== null && authority !== void 0 ? authority : this.wallet.publicKey); if (this.includeDelegates) { const delegatedAccountsPromise = this.getUserAccountsForDelegate(authority !== null && authority !== void 0 ? authority : this.wallet.publicKey); [userAccounts, delegatedAccounts] = await Promise.all([ userAccountsPromise, delegatedAccountsPromise, ]); !userAccounts && (userAccounts = []); !delegatedAccounts && (delegatedAccounts = []); } else { userAccounts = (_c = (await userAccountsPromise)) !== null && _c !== void 0 ? _c : []; } const allAccounts = userAccounts.concat(delegatedAccounts); const addAllAccountsPromise = allAccounts.map((acc) => this.addUser(acc.subAccountId, acc.authority, acc)); const addAllAccountsResults = await Promise.all(addAllAccountsPromise); result = addAllAccountsResults.every((res) => !!res); if (this.activeSubAccountId == undefined) { this.switchActiveUser((_e = (_d = userAccounts.concat(delegatedAccounts)[0]) === null || _d === void 0 ? void 0 : _d.subAccountId) !== null && _e !== void 0 ? _e : 0, (_g = (_f = userAccounts.concat(delegatedAccounts)[0]) === null || _f === void 0 ? void 0 : _f.authority) !== null && _g !== void 0 ? _g : this.authority); } } return result; } /** * Returns the instructions to initialize a user account and the public key of the user account. * @param subAccountId * @param name * @param referrerInfo * @returns [instructions, userAccountPublicKey] */ async getInitializeUserAccountIxs(subAccountId = 0, name, referrerInfo, poolId) { const initializeIxs = []; const [userAccountPublicKey, initializeUserAccountIx] = await this.getInitializeUserInstructions(subAccountId, name, referrerInfo); if (subAccountId === 0) { if (!(await this.checkIfAccountExists(this.getUserStatsAccountPublicKey()))) { initializeIxs.push(await this.getInitializeUserStatsIx()); } } initializeIxs.push(initializeUserAccountIx); if (poolId) { initializeIxs.push(await this.getUpdateUserPoolIdIx(poolId, subAccountId)); } return [initializeIxs, userAccountPublicKey]; } /** * Initializes a user account and returns the transaction signature and the public key of the user account. * @param subAccountId * @param name * @param referrerInfo * @param txParams * @returns [transactionSignature, userAccountPublicKey] */ async initializeUserAccount(subAccountId = 0, name, referrerInfo, txParams) { const [initializeIxs, userAccountPublicKey] = await this.getInitializeUserAccountIxs(subAccountId, name, referrerInfo); const tx = await this.buildTransaction(initializeIxs, txParams); const { txSig } = await this.sendTransaction(tx, [], this.opts); await this.addUser(subAccountId); return [txSig, userAccountPublicKey]; } async getInitializeUserStatsIx() { return await this.program.instruction.initializeUserStats({ accounts: { userStats: (0, pda_1.getUserStatsAccountPublicKey)(this.program.programId, this.wallet.publicKey // only allow payer to initialize own user stats account ), authority: this.wallet.publicKey, payer: this.wallet.publicKey, rent: anchor.web3.SYSVAR_RENT_PUBKEY, systemProgram: anchor.web3.SystemProgram.programId, state: await this.getStatePublicKey(), }, }); } async initializeSignedMsgUserOrders(authority, numOrders, txParams) { const initializeIxs = []; const [signedMsgUserAccountPublicKey, initializeUserAccountIx] = await this.getInitializeSignedMsgUserOrdersAccountIx(authority, numOrders); initializeIxs.push(initializeUserAccountIx); const tx = await this.buildTransaction(initializeIxs, txParams); const { txSig } = await this.sendTransaction(tx, [], this.opts); return [txSig, signedMsgUserAccountPublicKey]; } async getInitializeSignedMsgUserOrdersAccountIx(authority, numOrders) { const signedMsgUserAccountPublicKey = (0, pda_1.getSignedMsgUserAccountPublicKey)(this.program.programId, authority); const initializeUserAccountIx = await this.program.instruction.initializeSignedMsgUserOrders(numOrders, { accounts: { signedMsgUserOrders: signedMsgUserAccountPublicKey, authority, payer: this.wallet.publicKey, rent: anchor.web3.SYSVAR_RENT_PUBKEY, systemProgram: anchor.web3.SystemProgram.programId, }, }); return [signedMsgUserAccountPublicKey, initializeUserAccountIx]; } async resizeSignedMsgUserOrders(authority, numOrders, userSubaccountId, txParams) { const resizeUserAccountIx = await this.getResizeSignedMsgUserOrdersInstruction(authority, numOrders, userSubaccountId); const tx = await this.buildTransaction([resizeUserAccountIx], txParams); const { txSig } = await this.sendTransaction(tx, [], this.opts); return txSig; } async getResizeSignedMsgUserOrdersInstruction(authority, numOrders, userSubaccountId) { const signedMsgUserAccountPublicKey = (0, pda_1.getSignedMsgUserAccountPublicKey)(this.program.programId, authority); const resizeUserAccountIx = await this.program.instruction.resizeSignedMsgUserOrders(numOrders, { accounts: { signedMsgUserOrders: signedMsgUserAccountPublicKey, authority, payer: this.wallet.publicKey, systemProgram: anchor.web3.SystemProgram.programId, user: await (0, pda_1.getUserAccountPublicKey)(this.program.programId, authority, userSubaccountId), }, }); return resizeUserAccountIx; } async initializeSignedMsgWsDelegatesAccount(authority, delegates = [], txParams) { const ix = await this.getInitializeSignedMsgWsDelegatesAccountIx(authority, delegates); const tx = await this.buildTransaction([ix], txParams); const { txSig } = await this.sendTransaction(tx, [], this.opts); return txSig; } async getInitializeSignedMsgWsDelegatesAccountIx(authority, delegates = []) { const signedMsgWsDelegates = (0, pda_1.getSignedMsgWsDelegatesAccountPublicKey)(this.program.programId, authority); const ix = await this.program.instruction.initializeSignedMsgWsDelegates(delegates, { accounts: { signedMsgWsDelegates, authority: this.wallet.publicKey, rent: anchor.web3.SYSVAR_RENT_PUBKEY, systemProgram: anchor.web3.SystemProgram.programId, }, }); return ix; } async addSignedMsgWsDelegate(authority, delegate, txParams) { const ix = await this.getAddSignedMsgWsDelegateIx(authority, delegate); const tx = await this.buildTransaction([ix], txParams); const { txSig } = await this.sendTransaction(tx, [], this.opts); return txSig; } async getAddSignedMsgWsDelegateIx(authority, delegate) { const signedMsgWsDelegates = (0, pda_1.getSignedMsgWsDelegatesAccountPublicKey)(this.program.programId, authority); const ix = await this.program.instruction.changeSignedMsgWsDelegateStatus(delegate, true, { accounts: { signedMsgWsDelegates, authority: this.wallet.publicKey, systemProgram: anchor.web3.SystemProgram.programId, }, }); return ix; } async removeSignedMsgWsDelegate(authority, delegate, txParams) { const ix = await this.getRemoveSignedMsgWsDelegateIx(authority, delegate); const tx = await this.buildTransaction([ix], txParams); const { txSig } = await this.sendTransaction(tx, [], this.opts); return txSig; } async getRemoveSignedMsgWsDelegateIx(authority, delegate) { const signedMsgWsDelegates = (0, pda_1.getSignedMsgWsDelegatesAccountPublicKey)(this.program.programId, authority); const ix = await this.program.instruction.changeSignedMsgWsDelegateStatus(delegate, false, { accounts: { signedMsgWsDelegates, authority: this.wallet.publicKey, systemProgram: anchor.web3.SystemProgram.programId, }, }); return ix; } async initializeFuelOverflow(authority) { const ix = await this.getInitializeFuelOverflowIx(authority); const tx = await this.buildTransaction([ix], this.txParams); const { txSig } = await this.sendTransaction(tx, [], this.opts); return txSig; } async getInitializeFuelOverflowIx(authority) { return await this.program.instruction.initializeFuelOverflow({ accounts: { fuelOverflow: (0, pda_1.getFuelOverflowAccountPublicKey)(this.program.programId, authority !== null && authority !== void 0 ? authority : this.wallet.publicKey), userStats: (0, pda_1.getUserStatsAccountPublicKey)(this.program.programId, authority !== null && authority !== void 0 ? authority : this.wallet.publicKey), authority: authority !== null && authority !== void 0 ? authority : this.wallet.publicKey, payer: this.wallet.publicKey, rent: anchor.web3.SYSVAR_RENT_PUBKEY, systemProgram: anchor.web3.SystemProgram.programId, }, }); } async sweepFuel(authority) { const ix = await this.getSweepFuelIx(authority); const tx = await this.buildTransaction([ix], this.txParams); const { txSig } = await this.sendTransaction(tx, [], this.opts); return txSig; } async getSweepFuelIx(authority) { return await this.program.instruction.sweepFuel({ accounts: { fuelOverflow: (0, pda_1.getFuelOverflowAccountPublicKey)(this.program.programId, authority !== null && authority !== void 0 ? authority : this.wallet.publicKey), userStats: (0, pda_1.getUserStatsAccountPublicKey)(this.program.programId, authority !== null && authority !== void 0 ? authority : this.wallet.publicKey), authority: authority !== null && authority !== void 0 ? authority : this.wallet.publicKey, signer: this.wallet.publicKey, }, }); } async getInitializeUserInstructions(subAccountId = 0, name, referrerInfo) { const userAccountPublicKey = await (0, pda_1.getUserAccountPublicKey)(this.program.programId, this.wallet.publicKey, subAccountId); const remainingAccounts = new Array(); if (referrerInfo !== undefined) { remainingAccounts.push({ pubkey: referrerInfo.referrer, isWritable: true, isSigner: false, }); remainingAccounts.push({ pubkey: referrerInfo.referrerStats, isWritable: true, isSigner: false, }); } const state = this.getStateAccount(); if (!state.whitelistMint.equals(web3_js_1.PublicKey.default)) { const associatedTokenPublicKey = await (0, spl_token_1.getAssociatedTokenAddress)(state.whitelistMint, this.wallet.publicKey); remainingAccounts.push({ pubkey: associatedTokenPublicKey, isWritable: false, isSigner: false, }); } if (name === undefined) { if (subAccountId === 0) { name = userName_1.DEFAULT_USER_NAME; } else { name = `Subaccount ${subAccountId + 1}`; } } const nameBuffer = (0, userName_1.encodeName)(name); const initializeUserAccountIx = await this.program.instruction.initializeUser(subAccountId, nameBuffer, { accounts: { user: userAccountPublicKey, userStats: this.getUserStatsAccountPublicKey(), authority: this.wallet.publicKey, payer: this.wallet.publicKey, rent: anchor.web3.SYSVAR_RENT_PUBKEY, systemProgram: anchor.web3.SystemProgram.programId, state: await this.getStatePublicKey(), }, remainingAccounts, }); return [userAccountPublicKey, initializeUserAccountIx]; } async getNextSubAccountId() { const userStats = this.getUserStats(); let userStatsAccount; if (!userStats) { userStatsAccount = await (0, fetch_1.fetchUserStatsAccount)(this.connection, this.program, this.wallet.publicKey); } else { userStatsAccount = userStats.getAccount(); } return userStatsAccount.numberOfSubAccountsCreated; } async initializeReferrerName(name) { const userAccountPublicKey = (0, pda_1.getUserAccountPublicKeySync)(this.program.programId, this.wallet.publicKey, 0); const nameBuffer = (0, userName_1.encodeName)(name); const referrerNameAccountPublicKey = (0, pda_1.getReferrerNamePublicKeySync)(this.program.programId, nameBuffer); const tx = await this.program.transaction.initializeReferrerName(nameBuffer, { accounts: { referrerName: referrerNameAccountPublicKey, user: userAccountPublicKey, authority: this.wallet.publicKey, userStats: this.getUserStatsAccountPublicKey(), payer: this.wallet.publicKey, rent: anchor.web3.SYSVAR_RENT_PUBKEY, systemProgram: anchor.web3.SystemProgram.programId, }, }); const { txSig } = await this.sendTransaction(tx, [], this.opts); return txSig; } async updateUserName(name, subAccountId = 0) { const userAccountPublicKey = (0, pda_1.getUserAccountPublicKeySync)(this.program.programId, this.wallet.publicKey, subAccountId); const nameBuffer = (0, userName_1.encodeName)(name); const tx = await this.program.transaction.updateUserName(subAccountId, nameBuffer, { accounts: { user: userAccountPublicKey, authority: this.wallet.publicKey, }, }); const { txSig } = await this.sendTransaction(tx, [], this.opts); return txSig; } async updateUserCustomMarginRatio(updates, txParams) { const ixs = await Promise.all(updates.map(async ({ marginRatio, subAccountId }) => { const ix = await this.getUpdateUserCustomMarginRatioIx(marginRatio, subAccountId); return ix; })); const tx = await this.buildTransaction(ixs, txParams !== null && txParams !== void 0 ? txParams : this.txParams); const { txSig } = await this.sendTransaction(tx, [], this.opts); return txSig; } async getUpdateUserCustomMarginRatioIx(marginRatio, subAccountId = 0) { const userAccountPublicKey = (0, pda_1.getUserAccountPublicKeySync)(this.program.programId, this.wallet.publicKey, subAccountId); await this.addUser(subAccountId, this.wallet.publicKey); const ix = this.program.instruction.updateUserCustomMarginRatio(subAccountId, marginRatio, { accounts: { user: userAccountPublicKey, authority: this.wallet.publicKey, }, }); return ix; } async getUpdateUserPerpPositionCustomMarginRatioIx(perpMarketIndex, marginRatio, subAccountId = 0) { const userAccountPublicKey = (0, pda_1.getUserAccountPublicKeySync)(this.program.programId, this.wallet.publicKey, subAccountId); await this.addUser(subAccountId, this.wallet.publicKey); const ix = this.program.instruction.updateUserPerpPositionCustomMarginRatio(subAccountId, perpMarketIndex, marginRatio, { accounts: { user: userAccountPublicKey, authority: this.wallet.publicKey, }, }); return ix; } async updateUserPerpPositionCustomMarginRatio(perpMarketIndex, marginRatio, subAccountId = 0, txParams) { const ix = await this.getUpdateUserPerpPositionCustomMarginRatioIx(perpMarketIndex, marginRatio, subAccountId); const tx = await this.buildTransaction(ix, txParams !== null && txParams !== void 0 ? txParams : this.txParams); const { txSig } = await this.sendTransaction(tx, [], this.opts); return txSig; } async getUpdateUserMarginTradingEnabledIx(marginTradingEnabled, subAccountId = 0, userAccountPublicKey) { const userAccountPublicKeyToUse = userAccountPublicKey || (0, pda_1.getUserAccountPublicKeySync)(this.program.programId, this.wallet.publicKey, subAccountId); await this.addUser(subAccountId, this.wallet.publicKey); let remainingAccounts; try { remainingAccounts = this.getRemainingAccounts({ userAccounts: [this.getUserAccount(subAccountId)], }); } catch (err) { remainingAccounts = []; } return await this.program.instruction.updateUserMarginTradingEnabled(subAccountId, marginTradingEnabled, { accounts: { user: userAccountPublicKeyToUse, authority: this.wallet.publicKey, }, remainingAccounts, }); } async updateUserMarginTradingEnabled(updates) { const ixs = await Promise.all(updates.map(async ({ marginTradingEnabled, subAccountId }) => { return await this.getUpdateUserMarginTradingEnabledIx(marginTradingEnabled, subAccountId); })); const tx = await this.buildTransaction(ixs, this.txParams); const { txSig } = await this.sendTransaction(tx, [], this.opts); return txSig; } async getUpdateUserDelegateIx(delegate, overrides) { var _a, _b, _c; const subAccountId = (_a = overrides.subAccountId) !== null && _a !== void 0 ? _a : this.activeSubAccountId; const userAccountPublicKey = (_b = overrides.userAccountPublicKey) !== null && _b !== void 0 ? _b : (await this.getUserAccountPublicKey()); const authority = (_c = overrides.authority) !== null && _c !== void 0 ? _c : this.wallet.publicKey; return await this.program.instruction.updateUserDelegate(subAccountId, delegate, { accounts: { user: userAccountPublicKey, authority, }, }); } async updateUserDelegate(delegate, subAccountId = 0) { const tx = await this.program.transaction.updateUserDelegate(subAccountId, delegate, { accounts: { user: await this.getUserAccountPublicKey(), authority: this.