@drift-labs/sdk
Version:
SDK for Drift Protocol
933 lines • 280 kB
JavaScript
"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.