@drift-labs/vaults-sdk 0.1.107 → 0.1.109

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/cli/cli.ts CHANGED
@@ -17,6 +17,7 @@ import {
17
17
  listDepositorsForVault,
18
18
  managerUpdateMarginTradingEnabled,
19
19
  decodeLogs,
20
+ vaultInvariantChecks,
20
21
  } from "./commands";
21
22
 
22
23
  import { Command, Option } from 'commander';
@@ -147,6 +148,11 @@ program
147
148
  .description("Decode program logs from a txid")
148
149
  .addOption(new Option("--tx <tx>", "Transaction hash").makeOptionMandatory(true))
149
150
  .action((opts) => decodeLogs(program, opts));
151
+ program
152
+ .command("check-invariants")
153
+ .description("Perform sanity checks on vault/depositor invariants")
154
+ .addOption(new Option("--vault-address <address>", "Vault address").makeOptionMandatory(true))
155
+ .action((opts) => vaultInvariantChecks(program, opts));
150
156
 
151
157
  program.parseAsync().then(() => {
152
158
  process.exit(0);
@@ -1,4 +1,4 @@
1
- import { PublicKey, TransactionInstruction } from "@solana/web3.js";
1
+ import { ComputeBudgetProgram, PublicKey, Transaction, TransactionInstruction } from "@solana/web3.js";
2
2
  import {
3
3
  OptionValues,
4
4
  Command
@@ -19,27 +19,45 @@ export const applyProfitShare = async (program: Command, cmdOpts: OptionValues)
19
19
  driftVault
20
20
  } = await getCommandContext(program, true);
21
21
 
22
- const allVaultDepositors = await driftVault.getAllVaultDepositors(vaultAddress);
23
- // console.log(allVaultDepositors);
24
-
25
- console.log(`Cranking profit share for ${allVaultDepositors.length} depositors...`);
22
+ const vdToRealizeProfit = await driftVault.getAllVaultDepositorsWithNoWithdrawRequest(vaultAddress);
23
+ console.log(`Applying profit share for ${vdToRealizeProfit.length} depositors...`);
26
24
 
27
25
  const chunkSize = 10;
28
26
  const ixChunks: Array<Array<TransactionInstruction>> = [];
29
- for (let i = 0; i < allVaultDepositors.length; i += chunkSize) {
30
- const chunk = allVaultDepositors.slice(i, i + chunkSize);
27
+ for (let i = 0; i < vdToRealizeProfit.length; i += chunkSize) {
28
+ const chunk = vdToRealizeProfit.slice(i, i + chunkSize);
31
29
  const ixs = await Promise.all(chunk.map((vaultDepositor) => {
32
30
  return driftVault.getApplyProfitShareIx(vaultAddress, vaultDepositor.publicKey);
33
31
  }));
34
32
 
35
33
  ixChunks.push(ixs);
36
34
  }
37
- console.log(`Cranking ${ixChunks.length} of ${chunkSize} depositors at a time...`);
35
+ console.log(`Sending ${ixChunks.length} transactions...`);
36
+
37
+ for (let i = 0; i < ixChunks.length; i++) {
38
+ const ixs = ixChunks[i];
39
+ try {
40
+ ixs.unshift(ComputeBudgetProgram.setComputeUnitLimit({
41
+ units: 1_400_000,
42
+ }));
43
+ ixs.unshift(ComputeBudgetProgram.setComputeUnitPrice({
44
+ microLamports: 100,
45
+ }));
46
+
47
+ const tx = new Transaction();
48
+ tx.add(...ixs);
49
+ const { txSig } = await driftVault.driftClient.sendTransaction(
50
+ tx,
51
+ [],
52
+ driftVault.driftClient.opts
53
+ );
54
+
55
+ console.log(`[${i}]: https://solscan.io/tx/${txSig}`);
56
+
57
+ } catch (e) {
58
+ console.error(e);
59
+ continue;
60
+ }
38
61
 
39
- const txs = await Promise.all(ixChunks.map((ixs) => driftVault.createAndSendTxn(ixs, {
40
- units: 2_000_000
41
- })));
42
- for (const tx of txs) {
43
- console.log(`Crank tx: https://solscan.io/tx/${tx}`);
44
62
  }
45
63
  };
@@ -15,4 +15,6 @@ export * from './forceWithdraw';
15
15
  export * from './withdraw';
16
16
  export * from './listDepositorsForVault';
17
17
  export * from './managerUpdateMarginTradingEnabled';
18
- export * from './decodeLogs';
18
+ export * from './decodeLogs';
19
+ export * from './vaultInvariantChecks';
20
+
@@ -0,0 +1,83 @@
1
+ import { PublicKey } from "@solana/web3.js";
2
+ import {
3
+ OptionValues,
4
+ Command
5
+ } from "commander";
6
+ import { getCommandContext } from "../utils";
7
+ import { BN, convertToNumber } from "@drift-labs/sdk";
8
+ import {
9
+ calculateApplyProfitShare,
10
+ } from "../../src/math";
11
+
12
+ export const vaultInvariantChecks = async (program: Command, cmdOpts: OptionValues) => {
13
+
14
+ let vaultAddress: PublicKey;
15
+ try {
16
+ vaultAddress = new PublicKey(cmdOpts.vaultAddress as string);
17
+ } catch (err) {
18
+ console.error("Invalid vault address");
19
+ process.exit(1);
20
+ }
21
+
22
+ const {
23
+ driftVault
24
+ } = await getCommandContext(program, true);
25
+
26
+ /*
27
+ Invariants:
28
+ * sum(vault_depositors.shares) == vault.user_shares
29
+ * sum(vault_depositors.profit_share_paid) == vault.manager_total_profit_share
30
+ */
31
+
32
+
33
+ const vault = await driftVault.getVault(vaultAddress);
34
+ const vaultEquity = await driftVault.calculateVaultEquity({
35
+ vault,
36
+ });
37
+ const spotMarket = driftVault.driftClient.getSpotMarketAccount(vault.spotMarketIndex);
38
+ const spotPrecision = new BN(10).pow(new BN(spotMarket!.decimals));
39
+
40
+ let allVaultDepositors = await driftVault.getAllVaultDepositors(vaultAddress);
41
+
42
+ // Sort allVaultDepositors by vaultShares in descending order
43
+ allVaultDepositors = allVaultDepositors.sort((a, b) => b.account.vaultShares.cmp(a.account.vaultShares));
44
+
45
+ let totalUserShares = new BN(0);
46
+ let totalUserProfitSharePaid = new BN(0);
47
+ let totalUserProfitShareSharesPaid = new BN(0);
48
+ let totalPendingProfitShareAmount = new BN(0);
49
+
50
+ for (const vd of allVaultDepositors) {
51
+ totalUserShares = totalUserShares.add(vd.account.vaultShares);
52
+ if (!vd.account.lastWithdrawRequest.shares.eq(new BN(0))) {
53
+ const pct = vd.account.lastWithdrawRequest.shares.toNumber() / vd.account.vaultShares.toNumber();
54
+ console.log(`Vd has withdrawal ${vd.publicKey.toBase58()} (auth: ${vd.account.authority.toBase58()}): ${vd.account.lastWithdrawRequest.shares.toString()} ($${convertToNumber(vd.account.lastWithdrawRequest.value, spotPrecision)}), ${(pct * 100.00).toFixed(2)}%`);
55
+ }
56
+
57
+ if (!vd.account.cumulativeProfitShareAmount.eq(new BN(0))) {
58
+ // const profitSharePaid = vd.account.profitShareFeePaid.toNumber() / vd.account.cumulativeProfitShareAmount.toNumber();
59
+ // console.log(`Profit share paid: ${vd.publicKey.toBase58()} (auth: ${vd.account.authority.toBase58()}): ${Math.ceil(profitSharePaid * 100.0)}%`);
60
+ }
61
+ totalUserProfitSharePaid = totalUserProfitSharePaid.add(vd.account.profitShareFeePaid);
62
+ totalUserProfitShareSharesPaid = totalUserProfitShareSharesPaid.add(vd.account.cumulativeProfitShareAmount);
63
+
64
+ const pendingProfitShares = calculateApplyProfitShare(vd.account, vaultEquity, vault);
65
+ totalPendingProfitShareAmount = totalPendingProfitShareAmount.add(pendingProfitShares.profitShareAmount);
66
+
67
+ console.log(`Pending profit shares: ${vd.publicKey.toBase58()} (auth: ${vd.account.authority.toBase58()}): $${convertToNumber(pendingProfitShares.profitShareAmount, spotPrecision)}`);
68
+ }
69
+ console.log(`==== Vault Depositor Shares == vault.user_shares ====`);
70
+ console.log(`total vd shares: ${totalUserShares.toString()}`);
71
+ console.log(`total vault usershares: ${vault.userShares.toString()}`);
72
+ console.log(`diff: ${vault.userShares.sub(totalUserShares)}`);
73
+
74
+ console.log(``);
75
+ console.log(`==== Vault Depositor ProfitSharePaid == vault.manager_total_profit_share ====`);
76
+ console.log(`total vault d profitshares: ${totalUserProfitSharePaid.toString()}`);
77
+ console.log(`vault total profit shares: ${vault.managerTotalProfitShare.toString()}`);
78
+ console.log(`diff: ${vault.managerTotalProfitShare.sub(totalUserProfitSharePaid)}`);
79
+
80
+ console.log(``);
81
+ console.log(`==== Pending profit shares to realize ====`);
82
+ console.log(`${convertToNumber(totalPendingProfitShareAmount, spotPrecision)}`);
83
+ };
@@ -4,7 +4,7 @@ import {
4
4
  Command
5
5
  } from "commander";
6
6
  import { getCommandContext, printVault } from "../utils";
7
- import { QUOTE_PRECISION, convertToNumber } from "@drift-labs/sdk";
7
+ import { BN, PRICE_PRECISION, QUOTE_PRECISION, TEN, convertToNumber, decodeName } from "@drift-labs/sdk";
8
8
 
9
9
  export const viewVault = async (program: Command, cmdOpts: OptionValues) => {
10
10
 
@@ -17,14 +17,38 @@ export const viewVault = async (program: Command, cmdOpts: OptionValues) => {
17
17
  }
18
18
 
19
19
  const {
20
- driftVault
20
+ driftVault,
21
+ driftClient,
21
22
  } = await getCommandContext(program, false);
22
23
 
24
+
23
25
  const vault = await driftVault.getVault(address);
24
26
  const { managerSharePct } = printVault(vault);
25
27
  const vaultEquity = await driftVault.calculateVaultEquity({
26
28
  vault,
27
29
  });
28
- console.log(`vaultEquity: $${convertToNumber(vaultEquity, QUOTE_PRECISION)}`);
29
- console.log(`manager share: $${managerSharePct * convertToNumber(vaultEquity, QUOTE_PRECISION)}`);
30
+
31
+ const spotMarket = driftClient.getSpotMarketAccount(vault.spotMarketIndex);
32
+ if (!spotMarket) {
33
+ throw new Error(`Spot market ${vault.spotMarketIndex} not found`);
34
+ }
35
+ const spotOracle = driftClient.getOracleDataForSpotMarket(vault.spotMarketIndex);
36
+ if (!spotOracle) {
37
+ throw new Error(`Spot oracle ${vault.spotMarketIndex} not found`);
38
+ }
39
+ const oraclePriceNum = convertToNumber(spotOracle.price, PRICE_PRECISION);
40
+ const spotPrecision = TEN.pow(new BN(spotMarket.decimals));
41
+ const spotSymbol = decodeName(spotMarket.name);
42
+
43
+ const vaultEquityNum = convertToNumber(vaultEquity, QUOTE_PRECISION);
44
+ const netDepositsNum = convertToNumber(vault.netDeposits, spotPrecision);
45
+ console.log(`vaultEquity (USDC): $${vaultEquityNum}`);
46
+ console.log(`manager share (USDC): $${managerSharePct * vaultEquityNum}`);
47
+ console.log(`vault PnL (USDC): $${vaultEquityNum - netDepositsNum}`);
48
+
49
+ const vaultEquitySpot = vaultEquityNum / oraclePriceNum;
50
+
51
+ console.log(`vaultEquity (${spotSymbol}): ${vaultEquitySpot}`);
52
+ console.log(`manager share (${spotSymbol}): ${managerSharePct * vaultEquitySpot}`);
53
+ console.log(`vault PnL (${spotSymbol}): ${vaultEquitySpot - netDepositsNum}`);
30
54
  };
package/lib/index.d.ts CHANGED
@@ -8,3 +8,4 @@ export * from './types/types';
8
8
  export * from './constants';
9
9
  export * from './parsers';
10
10
  export * from './types/drift_vaults';
11
+ export * from './math';
package/lib/index.js CHANGED
@@ -24,3 +24,4 @@ __exportStar(require("./types/types"), exports);
24
24
  __exportStar(require("./constants"), exports);
25
25
  __exportStar(require("./parsers"), exports);
26
26
  __exportStar(require("./types/drift_vaults"), exports);
27
+ __exportStar(require("./math"), exports);
@@ -0,0 +1 @@
1
+ export * from './vaultDepositor';
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./vaultDepositor"), exports);
@@ -0,0 +1,15 @@
1
+ /// <reference types="bn.js" />
2
+ import { BN } from '@drift-labs/sdk';
3
+ import { Vault, VaultDepositor } from '../types/types';
4
+ /**
5
+ * Calculates the unrealized profitShare for a vaultDepositor
6
+ * @param vaultDepositor
7
+ * @param vaultEquity
8
+ * @param vault
9
+ * @returns
10
+ */
11
+ export declare function calculateApplyProfitShare(vaultDepositor: VaultDepositor, vaultEquity: BN, vault: Vault): {
12
+ profitShareAmount: BN;
13
+ profitShareShares: BN;
14
+ };
15
+ export declare function calculateProfitShare(vaultDepositor: VaultDepositor, totalAmount: BN, vault: Vault): BN;
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.calculateProfitShare = exports.calculateApplyProfitShare = void 0;
4
+ const sdk_1 = require("@drift-labs/sdk");
5
+ /**
6
+ * Calculates the unrealized profitShare for a vaultDepositor
7
+ * @param vaultDepositor
8
+ * @param vaultEquity
9
+ * @param vault
10
+ * @returns
11
+ */
12
+ function calculateApplyProfitShare(vaultDepositor, vaultEquity, vault) {
13
+ const amount = (0, sdk_1.unstakeSharesToAmount)(vaultDepositor.vaultShares, vault.totalShares, vaultEquity);
14
+ const profitShareAmount = calculateProfitShare(vaultDepositor, amount, vault);
15
+ const profitShareShares = (0, sdk_1.stakeAmountToShares)(profitShareAmount, vault.totalShares, vaultEquity);
16
+ return {
17
+ profitShareAmount,
18
+ profitShareShares,
19
+ };
20
+ }
21
+ exports.calculateApplyProfitShare = calculateApplyProfitShare;
22
+ function calculateProfitShare(vaultDepositor, totalAmount, vault) {
23
+ const profit = totalAmount.sub(vaultDepositor.netDeposits.add(vaultDepositor.cumulativeProfitShareAmount));
24
+ if (profit.gt(sdk_1.ZERO)) {
25
+ const profitShareAmount = profit
26
+ .mul(new sdk_1.BN(vault.profitShare))
27
+ .div(sdk_1.PERCENTAGE_PRECISION);
28
+ return profitShareAmount;
29
+ }
30
+ return sdk_1.ZERO;
31
+ }
32
+ exports.calculateProfitShare = calculateProfitShare;
@@ -1,7 +1,7 @@
1
1
  /// <reference types="bn.js" />
2
2
  /// <reference types="@coral-xyz/anchor/node_modules/@solana/web3.js" />
3
3
  /// <reference types="@pythnetwork/client/node_modules/@solana/web3.js" />
4
- import { BN, DriftClient } from '@drift-labs/sdk';
4
+ import { BN, DriftClient, User } from '@drift-labs/sdk';
5
5
  import { Program, ProgramAccount } from '@coral-xyz/anchor';
6
6
  import { DriftVaults } from './types/drift_vaults';
7
7
  import { CompetitionsClient } from '@drift-labs/competitions-sdk';
@@ -11,6 +11,10 @@ export declare class VaultClient {
11
11
  driftClient: DriftClient;
12
12
  program: Program<DriftVaults>;
13
13
  cliMode: boolean;
14
+ /**
15
+ * Cache map of drift user accounts of vaults.
16
+ */
17
+ readonly vaultUsers: Map<string, User>;
14
18
  constructor({ driftClient, program, cliMode, }: {
15
19
  driftClient: DriftClient;
16
20
  program: Program<DriftVaults>;
@@ -18,7 +22,9 @@ export declare class VaultClient {
18
22
  });
19
23
  getVault(vault: PublicKey): Promise<Vault>;
20
24
  getVaultDepositor(vaultDepositor: PublicKey): Promise<any>;
25
+ getAllVaultDepositorsWithNoWithdrawRequest(vault: PublicKey): Promise<ProgramAccount<VaultDepositor>[]>;
21
26
  getAllVaultDepositors(vault: PublicKey): Promise<ProgramAccount<VaultDepositor>[]>;
27
+ getSubscribedVaultUser(vaultDriftUserAccountPubKey: PublicKey): Promise<User>;
22
28
  /**
23
29
  *
24
30
  * @param vault pubkey
@@ -10,6 +10,10 @@ const spl_token_1 = require("@solana/spl-token");
10
10
  const bytes_1 = require("@coral-xyz/anchor/dist/cjs/utils/bytes");
11
11
  class VaultClient {
12
12
  constructor({ driftClient, program, cliMode, }) {
13
+ /**
14
+ * Cache map of drift user accounts of vaults.
15
+ */
16
+ this.vaultUsers = new Map();
13
17
  this.driftClient = driftClient;
14
18
  this.program = program;
15
19
  this.cliMode = !!cliMode;
@@ -21,7 +25,7 @@ class VaultClient {
21
25
  async getVaultDepositor(vaultDepositor) {
22
26
  return await this.program.account.vaultDepositor.fetch(vaultDepositor);
23
27
  }
24
- async getAllVaultDepositors(vault) {
28
+ async getAllVaultDepositorsWithNoWithdrawRequest(vault) {
25
29
  const filters = [
26
30
  {
27
31
  // discriminator = VaultDepositor
@@ -38,16 +42,51 @@ class VaultClient {
38
42
  },
39
43
  },
40
44
  {
41
- // last_withdraw_request_ts = 0
45
+ // last_withdraw_request.shares (u128) = 0
42
46
  memcmp: {
43
- offset: 144,
44
- bytes: bytes_1.bs58.encode(Uint8Array.from([0])),
47
+ offset: 112,
48
+ bytes: bytes_1.bs58.encode(new Uint8Array(16).fill(0)),
45
49
  },
46
50
  },
47
51
  ];
48
52
  // @ts-ignore
49
53
  return (await this.program.account.vaultDepositor.all(filters));
50
54
  }
55
+ async getAllVaultDepositors(vault) {
56
+ const filters = [
57
+ {
58
+ // discriminator = VaultDepositor
59
+ memcmp: {
60
+ offset: 0,
61
+ bytes: bytes_1.bs58.encode(anchor_1.BorshAccountsCoder.accountDiscriminator('VaultDepositor')),
62
+ },
63
+ },
64
+ {
65
+ // vault = vault
66
+ memcmp: {
67
+ offset: 8,
68
+ bytes: vault.toBase58(),
69
+ },
70
+ },
71
+ ];
72
+ // @ts-ignore
73
+ return (await this.program.account.vaultDepositor.all(filters));
74
+ }
75
+ async getSubscribedVaultUser(vaultDriftUserAccountPubKey) {
76
+ let vaultUser = this.vaultUsers.get(vaultDriftUserAccountPubKey.toBase58());
77
+ if (!vaultUser) {
78
+ vaultUser = new sdk_1.User({
79
+ driftClient: this.driftClient,
80
+ userAccountPublicKey: vaultDriftUserAccountPubKey,
81
+ });
82
+ await vaultUser.subscribe();
83
+ this.vaultUsers.set(vaultDriftUserAccountPubKey.toBase58(), vaultUser);
84
+ }
85
+ if (!(vaultUser === null || vaultUser === void 0 ? void 0 : vaultUser.isSubscribed)) {
86
+ await vaultUser.subscribe();
87
+ }
88
+ return vaultUser;
89
+ }
51
90
  /**
52
91
  *
53
92
  * @param vault pubkey
@@ -65,11 +104,7 @@ class VaultClient {
65
104
  else {
66
105
  throw new Error('Must supply address or vault');
67
106
  }
68
- const user = new sdk_1.User({
69
- driftClient: this.driftClient,
70
- userAccountPublicKey: vaultAccount.user,
71
- });
72
- await user.subscribe();
107
+ const user = await this.getSubscribedVaultUser(vaultAccount.user);
73
108
  const netSpotValue = user.getNetSpotMarketValue();
74
109
  const unrealizedPnl = user.getUnrealizedPNL(true, undefined, undefined);
75
110
  return netSpotValue.add(unrealizedPnl);
@@ -146,11 +181,7 @@ class VaultClient {
146
181
  if (!driftSpotMarket) {
147
182
  throw new Error(`Spot market ${vaultAccount.spotMarketIndex} not found on driftClient`);
148
183
  }
149
- const user = new sdk_1.User({
150
- driftClient: this.driftClient,
151
- userAccountPublicKey: vaultAccount.user,
152
- });
153
- await user.subscribe();
184
+ const user = await this.getSubscribedVaultUser(vaultAccount.user);
154
185
  const remainingAccounts = this.driftClient.getRemainingAccounts({
155
186
  userAccounts: [user.getUserAccount()],
156
187
  writableSpotMarketIndexes: [vaultAccount.spotMarketIndex],
@@ -177,11 +208,7 @@ class VaultClient {
177
208
  if (!this.driftClient.wallet.publicKey.equals(vaultAccount.manager)) {
178
209
  throw new Error(`Only the manager of the vault can request a withdraw.`);
179
210
  }
180
- const user = new sdk_1.User({
181
- driftClient: this.driftClient,
182
- userAccountPublicKey: vaultAccount.user,
183
- });
184
- await user.subscribe();
211
+ const user = await this.getSubscribedVaultUser(vaultAccount.user);
185
212
  const remainingAccounts = this.driftClient.getRemainingAccounts({
186
213
  userAccounts: [user.getUserAccount()],
187
214
  writableSpotMarketIndexes: [vaultAccount.spotMarketIndex],
@@ -223,11 +250,7 @@ class VaultClient {
223
250
  driftUser: vaultAccount.user,
224
251
  driftState: driftStateKey,
225
252
  };
226
- const user = new sdk_1.User({
227
- driftClient: this.driftClient,
228
- userAccountPublicKey: vaultAccount.user,
229
- });
230
- await user.subscribe();
253
+ const user = await this.getSubscribedVaultUser(vaultAccount.user);
231
254
  const remainingAccounts = this.driftClient.getRemainingAccounts({
232
255
  userAccounts: [user.getUserAccount()],
233
256
  });
@@ -254,11 +277,7 @@ class VaultClient {
254
277
  if (!this.driftClient.wallet.publicKey.equals(vaultAccount.manager)) {
255
278
  throw new Error(`Only the manager of the vault can request a withdraw.`);
256
279
  }
257
- const user = new sdk_1.User({
258
- driftClient: this.driftClient,
259
- userAccountPublicKey: vaultAccount.user,
260
- });
261
- await user.subscribe();
280
+ const user = await this.getSubscribedVaultUser(vaultAccount.user);
262
281
  const remainingAccounts = this.driftClient.getRemainingAccounts({
263
282
  userAccounts: [user.getUserAccount()],
264
283
  writableSpotMarketIndexes: [vaultAccount.spotMarketIndex],
@@ -298,11 +317,7 @@ class VaultClient {
298
317
  }
299
318
  async getApplyProfitShareIx(vault, vaultDepositor) {
300
319
  const vaultAccount = await this.program.account.vault.fetch(vault);
301
- const user = new sdk_1.User({
302
- driftClient: this.driftClient,
303
- userAccountPublicKey: vaultAccount.user,
304
- });
305
- await user.subscribe();
320
+ const user = await this.getSubscribedVaultUser(vaultAccount.user);
306
321
  const spotMarket = this.driftClient.getSpotMarketAccount(vaultAccount.spotMarketIndex);
307
322
  if (!spotMarket) {
308
323
  throw new Error(`Spot market ${vaultAccount.spotMarketIndex} not found on driftClient`);
@@ -386,11 +401,7 @@ class VaultClient {
386
401
  vaultPubKey = vaultDepositorAccount.vault;
387
402
  }
388
403
  const vaultAccount = await this.program.account.vault.fetch(vaultPubKey);
389
- const user = new sdk_1.User({
390
- driftClient: this.driftClient,
391
- userAccountPublicKey: vaultAccount.user,
392
- });
393
- await user.subscribe();
404
+ const user = await this.getSubscribedVaultUser(vaultAccount.user);
394
405
  const remainingAccounts = this.driftClient.getRemainingAccounts({
395
406
  userAccounts: [user.getUserAccount()],
396
407
  writableSpotMarketIndexes: [vaultAccount.spotMarketIndex],
@@ -443,11 +454,7 @@ class VaultClient {
443
454
  async requestWithdraw(vaultDepositor, amount, withdrawUnit) {
444
455
  const vaultDepositorAccount = await this.program.account.vaultDepositor.fetch(vaultDepositor);
445
456
  const vaultAccount = await this.program.account.vault.fetch(vaultDepositorAccount.vault);
446
- const user = new sdk_1.User({
447
- driftClient: this.driftClient,
448
- userAccountPublicKey: vaultAccount.user,
449
- });
450
- await user.subscribe();
457
+ const user = await this.getSubscribedVaultUser(vaultAccount.user);
451
458
  const remainingAccounts = this.driftClient.getRemainingAccounts({
452
459
  userAccounts: [user.getUserAccount()],
453
460
  });
@@ -481,11 +488,7 @@ class VaultClient {
481
488
  async withdraw(vaultDepositor) {
482
489
  const vaultDepositorAccount = await this.program.account.vaultDepositor.fetch(vaultDepositor);
483
490
  const vaultAccount = await this.program.account.vault.fetch(vaultDepositorAccount.vault);
484
- const user = new sdk_1.User({
485
- driftClient: this.driftClient,
486
- userAccountPublicKey: vaultAccount.user,
487
- });
488
- await user.subscribe();
491
+ const user = await this.getSubscribedVaultUser(vaultAccount.user);
489
492
  const remainingAccounts = this.driftClient.getRemainingAccounts({
490
493
  userAccounts: [user.getUserAccount()],
491
494
  writableSpotMarketIndexes: [vaultAccount.spotMarketIndex],
@@ -530,11 +533,7 @@ class VaultClient {
530
533
  async forceWithdraw(vaultDepositor) {
531
534
  const vaultDepositorAccount = await this.program.account.vaultDepositor.fetch(vaultDepositor);
532
535
  const vaultAccount = await this.program.account.vault.fetch(vaultDepositorAccount.vault);
533
- const user = new sdk_1.User({
534
- driftClient: this.driftClient,
535
- userAccountPublicKey: vaultAccount.user,
536
- });
537
- await user.subscribe();
536
+ const user = await this.getSubscribedVaultUser(vaultAccount.user);
538
537
  const remainingAccounts = this.driftClient.getRemainingAccounts({
539
538
  userAccounts: [user.getUserAccount()],
540
539
  writableSpotMarketIndexes: [vaultAccount.spotMarketIndex],
@@ -593,11 +592,7 @@ class VaultClient {
593
592
  driftUser: vaultAccount.user,
594
593
  driftState: driftStateKey,
595
594
  };
596
- const user = new sdk_1.User({
597
- driftClient: this.driftClient,
598
- userAccountPublicKey: vaultAccount.user,
599
- });
600
- await user.subscribe();
595
+ const user = await this.getSubscribedVaultUser(vaultAccount.user);
601
596
  const remainingAccounts = this.driftClient.getRemainingAccounts({
602
597
  userAccounts: [user.getUserAccount()],
603
598
  });
@@ -629,11 +624,7 @@ class VaultClient {
629
624
  const vaultDepositorAccount = await this.program.account.vaultDepositor.fetch(vaultDepositor);
630
625
  const vaultPubKey = vaultDepositorAccount.vault;
631
626
  const vaultAccount = await this.program.account.vault.fetch(vaultPubKey);
632
- const user = new sdk_1.User({
633
- driftClient: this.driftClient,
634
- userAccountPublicKey: vaultAccount.user,
635
- });
636
- await user.subscribe();
627
+ const user = await this.getSubscribedVaultUser(vaultAccount.user);
637
628
  const remainingAccounts = this.driftClient.getRemainingAccounts({
638
629
  userAccounts: [user.getUserAccount()],
639
630
  writableSpotMarketIndexes: [vaultAccount.spotMarketIndex],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drift-labs/vaults-sdk",
3
- "version": "0.1.107",
3
+ "version": "0.1.109",
4
4
  "main": "lib/index.js",
5
5
  "types": "lib/index.d.ts",
6
6
  "directories": {
@@ -8,8 +8,8 @@
8
8
  },
9
9
  "dependencies": {
10
10
  "@coral-xyz/anchor": "^0.26.0",
11
- "@drift-labs/competitions-sdk": "0.2.73",
12
- "@drift-labs/sdk": "2.54.0-beta.3",
11
+ "@drift-labs/competitions-sdk": "0.2.131",
12
+ "@drift-labs/sdk": "2.54.0-beta.9",
13
13
  "@solana/web3.js": "1.73.2",
14
14
  "commander": "^11.0.0",
15
15
  "dotenv": "^16.3.1",
package/src/index.ts CHANGED
@@ -8,3 +8,4 @@ export * from './types/types';
8
8
  export * from './constants';
9
9
  export * from './parsers';
10
10
  export * from './types/drift_vaults';
11
+ export * from './math';
@@ -0,0 +1 @@
1
+ export * from './vaultDepositor';
@@ -0,0 +1,58 @@
1
+ import {
2
+ BN,
3
+ PERCENTAGE_PRECISION,
4
+ ZERO,
5
+ unstakeSharesToAmount as depositSharesToVaultAmount,
6
+ stakeAmountToShares as vaultAmountToDepositorShares,
7
+ } from '@drift-labs/sdk';
8
+ import { Vault, VaultDepositor } from '../types/types';
9
+
10
+ /**
11
+ * Calculates the unrealized profitShare for a vaultDepositor
12
+ * @param vaultDepositor
13
+ * @param vaultEquity
14
+ * @param vault
15
+ * @returns
16
+ */
17
+ export function calculateApplyProfitShare(
18
+ vaultDepositor: VaultDepositor,
19
+ vaultEquity: BN,
20
+ vault: Vault
21
+ ): {
22
+ profitShareAmount: BN;
23
+ profitShareShares: BN;
24
+ } {
25
+ const amount = depositSharesToVaultAmount(
26
+ vaultDepositor.vaultShares,
27
+ vault.totalShares,
28
+ vaultEquity
29
+ );
30
+ const profitShareAmount = calculateProfitShare(vaultDepositor, amount, vault);
31
+ const profitShareShares = vaultAmountToDepositorShares(
32
+ profitShareAmount,
33
+ vault.totalShares,
34
+ vaultEquity
35
+ );
36
+ return {
37
+ profitShareAmount,
38
+ profitShareShares,
39
+ };
40
+ }
41
+
42
+ export function calculateProfitShare(
43
+ vaultDepositor: VaultDepositor,
44
+ totalAmount: BN,
45
+ vault: Vault
46
+ ) {
47
+ const profit = totalAmount.sub(
48
+ vaultDepositor.netDeposits.add(vaultDepositor.cumulativeProfitShareAmount)
49
+ );
50
+ if (profit.gt(ZERO)) {
51
+ const profitShareAmount = profit
52
+ .mul(new BN(vault.profitShare))
53
+ .div(PERCENTAGE_PRECISION);
54
+ return profitShareAmount;
55
+ }
56
+
57
+ return ZERO;
58
+ }
@@ -42,6 +42,11 @@ export class VaultClient {
42
42
  program: Program<DriftVaults>;
43
43
  cliMode: boolean;
44
44
 
45
+ /**
46
+ * Cache map of drift user accounts of vaults.
47
+ */
48
+ readonly vaultUsers: Map<string, User> = new Map<string, User>();
49
+
45
50
  constructor({
46
51
  driftClient,
47
52
  program,
@@ -65,7 +70,7 @@ export class VaultClient {
65
70
  return await this.program.account.vaultDepositor.fetch(vaultDepositor);
66
71
  }
67
72
 
68
- public async getAllVaultDepositors(
73
+ public async getAllVaultDepositorsWithNoWithdrawRequest(
69
74
  vault: PublicKey
70
75
  ): Promise<ProgramAccount<VaultDepositor>[]> {
71
76
  const filters = [
@@ -86,10 +91,10 @@ export class VaultClient {
86
91
  },
87
92
  },
88
93
  {
89
- // last_withdraw_request_ts = 0
94
+ // last_withdraw_request.shares (u128) = 0
90
95
  memcmp: {
91
- offset: 144,
92
- bytes: bs58.encode(Uint8Array.from([0])),
96
+ offset: 112,
97
+ bytes: bs58.encode(new Uint8Array(16).fill(0)),
93
98
  },
94
99
  },
95
100
  ];
@@ -99,6 +104,52 @@ export class VaultClient {
99
104
  )) as ProgramAccount<VaultDepositor>[];
100
105
  }
101
106
 
107
+ public async getAllVaultDepositors(
108
+ vault: PublicKey
109
+ ): Promise<ProgramAccount<VaultDepositor>[]> {
110
+ const filters = [
111
+ {
112
+ // discriminator = VaultDepositor
113
+ memcmp: {
114
+ offset: 0,
115
+ bytes: bs58.encode(
116
+ BorshAccountsCoder.accountDiscriminator('VaultDepositor')
117
+ ),
118
+ },
119
+ },
120
+ {
121
+ // vault = vault
122
+ memcmp: {
123
+ offset: 8,
124
+ bytes: vault.toBase58(),
125
+ },
126
+ },
127
+ ];
128
+ // @ts-ignore
129
+ return (await this.program.account.vaultDepositor.all(
130
+ filters
131
+ )) as ProgramAccount<VaultDepositor>[];
132
+ }
133
+
134
+ public async getSubscribedVaultUser(vaultDriftUserAccountPubKey: PublicKey) {
135
+ let vaultUser = this.vaultUsers.get(vaultDriftUserAccountPubKey.toBase58());
136
+
137
+ if (!vaultUser) {
138
+ vaultUser = new User({
139
+ driftClient: this.driftClient,
140
+ userAccountPublicKey: vaultDriftUserAccountPubKey,
141
+ });
142
+ await vaultUser.subscribe();
143
+ this.vaultUsers.set(vaultDriftUserAccountPubKey.toBase58(), vaultUser);
144
+ }
145
+
146
+ if (!vaultUser?.isSubscribed) {
147
+ await vaultUser.subscribe();
148
+ }
149
+
150
+ return vaultUser;
151
+ }
152
+
102
153
  /**
103
154
  *
104
155
  * @param vault pubkey
@@ -118,11 +169,7 @@ export class VaultClient {
118
169
  throw new Error('Must supply address or vault');
119
170
  }
120
171
 
121
- const user = new User({
122
- driftClient: this.driftClient,
123
- userAccountPublicKey: vaultAccount.user,
124
- });
125
- await user.subscribe();
172
+ const user = await this.getSubscribedVaultUser(vaultAccount.user);
126
173
 
127
174
  const netSpotValue = user.getNetSpotMarketValue();
128
175
  const unrealizedPnl = user.getUnrealizedPNL(true, undefined, undefined);
@@ -246,11 +293,7 @@ export class VaultClient {
246
293
  );
247
294
  }
248
295
 
249
- const user = new User({
250
- driftClient: this.driftClient,
251
- userAccountPublicKey: vaultAccount.user,
252
- });
253
- await user.subscribe();
296
+ const user = await this.getSubscribedVaultUser(vaultAccount.user);
254
297
 
255
298
  const remainingAccounts = this.driftClient.getRemainingAccounts({
256
299
  userAccounts: [user.getUserAccount()],
@@ -297,11 +340,7 @@ export class VaultClient {
297
340
  throw new Error(`Only the manager of the vault can request a withdraw.`);
298
341
  }
299
342
 
300
- const user = new User({
301
- driftClient: this.driftClient,
302
- userAccountPublicKey: vaultAccount.user,
303
- });
304
- await user.subscribe();
343
+ const user = await this.getSubscribedVaultUser(vaultAccount.user);
305
344
  const remainingAccounts = this.driftClient.getRemainingAccounts({
306
345
  userAccounts: [user.getUserAccount()],
307
346
  writableSpotMarketIndexes: [vaultAccount.spotMarketIndex],
@@ -364,11 +403,7 @@ export class VaultClient {
364
403
  driftState: driftStateKey,
365
404
  };
366
405
 
367
- const user = new User({
368
- driftClient: this.driftClient,
369
- userAccountPublicKey: vaultAccount.user,
370
- });
371
- await user.subscribe();
406
+ const user = await this.getSubscribedVaultUser(vaultAccount.user);
372
407
  const remainingAccounts = this.driftClient.getRemainingAccounts({
373
408
  userAccounts: [user.getUserAccount()],
374
409
  });
@@ -402,11 +437,7 @@ export class VaultClient {
402
437
  throw new Error(`Only the manager of the vault can request a withdraw.`);
403
438
  }
404
439
 
405
- const user = new User({
406
- driftClient: this.driftClient,
407
- userAccountPublicKey: vaultAccount.user,
408
- });
409
- await user.subscribe();
440
+ const user = await this.getSubscribedVaultUser(vaultAccount.user);
410
441
 
411
442
  const remainingAccounts = this.driftClient.getRemainingAccounts({
412
443
  userAccounts: [user.getUserAccount()],
@@ -479,11 +510,7 @@ export class VaultClient {
479
510
  ): Promise<TransactionInstruction> {
480
511
  const vaultAccount = await this.program.account.vault.fetch(vault);
481
512
 
482
- const user = new User({
483
- driftClient: this.driftClient,
484
- userAccountPublicKey: vaultAccount.user,
485
- });
486
- await user.subscribe();
513
+ const user = await this.getSubscribedVaultUser(vaultAccount.user);
487
514
 
488
515
  const spotMarket = this.driftClient.getSpotMarketAccount(
489
516
  vaultAccount.spotMarketIndex
@@ -608,11 +635,7 @@ export class VaultClient {
608
635
 
609
636
  const vaultAccount = await this.program.account.vault.fetch(vaultPubKey);
610
637
 
611
- const user = new User({
612
- driftClient: this.driftClient,
613
- userAccountPublicKey: vaultAccount.user,
614
- });
615
- await user.subscribe();
638
+ const user = await this.getSubscribedVaultUser(vaultAccount.user);
616
639
  const remainingAccounts = this.driftClient.getRemainingAccounts({
617
640
  userAccounts: [user.getUserAccount()],
618
641
  writableSpotMarketIndexes: [vaultAccount.spotMarketIndex],
@@ -695,11 +718,7 @@ export class VaultClient {
695
718
  vaultDepositorAccount.vault
696
719
  );
697
720
 
698
- const user = new User({
699
- driftClient: this.driftClient,
700
- userAccountPublicKey: vaultAccount.user,
701
- });
702
- await user.subscribe();
721
+ const user = await this.getSubscribedVaultUser(vaultAccount.user);
703
722
  const remainingAccounts = this.driftClient.getRemainingAccounts({
704
723
  userAccounts: [user.getUserAccount()],
705
724
  });
@@ -751,11 +770,7 @@ export class VaultClient {
751
770
  vaultDepositorAccount.vault
752
771
  );
753
772
 
754
- const user = new User({
755
- driftClient: this.driftClient,
756
- userAccountPublicKey: vaultAccount.user,
757
- });
758
- await user.subscribe();
773
+ const user = await this.getSubscribedVaultUser(vaultAccount.user);
759
774
  const remainingAccounts = this.driftClient.getRemainingAccounts({
760
775
  userAccounts: [user.getUserAccount()],
761
776
  writableSpotMarketIndexes: [vaultAccount.spotMarketIndex],
@@ -823,11 +838,7 @@ export class VaultClient {
823
838
  vaultDepositorAccount.vault
824
839
  );
825
840
 
826
- const user = new User({
827
- driftClient: this.driftClient,
828
- userAccountPublicKey: vaultAccount.user,
829
- });
830
- await user.subscribe();
841
+ const user = await this.getSubscribedVaultUser(vaultAccount.user);
831
842
  const remainingAccounts = this.driftClient.getRemainingAccounts({
832
843
  userAccounts: [user.getUserAccount()],
833
844
  writableSpotMarketIndexes: [vaultAccount.spotMarketIndex],
@@ -914,11 +925,7 @@ export class VaultClient {
914
925
  driftState: driftStateKey,
915
926
  };
916
927
 
917
- const user = new User({
918
- driftClient: this.driftClient,
919
- userAccountPublicKey: vaultAccount.user,
920
- });
921
- await user.subscribe();
928
+ const user = await this.getSubscribedVaultUser(vaultAccount.user);
922
929
  const remainingAccounts = this.driftClient.getRemainingAccounts({
923
930
  userAccounts: [user.getUserAccount()],
924
931
  });
@@ -958,11 +965,7 @@ export class VaultClient {
958
965
 
959
966
  const vaultAccount = await this.program.account.vault.fetch(vaultPubKey);
960
967
 
961
- const user = new User({
962
- driftClient: this.driftClient,
963
- userAccountPublicKey: vaultAccount.user,
964
- });
965
- await user.subscribe();
968
+ const user = await this.getSubscribedVaultUser(vaultAccount.user);
966
969
  const remainingAccounts = this.driftClient.getRemainingAccounts({
967
970
  userAccounts: [user.getUserAccount()],
968
971
  writableSpotMarketIndexes: [vaultAccount.spotMarketIndex],