@velocity-exchange/vaults-sdk 0.0.1

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.
Files changed (65) hide show
  1. package/.env.example +3 -0
  2. package/README.md +152 -0
  3. package/cli/cli.ts +751 -0
  4. package/cli/commands/adminDeleteFeeUpdate.ts +73 -0
  5. package/cli/commands/adminInitFeeUpdate.ts +73 -0
  6. package/cli/commands/adminUpdateVaultClass.ts +49 -0
  7. package/cli/commands/applyProfitShare.ts +139 -0
  8. package/cli/commands/decodeLogs.ts +98 -0
  9. package/cli/commands/deposit.ts +98 -0
  10. package/cli/commands/deriveVaultAddress.ts +14 -0
  11. package/cli/commands/forceWithdraw.ts +56 -0
  12. package/cli/commands/forceWithdrawAll.ts +142 -0
  13. package/cli/commands/index.ts +28 -0
  14. package/cli/commands/initVault.ts +227 -0
  15. package/cli/commands/initVaultDepositor.ts +42 -0
  16. package/cli/commands/listDepositorsForVault.ts +32 -0
  17. package/cli/commands/managerApplyProfitShare.ts +32 -0
  18. package/cli/commands/managerBorrow.ts +77 -0
  19. package/cli/commands/managerCancelWithdraw.ts +30 -0
  20. package/cli/commands/managerDeposit.ts +45 -0
  21. package/cli/commands/managerRepay.ts +94 -0
  22. package/cli/commands/managerRequestWithdraw.ts +86 -0
  23. package/cli/commands/managerUpdateBorrow.ts +56 -0
  24. package/cli/commands/managerUpdateFees.ts +156 -0
  25. package/cli/commands/managerUpdateMarginTradingEnabled.ts +32 -0
  26. package/cli/commands/managerUpdatePoolId.ts +36 -0
  27. package/cli/commands/managerUpdateVault.ts +210 -0
  28. package/cli/commands/managerUpdateVaultDelegate.ts +43 -0
  29. package/cli/commands/managerUpdateVaultManager.ts +77 -0
  30. package/cli/commands/managerWithdraw.ts +30 -0
  31. package/cli/commands/requestWithdraw.ts +58 -0
  32. package/cli/commands/vaultDeposit.ts +42 -0
  33. package/cli/commands/vaultInvariantChecks.ts +407 -0
  34. package/cli/commands/vaultWithdraw.ts +42 -0
  35. package/cli/commands/viewVault.ts +50 -0
  36. package/cli/commands/viewVaultDepositor.ts +36 -0
  37. package/cli/commands/withdraw.ts +40 -0
  38. package/cli/ledgerWallet.test.ts +49 -0
  39. package/cli/ledgerWallet.ts +111 -0
  40. package/cli/utils.ts +389 -0
  41. package/package.json +48 -0
  42. package/src/accountSubscribers/index.ts +2 -0
  43. package/src/accountSubscribers/pollingVaultDepositorSubscriber.ts +69 -0
  44. package/src/accountSubscribers/pollingVaultSubscriber.ts +63 -0
  45. package/src/accountSubscribers/pollingVaultsProgramAccountSubscriber.ts +114 -0
  46. package/src/accounts/index.ts +2 -0
  47. package/src/accounts/vaultAccount.ts +255 -0
  48. package/src/accounts/vaultDepositorAccount.ts +77 -0
  49. package/src/accounts/vaultsProgramAccount.ts +38 -0
  50. package/src/addresses.ts +114 -0
  51. package/src/constants/index.ts +15 -0
  52. package/src/idl/drift_vaults.json +5698 -0
  53. package/src/index.ts +11 -0
  54. package/src/math/index.ts +2 -0
  55. package/src/math/vault.ts +71 -0
  56. package/src/math/vaultDepositor.ts +90 -0
  57. package/src/name.ts +18 -0
  58. package/src/parsers/index.ts +1 -0
  59. package/src/parsers/logParser.ts +28 -0
  60. package/src/types/drift_vaults.ts +6211 -0
  61. package/src/types/types.ts +336 -0
  62. package/src/utils.ts +74 -0
  63. package/src/vaultClient.ts +3666 -0
  64. package/tsconfig.json +24 -0
  65. package/velocity-exchange-vaults-sdk-0.0.1.tgz +0 -0
@@ -0,0 +1,50 @@
1
+ import { PublicKey } from "@solana/web3.js";
2
+ import {
3
+ OptionValues,
4
+ Command
5
+ } from "commander";
6
+ import { getCommandContext, printVault } from "../utils";
7
+ import { FeeUpdate, getFeeUpdateAddressSync } from "../../src";
8
+
9
+ export const viewVault = async (program: Command, cmdOpts: OptionValues) => {
10
+
11
+ let address: PublicKey;
12
+ try {
13
+ address = new PublicKey(cmdOpts.vaultAddress as string);
14
+ } catch (err) {
15
+ console.error("Invalid vault address");
16
+ process.exit(1);
17
+ }
18
+
19
+ const {
20
+ driftVault,
21
+ driftClient,
22
+ } = await getCommandContext(program, false);
23
+
24
+
25
+ const vaultAndSlot = await driftVault.getVaultAndSlot(address);
26
+ const vault = vaultAndSlot.vault;
27
+ const spotMarket = driftClient.getSpotMarketAccount(vault.spotMarketIndex);
28
+ if (!spotMarket) {
29
+ throw new Error(`Spot market ${vault.spotMarketIndex} not found`);
30
+ }
31
+ const spotOracle = driftClient.getOracleDataForSpotMarket(vault.spotMarketIndex);
32
+ if (!spotOracle) {
33
+ throw new Error(`Spot oracle ${vault.spotMarketIndex} not found`);
34
+ }
35
+ const vaultEquity = await driftVault.calculateVaultEquity({
36
+ vault,
37
+ });
38
+
39
+ let feeUpdateAccount: FeeUpdate | null = null;
40
+
41
+ try {
42
+ const feeUpdatePubkey = getFeeUpdateAddressSync(driftVault.program.programId, address);
43
+ feeUpdateAccount = await driftVault.getFeeUpdate(feeUpdatePubkey);
44
+ } catch (err) {
45
+ feeUpdateAccount = null;
46
+ }
47
+
48
+ await printVault(vaultAndSlot.slot, driftClient, vault, vaultEquity, spotMarket, spotOracle, feeUpdateAccount);
49
+ };
50
+
@@ -0,0 +1,36 @@
1
+ import { PublicKey } from "@solana/web3.js";
2
+ import {
3
+ OptionValues,
4
+ Command
5
+ } from "commander";
6
+ import { getCommandContext, printVaultDepositor } from "../utils";
7
+ import { getVaultDepositorAddressSync } from "../../src";
8
+
9
+ export const viewVaultDepositor = async (program: Command, cmdOpts: OptionValues) => {
10
+
11
+ let vaultDepositorAddress: PublicKey;
12
+
13
+ const {
14
+ driftVault
15
+ } = await getCommandContext(program, false);
16
+
17
+ try {
18
+ if (cmdOpts.vaultDepositorAddress !== undefined) {
19
+ vaultDepositorAddress = new PublicKey(cmdOpts.vaultDepositorAddress as string);
20
+ } else if (cmdOpts.authority !== undefined && cmdOpts.vaultAddress !== undefined) {
21
+ vaultDepositorAddress = getVaultDepositorAddressSync(
22
+ driftVault.program.programId,
23
+ new PublicKey(cmdOpts.vaultAddress as string),
24
+ new PublicKey(cmdOpts.authority as string));
25
+ } else {
26
+ console.error("Must supply --vault-depositor-address or --authority and --vault-address");
27
+ process.exit(1);
28
+ }
29
+ } catch (err) {
30
+ console.error("Failed to load VaultDepositor address");
31
+ process.exit(1);
32
+ }
33
+
34
+ const vaultDepositor = await driftVault.getVaultDepositor(vaultDepositorAddress);
35
+ printVaultDepositor(vaultDepositor);
36
+ };
@@ -0,0 +1,40 @@
1
+ import { PublicKey } from "@solana/web3.js";
2
+ import {
3
+ OptionValues,
4
+ Command
5
+ } from "commander";
6
+ import { getCommandContext } from "../utils";
7
+ import { getVaultDepositorAddressSync, VAULT_PROGRAM_ID } from "../../src";
8
+
9
+ export const withdraw = async (program: Command, cmdOpts: OptionValues) => {
10
+
11
+ // verify correct args provided
12
+ if (!cmdOpts.vaultDepositorAddress) {
13
+ if (!cmdOpts.vaultAddress || !cmdOpts.authority) {
14
+ console.error("Must provide --vault-address and --authority if not providing --vault-depositor-address");
15
+ process.exit(1);
16
+ }
17
+ }
18
+
19
+ const {
20
+ driftVault,
21
+ driftClient
22
+ } = await getCommandContext(program, true);
23
+
24
+ let vaultDepositorAddress: PublicKey;
25
+ if (cmdOpts.vaultDepositorAddress) {
26
+ try {
27
+ vaultDepositorAddress = new PublicKey(cmdOpts.vaultDepositorAddress as string);
28
+ } catch (err) {
29
+ console.error("Invalid vault depositor address");
30
+ process.exit(1);
31
+ }
32
+ } else {
33
+ const vaultAddress = new PublicKey(cmdOpts.vaultAddress as string);
34
+ const authority = new PublicKey(cmdOpts.authority as string);
35
+ vaultDepositorAddress = getVaultDepositorAddressSync(VAULT_PROGRAM_ID, vaultAddress, authority);
36
+ }
37
+
38
+ const tx = await driftVault.withdraw(vaultDepositorAddress);
39
+ console.log(`Withdrew from vault: https://solana.fm/tx/${tx}${driftClient.env === "devnet" ? "?cluster=devnet-solana" : ""}`);
40
+ };
@@ -0,0 +1,49 @@
1
+ import assert from 'assert';
2
+
3
+ import { parseKeypairUrl } from './ledgerWallet';
4
+
5
+ assert.deepStrictEqual(parseKeypairUrl(''), {
6
+ walletId: undefined,
7
+ account: undefined,
8
+ change: undefined,
9
+ });
10
+
11
+ assert.deepStrictEqual(parseKeypairUrl('usb://ledger'), {
12
+ walletId: undefined,
13
+ account: undefined,
14
+ change: undefined,
15
+ });
16
+
17
+ assert.deepStrictEqual(parseKeypairUrl('usb://ledger?key=1'), {
18
+ walletId: undefined,
19
+ account: 1,
20
+ change: undefined,
21
+ });
22
+
23
+ assert.deepStrictEqual(parseKeypairUrl('usb://ledger?key=1/2'), {
24
+ walletId: undefined,
25
+ account: 1,
26
+ change: 2,
27
+ });
28
+
29
+ assert.deepStrictEqual(
30
+ parseKeypairUrl(
31
+ 'usb://ledger/BsNsvfXqQTtJnagwFWdBS7FBXgnsK8VZ5CmuznN85swK?key=0'
32
+ ),
33
+ {
34
+ walletId: 'BsNsvfXqQTtJnagwFWdBS7FBXgnsK8VZ5CmuznN85swK',
35
+ account: 0,
36
+ change: undefined,
37
+ }
38
+ );
39
+
40
+ assert.deepStrictEqual(
41
+ parseKeypairUrl(
42
+ 'usb://ledger/BsNsvfXqQTtJnagwFWdBS7FBXgnsK8VZ5CmuznN85swK?key=0/0'
43
+ ),
44
+ {
45
+ walletId: 'BsNsvfXqQTtJnagwFWdBS7FBXgnsK8VZ5CmuznN85swK',
46
+ account: 0,
47
+ change: 0,
48
+ }
49
+ );
@@ -0,0 +1,111 @@
1
+ import Solana from '@ledgerhq/hw-app-solana';
2
+ import type { default as Transport } from '@ledgerhq/hw-transport';
3
+ import TransportNodeHid from '@ledgerhq/hw-transport-node-hid';
4
+ import { getDevices } from '@ledgerhq/hw-transport-node-hid-noevents';
5
+ import {
6
+ LedgerWalletAdapter,
7
+ getDerivationPath,
8
+ } from '@solana/wallet-adapter-ledger';
9
+ import { PublicKey, Keypair } from '@solana/web3.js';
10
+ import type { Wallet } from '@velocity-exchange/sdk';
11
+
12
+ // Follows solana cli url format
13
+ // usb://<MANUFACTURER>[/<WALLET_ID>][?key=<ACCOUNT>[/<CHANGE>]]
14
+ // See: https://docs.solanalabs.com/cli/intro#hardware-wallet
15
+ export const parseKeypairUrl = (
16
+ url = ''
17
+ ): {
18
+ walletId?: string;
19
+ account?: number;
20
+ change?: number;
21
+ } => {
22
+ const walletId = url.match(/(?<=usb:\/\/ledger\/)(\w+)?/)?.[0];
23
+ const [account, change] = (url.split('?key=')[1]?.split('/') ?? []).map(
24
+ Number
25
+ );
26
+ return {
27
+ walletId,
28
+ account,
29
+ change,
30
+ };
31
+ };
32
+
33
+ async function getPublicKey(
34
+ transport: Transport,
35
+ account?: number,
36
+ change?: number
37
+ ): Promise<PublicKey> {
38
+ const path =
39
+ "44'/501'" + // Following BIP44 standard
40
+ (account !== undefined ? `/${account}` : '') +
41
+ (change !== undefined ? `/${change}` : '');
42
+
43
+ const { address } = await new Solana(transport).getAddress(path);
44
+ return new PublicKey(new Uint8Array(address));
45
+ }
46
+
47
+ /*
48
+ * Returns a Drift compatible wallet backed by ledger hardware device
49
+ * This only works in an nodejs environment, based on the transport used
50
+ *
51
+ * Key derivation path is set based on:
52
+ * See: https://docs.solanalabs.com/cli/intro#hardware-wallet
53
+ */
54
+ export async function getLedgerWallet(url = ''): Promise<Wallet> {
55
+ const { account, change, walletId } = parseKeypairUrl(url);
56
+
57
+ const derivationPath = getDerivationPath(account, change);
58
+
59
+ // Load the first device
60
+ let transport = await TransportNodeHid.open('');
61
+
62
+ // If walletId is specified, we need to loop and correct device.
63
+ if (walletId) {
64
+ const devices = getDevices();
65
+ let correctDeviceFound = false;
66
+
67
+ for (const device of devices) {
68
+ // Wallet id is the public key of the device (with no account or change)
69
+ const connectedWalletId = await getPublicKey(
70
+ transport,
71
+ undefined,
72
+ undefined
73
+ );
74
+
75
+ if (connectedWalletId.toString() === walletId) {
76
+ correctDeviceFound = true;
77
+ break;
78
+ }
79
+
80
+ transport.close();
81
+ transport = await TransportNodeHid.open(device.path);
82
+ }
83
+
84
+ if (!correctDeviceFound) {
85
+ throw new Error('Wallet not found');
86
+ }
87
+ }
88
+
89
+ const publicKey = await getPublicKey(transport, account, change);
90
+
91
+ // We can reuse the existing ledger wallet adapter
92
+ // But we need to inject/hack in our own transport (as we not a browser)
93
+ const wallet = new LedgerWalletAdapter({ derivationPath });
94
+
95
+ // Do some hacky things to get the wallet to work
96
+ // These are all done in the `connect` of the ledger wallet adapter
97
+ wallet['_transport'] = transport;
98
+ wallet['_publicKey'] = publicKey;
99
+ transport.on('disconnect', wallet['_disconnected']);
100
+ wallet.emit('connect', publicKey);
101
+
102
+ // Return a Drift compatible wallet
103
+ return {
104
+ payer: undefined as unknown as Keypair, // Doesn't appear to break things
105
+ publicKey: publicKey,
106
+ signTransaction: wallet.signTransaction.bind(wallet),
107
+ signVersionedTransaction: wallet.signTransaction.bind(wallet),
108
+ signAllTransactions: wallet.signAllTransactions.bind(wallet),
109
+ signAllVersionedTransactions: wallet.signAllTransactions.bind(wallet),
110
+ };
111
+ }
package/cli/utils.ts ADDED
@@ -0,0 +1,389 @@
1
+ import {
2
+ BASE_PRECISION,
3
+ BN,
4
+ DriftClient,
5
+ DriftEnv,
6
+ OraclePriceData,
7
+ PRICE_PRECISION,
8
+ QUOTE_PRECISION,
9
+ SpotMarketAccount,
10
+ TEN,
11
+ User,
12
+ Wallet,
13
+ WhileValidTxSender,
14
+ convertToNumber,
15
+ getSignedTokenAmount,
16
+ getTokenAmount,
17
+ loadKeypair,
18
+ } from '@velocity-exchange/sdk';
19
+ import {
20
+ FeeUpdate,
21
+ Vault,
22
+ VaultClass,
23
+ VaultClient,
24
+ VaultDepositor,
25
+ decodeName,
26
+ } from '../src';
27
+ import { Command } from 'commander';
28
+ import {
29
+ Connection,
30
+ Keypair,
31
+ PublicKey,
32
+ Transaction,
33
+ TransactionInstruction,
34
+ } from '@solana/web3.js';
35
+ import { AnchorProvider, Wallet as AnchorWallet } from '@coral-xyz/anchor';
36
+ import * as anchor from '@coral-xyz/anchor';
37
+ import { IDL } from '../src/utils';
38
+ import { DriftVaults } from '../src/types/drift_vaults';
39
+ import { getLedgerWallet } from './ledgerWallet';
40
+ import fs from 'fs';
41
+ import { bs58 } from '@coral-xyz/anchor/dist/cjs/utils/bytes';
42
+
43
+ export async function printVault(
44
+ slot: number,
45
+ driftClient: DriftClient,
46
+ vault: Vault,
47
+ vaultEquity: BN,
48
+ spotMarket: SpotMarketAccount,
49
+ spotOracle: OraclePriceData,
50
+ feeUpdateAccount: FeeUpdate | null = null
51
+ ) {
52
+ const oraclePriceNum = convertToNumber(spotOracle.price, PRICE_PRECISION);
53
+ const spotPrecision = TEN.pow(new BN(spotMarket.decimals));
54
+ const spotSymbol = decodeName(spotMarket.name);
55
+
56
+ console.log(`slot: ${slot}`);
57
+ console.log(`vault: ${decodeName(vault.name)}`);
58
+ console.log(`vaultClass: ${vault.vaultClass}`);
59
+ if (vault.vaultClass === VaultClass.TRUSTED) {
60
+ console.log(
61
+ ` managerBorrowedValue: ${convertToNumber(
62
+ vault.managerBorrowedValue,
63
+ spotPrecision
64
+ )} ${spotSymbol}`
65
+ );
66
+ }
67
+ console.log(`pubkey: ${vault.pubkey.toBase58()}`);
68
+ console.log(`manager: ${vault.manager.toBase58()}`);
69
+ console.log(`tokenAccount: ${vault.tokenAccount.toBase58()}`);
70
+ console.log(`driftUserStats: ${vault.userStats.toBase58()}`);
71
+ console.log(`driftUser: ${vault.user.toBase58()}`);
72
+ console.log(`delegate: ${vault.delegate.toBase58()}`);
73
+ console.log(`liqDelegate: ${vault.liquidationDelegate.toBase58()}`);
74
+ console.log(`userShares: ${vault.userShares.toString()}`);
75
+ console.log(`totalShares: ${vault.totalShares.toString()}`);
76
+ const managerShares = vault.totalShares.sub(vault.userShares);
77
+ const managerSharePct =
78
+ managerShares.toNumber() / vault.totalShares.toNumber();
79
+ console.log(
80
+ ` [managerShares]: ${managerShares.toString()} (${(
81
+ managerSharePct * 100.0
82
+ ).toFixed(4)}%)`
83
+ );
84
+ console.log(`totalShares: ${vault.totalShares.toString()}`);
85
+ console.log(`lastFeeUpdateTs: ${vault.lastFeeUpdateTs.toString()}`);
86
+ console.log(`liquidationStartTs: ${vault.liquidationStartTs.toString()}`);
87
+ console.log(`redeemPeriod: ${vault.redeemPeriod.toString()}`);
88
+ console.log(
89
+ `totalWithdrawRequested: ${vault.totalWithdrawRequested.toString()}`
90
+ );
91
+ console.log(
92
+ `maxTokens: ${convertToNumber(
93
+ vault.maxTokens,
94
+ spotPrecision
95
+ )} ${spotSymbol} (${vault.maxTokens.toString()})`
96
+ );
97
+ console.log(`sharesBase: ${vault.sharesBase}`);
98
+ console.log(`managementFee: ${vault.managementFee.toString()}`);
99
+ console.log(`initTs: ${vault.initTs.toString()}`);
100
+ console.log(
101
+ `netDeposits: ${convertToNumber(
102
+ vault.netDeposits,
103
+ spotPrecision
104
+ )} ${spotSymbol} (${vault.netDeposits.toString()})`
105
+ );
106
+ console.log(
107
+ `totalDeposits: ${convertToNumber(
108
+ vault.totalDeposits,
109
+ spotPrecision
110
+ )} ${spotSymbol} (${vault.totalDeposits.toString()})`
111
+ );
112
+ console.log(
113
+ `totalWithdraws: ${convertToNumber(
114
+ vault.totalWithdraws,
115
+ spotPrecision
116
+ )} ${spotSymbol} (${vault.totalWithdraws.toString()})`
117
+ );
118
+ console.log(
119
+ `managerNetDeposits: ${convertToNumber(
120
+ vault.managerNetDeposits,
121
+ spotPrecision
122
+ )} ${spotSymbol} (${vault.managerNetDeposits.toString()})`
123
+ );
124
+ console.log(
125
+ `managerTotalDeposits: ${convertToNumber(
126
+ vault.managerTotalDeposits,
127
+ spotPrecision
128
+ )} ${spotSymbol} (${vault.managerTotalDeposits.toString()})`
129
+ );
130
+ console.log(
131
+ `managerTotalWithdraws: ${convertToNumber(
132
+ vault.managerTotalWithdraws,
133
+ spotPrecision
134
+ )} ${spotSymbol} (${vault.managerTotalWithdraws.toString()})`
135
+ );
136
+ console.log(
137
+ `managerTotalFee: ${convertToNumber(
138
+ vault.managerTotalFee,
139
+ spotPrecision
140
+ )} ${spotSymbol} (${vault.managerTotalFee.toString()})`
141
+ );
142
+ console.log(
143
+ `managerTotalProfitShare: ${convertToNumber(
144
+ vault.managerTotalProfitShare,
145
+ spotPrecision
146
+ )} ${spotSymbol} (${vault.managerTotalProfitShare.toString()})`
147
+ );
148
+ console.log(`lastManagerWithdrawRequest:`);
149
+ console.log(
150
+ ` shares: ${vault.lastManagerWithdrawRequest.shares.toString()}`
151
+ );
152
+ console.log(
153
+ ` values: ${convertToNumber(
154
+ vault.lastManagerWithdrawRequest.value,
155
+ spotPrecision
156
+ )} ${spotSymbol} (${vault.lastManagerWithdrawRequest.value.toString()})`
157
+ );
158
+ console.log(` ts: ${vault.lastManagerWithdrawRequest.ts.toString()}`);
159
+ console.log(`FeeUpdate Account:`);
160
+ if (feeUpdateAccount) {
161
+ const timeUntilUpdate = feeUpdateAccount.incomingUpdateTs.sub(
162
+ new BN(Date.now() / 1000)
163
+ );
164
+ console.log(
165
+ ` incomingUpdateTs: ${feeUpdateAccount.incomingUpdateTs.toString()} (in ${timeUntilUpdate.toString()} seconds)`
166
+ );
167
+ console.log(
168
+ ` incomingManagementFee: ${vault.managementFee.toString()} -> ${feeUpdateAccount.incomingManagementFee.toString()}`
169
+ );
170
+ console.log(
171
+ ` incomingProfitShare: ${vault.profitShare} -> ${feeUpdateAccount.incomingProfitShare}`
172
+ );
173
+ console.log(
174
+ ` incomingHurdleRate: ${vault.hurdleRate} -> ${feeUpdateAccount.incomingHurdleRate}`
175
+ );
176
+ } else {
177
+ console.log(` None`);
178
+ }
179
+ console.log(`minDepositAmount: ${vault.minDepositAmount.toString()}`);
180
+ console.log(`profitShare: ${vault.profitShare}`);
181
+ console.log(`hurdleRate: ${vault.hurdleRate}`);
182
+ console.log(`spotMarketIndex: ${vault.spotMarketIndex}`);
183
+ console.log(`permissioned: ${vault.permissioned}`);
184
+
185
+ const vaultEquityNum = convertToNumber(vaultEquity, QUOTE_PRECISION);
186
+ const netDepositsNum = convertToNumber(vault.netDeposits, spotPrecision);
187
+ console.log(`vaultEquity (USDC): $${vaultEquityNum}`);
188
+ console.log(`manager share (USDC): $${managerSharePct * vaultEquityNum}`);
189
+
190
+ const vaultEquitySpot = vaultEquityNum / oraclePriceNum;
191
+
192
+ const user = new User({
193
+ // accountSubscription,
194
+ driftClient,
195
+ userAccountPublicKey: vault.user,
196
+ });
197
+ await user.subscribe();
198
+
199
+ for (const spotPos of user.getActiveSpotPositions()) {
200
+ const sm = driftClient.getSpotMarketAccount(spotPos.marketIndex)!;
201
+ const prec = TEN.pow(new BN(sm.decimals));
202
+ const sym = decodeName(sm.name);
203
+ const bal = getSignedTokenAmount(
204
+ getTokenAmount(spotPos.scaledBalance, sm, spotPos.balanceType),
205
+ spotPos.balanceType
206
+ );
207
+ console.log(
208
+ `Spot Position: ${spotPos.marketIndex}, ${convertToNumber(
209
+ bal,
210
+ prec
211
+ )} ${sym}`
212
+ );
213
+ }
214
+
215
+ for (const perpPos of user.getActivePerpPositions()) {
216
+ console.log(
217
+ `Perp Position: ${perpPos.marketIndex}, base: ${convertToNumber(
218
+ perpPos.baseAssetAmount,
219
+ BASE_PRECISION
220
+ )}, quote: ${convertToNumber(perpPos.quoteAssetAmount, QUOTE_PRECISION)}`
221
+ );
222
+ const upnl = user.getUnrealizedPNL(true, perpPos.marketIndex);
223
+ console.log(` upnl: ${convertToNumber(upnl, QUOTE_PRECISION)}`);
224
+ }
225
+
226
+ console.log(`vaultEquity (${spotSymbol}): ${vaultEquitySpot}`);
227
+ console.log(
228
+ `manager share (${spotSymbol}): ${managerSharePct * vaultEquitySpot}`
229
+ );
230
+ console.log(
231
+ `vault PnL (${spotSymbol}): ${vaultEquitySpot - netDepositsNum}`
232
+ );
233
+ console.log(
234
+ `vault PnL (USD) ${convertToNumber(
235
+ user.getTotalAllTimePnl(),
236
+ QUOTE_PRECISION
237
+ )}`
238
+ );
239
+ console.log(
240
+ `vault PnL (spot) ${
241
+ convertToNumber(user.getTotalAllTimePnl(), QUOTE_PRECISION) /
242
+ oraclePriceNum
243
+ }`
244
+ );
245
+
246
+ return {
247
+ managerShares,
248
+ managerSharePct,
249
+ };
250
+ }
251
+
252
+ export function printVaultDepositor(vaultDepositor: VaultDepositor) {
253
+ console.log(`vault: ${vaultDepositor.vault.toBase58()}`);
254
+ console.log(`pubkey: ${vaultDepositor.pubkey.toBase58()}`);
255
+ console.log(`authority: ${vaultDepositor.authority.toBase58()}`);
256
+ console.log(`vaultShares: ${vaultDepositor.vaultShares.toString()}`);
257
+ console.log(
258
+ `lastWithdrawRequest.Shares: ${vaultDepositor.lastWithdrawRequest.shares.toString()}`
259
+ );
260
+ console.log(
261
+ `lastWithdrawRequest.Value: ${vaultDepositor.lastWithdrawRequest.value.toString()}`
262
+ );
263
+ console.log(
264
+ `lastWithdrawRequest.Ts: ${vaultDepositor.lastWithdrawRequest.ts.toString()}`
265
+ );
266
+ console.log(
267
+ `lastValidTs: ${vaultDepositor.lastValidTs.toString()}`
268
+ );
269
+ console.log(
270
+ `netDeposits: ${vaultDepositor.netDeposits.toString()}`
271
+ );
272
+ console.log(
273
+ `totalDeposits: ${vaultDepositor.totalDeposits.toString()}`
274
+ );
275
+ console.log(
276
+ `totalWithdraws: ${vaultDepositor.totalWithdraws.toString()}`
277
+ );
278
+ console.log(
279
+ `cumulativeProfitShareAmount: ${vaultDepositor.cumulativeProfitShareAmount.toString()}`
280
+ );
281
+ console.log(
282
+ `vaultSharesBase: ${vaultDepositor.vaultSharesBase.toString()}`
283
+ );
284
+ }
285
+
286
+ export async function getCommandContext(
287
+ program: Command,
288
+ needToSign: boolean
289
+ ): Promise<{
290
+ driftClient: DriftClient;
291
+ driftVault: VaultClient;
292
+ wallet: Wallet;
293
+ }> {
294
+ const opts = program.opts();
295
+
296
+ let wallet: Wallet;
297
+ const isLedgerUrl = opts.keypair.startsWith('usb://ledger');
298
+ console.log('isLedgerUrl:', isLedgerUrl);
299
+
300
+ if (isLedgerUrl || fs.existsSync(opts.keypair)) {
301
+ console.log('opts.keypair:', opts.keypair);
302
+ } else {
303
+ console.log('opts.keypair:', opts.keypair.replace(/./g, '*'));
304
+ }
305
+
306
+ let loadedKeypair = false;
307
+ wallet = new Wallet(Keypair.generate());
308
+
309
+ if (isLedgerUrl) {
310
+ wallet = (await getLedgerWallet(opts.keypair)) as unknown as Wallet;
311
+ loadedKeypair = true;
312
+ } else if (opts.keypair) {
313
+ try {
314
+ const keypair = loadKeypair(opts.keypair as string);
315
+ wallet = new Wallet(keypair);
316
+ loadedKeypair = true;
317
+ } catch (e) {
318
+ console.error(`Need to provide a valid keypair: ${e}`);
319
+ process.exit(1);
320
+ }
321
+ } else {
322
+ if (needToSign) {
323
+ throw new Error('Need to provide a keypair.');
324
+ }
325
+ }
326
+
327
+ const driftEnv = opts.env as DriftEnv;
328
+ console.log('driftEnv:', driftEnv);
329
+
330
+ if (loadedKeypair) {
331
+ console.log(`Loaded wallet address: ${wallet.publicKey.toBase58()}`);
332
+ }
333
+
334
+ const connection = new Connection(opts.url, {
335
+ commitment: opts.commitment,
336
+ });
337
+
338
+ const driftClient = new DriftClient({
339
+ connection,
340
+ wallet,
341
+ env: driftEnv as DriftEnv,
342
+ opts: {
343
+ commitment: opts.commitment,
344
+ skipPreflight: false,
345
+ preflightCommitment: opts.commitment,
346
+ },
347
+ txSender: new WhileValidTxSender({
348
+ connection,
349
+ wallet,
350
+ opts: {
351
+ maxRetries: 0,
352
+ },
353
+ retrySleep: 1000,
354
+ }),
355
+ });
356
+ await driftClient.subscribe();
357
+
358
+ const provider = new AnchorProvider(connection, wallet as AnchorWallet, {});
359
+ anchor.setProvider(provider);
360
+ const vaultProgram = new anchor.Program<DriftVaults>(IDL, provider);
361
+
362
+ const driftVault = new VaultClient({
363
+ driftClient,
364
+ program: vaultProgram,
365
+ cliMode: true,
366
+ });
367
+
368
+ return {
369
+ driftClient,
370
+ driftVault,
371
+ wallet,
372
+ };
373
+ }
374
+
375
+ /// a valid blockhash to allow tx simulation
376
+ export const BLOCKHASH_PLACEHOLDER =
377
+ 'CHNfFRxxeCTyy1RRnKGsV2dkrJfKwVEa8zE7cZgK4TSe';
378
+
379
+ export function dumpTransactionMessage(
380
+ payer: PublicKey,
381
+ ixs: Array<TransactionInstruction>
382
+ ): string {
383
+ const tx = new Transaction();
384
+ tx.add(...ixs);
385
+ tx.feePayer = payer;
386
+ tx.recentBlockhash = BLOCKHASH_PLACEHOLDER;
387
+
388
+ return bs58.encode(tx.serialize({ requireAllSignatures: false }));
389
+ }