@drift-labs/vaults-sdk 0.1.106 → 0.1.108

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,72 @@
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
+ const allVaultDepositors = await driftVault.getAllVaultDepositors(vaultAddress);
41
+
42
+ let totalUserShares = new BN(0);
43
+ let totalUserProfitSharePaid = new BN(0);
44
+
45
+ for (const vd of allVaultDepositors) {
46
+ totalUserShares = totalUserShares.add(vd.account.vaultShares);
47
+ if (!vd.account.lastWithdrawRequest.shares.eq(new BN(0))) {
48
+ const pct = vd.account.lastWithdrawRequest.shares.toNumber() / vd.account.vaultShares.toNumber();
49
+ 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)}%`);
50
+ }
51
+
52
+ if (!vd.account.cumulativeProfitShareAmount.eq(new BN(0))) {
53
+ // const profitSharePaid = vd.account.profitShareFeePaid.toNumber() / vd.account.cumulativeProfitShareAmount.toNumber();
54
+ // console.log(`Profit share paid: ${vd.publicKey.toBase58()} (auth: ${vd.account.authority.toBase58()}): ${Math.ceil(profitSharePaid * 100.0)}%`);
55
+ }
56
+ totalUserProfitSharePaid = totalUserProfitSharePaid.add(vd.account.profitShareFeePaid);
57
+
58
+ const pendingProfitShares = calculateApplyProfitShare(vd.account, vaultEquity, vault);
59
+ console.log(`Pending profit shares: ${vd.publicKey.toBase58()} (auth: ${vd.account.authority.toBase58()}): $${convertToNumber(pendingProfitShares.profitShareAmount, spotPrecision)}`);
60
+ console.log(` . ${pendingProfitShares.profitShareShares}, ${pendingProfitShares.profitShareAmount}`);
61
+ }
62
+ console.log(`==== Vault Depositor Shares == vault.user_shares ====`);
63
+ console.log(`total vd shares: ${totalUserShares.toString()}`);
64
+ console.log(`total vault usershares: ${vault.userShares.toString()}`);
65
+ console.log(`diff: ${vault.userShares.sub(totalUserShares)}`);
66
+
67
+ console.log(``);
68
+ console.log(`==== Vault Depositor ProfitSharePaid == vault.manager_total_profit_share ====`);
69
+ console.log(`total vault d profitshares: ${totalUserProfitSharePaid.toString()}`);
70
+ console.log(`vault total profit shares: ${vault.managerTotalProfitShare.toString()}`);
71
+ console.log(`diff: ${vault.managerTotalProfitShare.sub(totalUserProfitSharePaid)}`);
72
+ };
@@ -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;
@@ -18,6 +18,7 @@ export declare class VaultClient {
18
18
  });
19
19
  getVault(vault: PublicKey): Promise<Vault>;
20
20
  getVaultDepositor(vaultDepositor: PublicKey): Promise<any>;
21
+ getAllVaultDepositorsWithNoWithdrawRequest(vault: PublicKey): Promise<ProgramAccount<VaultDepositor>[]>;
21
22
  getAllVaultDepositors(vault: PublicKey): Promise<ProgramAccount<VaultDepositor>[]>;
22
23
  /**
23
24
  *
@@ -21,7 +21,7 @@ class VaultClient {
21
21
  async getVaultDepositor(vaultDepositor) {
22
22
  return await this.program.account.vaultDepositor.fetch(vaultDepositor);
23
23
  }
24
- async getAllVaultDepositors(vault) {
24
+ async getAllVaultDepositorsWithNoWithdrawRequest(vault) {
25
25
  const filters = [
26
26
  {
27
27
  // discriminator = VaultDepositor
@@ -38,10 +38,30 @@ class VaultClient {
38
38
  },
39
39
  },
40
40
  {
41
- // last_withdraw_request_ts = 0
41
+ // last_withdraw_request.shares (u128) = 0
42
42
  memcmp: {
43
- offset: 144,
44
- bytes: bytes_1.bs58.encode(Uint8Array.from([0])),
43
+ offset: 112,
44
+ bytes: bytes_1.bs58.encode(new Uint8Array(16).fill(0)),
45
+ },
46
+ },
47
+ ];
48
+ // @ts-ignore
49
+ return (await this.program.account.vaultDepositor.all(filters));
50
+ }
51
+ async getAllVaultDepositors(vault) {
52
+ const filters = [
53
+ {
54
+ // discriminator = VaultDepositor
55
+ memcmp: {
56
+ offset: 0,
57
+ bytes: bytes_1.bs58.encode(anchor_1.BorshAccountsCoder.accountDiscriminator('VaultDepositor')),
58
+ },
59
+ },
60
+ {
61
+ // vault = vault
62
+ memcmp: {
63
+ offset: 8,
64
+ bytes: vault.toBase58(),
45
65
  },
46
66
  },
47
67
  ];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drift-labs/vaults-sdk",
3
- "version": "0.1.106",
3
+ "version": "0.1.108",
4
4
  "main": "lib/index.js",
5
5
  "types": "lib/index.d.ts",
6
6
  "directories": {
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
+ }
@@ -65,7 +65,7 @@ export class VaultClient {
65
65
  return await this.program.account.vaultDepositor.fetch(vaultDepositor);
66
66
  }
67
67
 
68
- public async getAllVaultDepositors(
68
+ public async getAllVaultDepositorsWithNoWithdrawRequest(
69
69
  vault: PublicKey
70
70
  ): Promise<ProgramAccount<VaultDepositor>[]> {
71
71
  const filters = [
@@ -86,10 +86,37 @@ export class VaultClient {
86
86
  },
87
87
  },
88
88
  {
89
- // last_withdraw_request_ts = 0
89
+ // last_withdraw_request.shares (u128) = 0
90
+ memcmp: {
91
+ offset: 112,
92
+ bytes: bs58.encode(new Uint8Array(16).fill(0)),
93
+ },
94
+ },
95
+ ];
96
+ // @ts-ignore
97
+ return (await this.program.account.vaultDepositor.all(
98
+ filters
99
+ )) as ProgramAccount<VaultDepositor>[];
100
+ }
101
+
102
+ public async getAllVaultDepositors(
103
+ vault: PublicKey
104
+ ): Promise<ProgramAccount<VaultDepositor>[]> {
105
+ const filters = [
106
+ {
107
+ // discriminator = VaultDepositor
108
+ memcmp: {
109
+ offset: 0,
110
+ bytes: bs58.encode(
111
+ BorshAccountsCoder.accountDiscriminator('VaultDepositor')
112
+ ),
113
+ },
114
+ },
115
+ {
116
+ // vault = vault
90
117
  memcmp: {
91
- offset: 144,
92
- bytes: bs58.encode(Uint8Array.from([0])),
118
+ offset: 8,
119
+ bytes: vault.toBase58(),
93
120
  },
94
121
  },
95
122
  ];