@drift-labs/vaults-sdk 0.1.416 → 0.1.417

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.
@@ -8,6 +8,7 @@ import {
8
8
  getUserStatsAccountPublicKey,
9
9
  TEN,
10
10
  UserMap,
11
+ unstakeSharesToAmount as depositSharesToVaultAmount,
11
12
  ZERO,
12
13
  } from '@drift-labs/sdk';
13
14
  import { BorshAccountsCoder, Program, ProgramAccount } from '@coral-xyz/anchor';
@@ -21,8 +22,10 @@ import {
21
22
  getTokenVaultAddressSync,
22
23
  getVaultAddressSync,
23
24
  getVaultDepositorAddressSync,
25
+ getVaultProtocolAddressSync,
24
26
  } from './addresses';
25
27
  import {
28
+ AccountMeta,
26
29
  AddressLookupTableAccount,
27
30
  ComputeBudgetProgram,
28
31
  PublicKey,
@@ -37,9 +40,17 @@ import {
37
40
  getAssociatedTokenAddressSync,
38
41
  TOKEN_PROGRAM_ID,
39
42
  } from '@solana/spl-token';
40
- import { Vault, VaultDepositor, WithdrawUnit } from './types/types';
43
+ import {
44
+ Vault,
45
+ VaultDepositor,
46
+ VaultParams,
47
+ VaultProtocol,
48
+ VaultProtocolParams,
49
+ WithdrawUnit,
50
+ } from './types/types';
41
51
  import { bs58 } from '@coral-xyz/anchor/dist/cjs/utils/bytes';
42
52
  import { UserMapConfig } from '@drift-labs/sdk/lib/userMap/userMapConfig';
53
+ import { calculateRealizedVaultDepositorEquity } from './math';
43
54
 
44
55
  export type TxParams = {
45
56
  cuLimit?: number;
@@ -87,8 +98,14 @@ export class VaultClient {
87
98
  }
88
99
  }
89
100
 
101
+ /**
102
+ * Unsubscribes from the vault users map. Call this to clean up any dangling promises.
103
+ */
104
+ public async unsubscribe() {
105
+ await this.vaultUsers.unsubscribe();
106
+ }
107
+
90
108
  public async getVault(vault: PublicKey): Promise<Vault> {
91
- // @ts-ignore
92
109
  return await this.program.account.vault.fetch(vault);
93
110
  }
94
111
 
@@ -119,6 +136,27 @@ export class VaultClient {
119
136
  };
120
137
  }
121
138
 
139
+ public getVaultProtocolAddress(vault: PublicKey): PublicKey {
140
+ return getVaultProtocolAddressSync(this.program.programId, vault);
141
+ }
142
+
143
+ public async getVaultProtocol(
144
+ vaultProtocol: PublicKey
145
+ ): Promise<VaultProtocol> {
146
+ return await this.program.account.vaultProtocol.fetch(vaultProtocol);
147
+ }
148
+
149
+ public async getVaultProtocolAndSlot(
150
+ vaultProtocol: PublicKey
151
+ ): Promise<{ vaultProtocol: VaultProtocol; slot: number }> {
152
+ const vaultProtocolAndSlot =
153
+ await this.program.account.vaultProtocol.fetchAndContext(vaultProtocol);
154
+ return {
155
+ vaultProtocol: vaultProtocolAndSlot.data as VaultProtocol,
156
+ slot: vaultProtocolAndSlot.context.slot,
157
+ };
158
+ }
159
+
122
160
  public async getAllVaultDepositorsWithNoWithdrawRequest(
123
161
  vault: PublicKey
124
162
  ): Promise<ProgramAccount<VaultDepositor>[]> {
@@ -189,13 +227,21 @@ export class VaultClient {
189
227
  /**
190
228
  *
191
229
  * @param vault pubkey
230
+ * @param factorUnrealizedPNL add unrealized pnl to net balance
192
231
  * @returns vault equity, in USDC
193
232
  */
194
233
  public async calculateVaultEquity(params: {
195
234
  address?: PublicKey;
196
235
  vault?: Vault;
236
+ factorUnrealizedPNL?: boolean;
197
237
  }): Promise<BN> {
198
238
  try {
239
+ // defaults to true if undefined
240
+ let factorUnrealizedPNL = true;
241
+ if (params.factorUnrealizedPNL !== undefined) {
242
+ factorUnrealizedPNL = params.factorUnrealizedPNL;
243
+ }
244
+
199
245
  let vaultAccount: Vault;
200
246
  if (params.address !== undefined) {
201
247
  // @ts-ignore
@@ -209,9 +255,13 @@ export class VaultClient {
209
255
  const user = await this.getSubscribedVaultUser(vaultAccount.user);
210
256
 
211
257
  const netSpotValue = user.getNetSpotMarketValue();
212
- const unrealizedPnl = user.getUnrealizedPNL(true, undefined, undefined);
213
258
 
214
- return netSpotValue.add(unrealizedPnl);
259
+ if (factorUnrealizedPNL) {
260
+ const unrealizedPnl = user.getUnrealizedPNL(true, undefined, undefined);
261
+ return netSpotValue.add(unrealizedPnl);
262
+ } else {
263
+ return netSpotValue;
264
+ }
215
265
  } catch (err) {
216
266
  console.error('VaultClient ~ err:', err);
217
267
  return ZERO;
@@ -221,15 +271,16 @@ export class VaultClient {
221
271
  /**
222
272
  *
223
273
  * @param vault pubkey
224
- * @returns vault equity, in spot deposit asset
274
+ * @param factorUnrealizedPNL add unrealized pnl to existing equity
275
+ * @returns total vault equity, in spot deposit asset
225
276
  */
226
277
  public async calculateVaultEquityInDepositAsset(params: {
227
278
  address?: PublicKey;
228
279
  vault?: Vault;
280
+ factorUnrealizedPNL?: boolean;
229
281
  }): Promise<BN> {
230
282
  let vaultAccount: Vault;
231
283
  if (params.address !== undefined) {
232
- // @ts-ignore
233
284
  vaultAccount = await this.program.account.vault.fetch(params.address);
234
285
  } else if (params.vault !== undefined) {
235
286
  vaultAccount = params.vault;
@@ -238,6 +289,7 @@ export class VaultClient {
238
289
  }
239
290
  const vaultEquity = await this.calculateVaultEquity({
240
291
  vault: vaultAccount,
292
+ factorUnrealizedPNL: params.factorUnrealizedPNL,
241
293
  });
242
294
  const spotMarket = this.driftClient.getSpotMarketAccount(
243
295
  vaultAccount.spotMarketIndex
@@ -250,6 +302,124 @@ export class VaultClient {
250
302
  return vaultEquity.mul(spotPrecision).div(spotOracle.price);
251
303
  }
252
304
 
305
+ /**
306
+ * @param params
307
+ * @returns vault depositor equity, in spot market value (which is usually USDC)
308
+ */
309
+ public async calculateWithdrawableVaultDepositorEquity(params: {
310
+ vaultDepositorAddress?: PublicKey;
311
+ vaultDepositor?: VaultDepositor;
312
+ vaultAddress?: PublicKey;
313
+ vault?: Vault;
314
+ }): Promise<BN> {
315
+ let vaultAccount: Vault;
316
+ if (params.vaultAddress !== undefined) {
317
+ vaultAccount = await this.program.account.vault.fetch(
318
+ params.vaultAddress
319
+ );
320
+ } else if (params.vault !== undefined) {
321
+ vaultAccount = params.vault;
322
+ } else {
323
+ throw new Error('Must supply vaultAddress or vault');
324
+ }
325
+
326
+ let vaultDepositorAccount: VaultDepositor;
327
+ if (params.vaultDepositorAddress !== undefined) {
328
+ vaultDepositorAccount = await this.program.account.vaultDepositor.fetch(
329
+ params.vaultDepositorAddress
330
+ );
331
+ } else if (params.vaultDepositor !== undefined) {
332
+ vaultDepositorAccount = params.vaultDepositor;
333
+ } else {
334
+ throw new Error('Must supply vaultDepositorAddress or vaultDepositor');
335
+ }
336
+
337
+ const vaultEquity = await this.calculateVaultEquity({
338
+ vault: vaultAccount,
339
+ factorUnrealizedPNL: false,
340
+ });
341
+ return calculateRealizedVaultDepositorEquity(
342
+ vaultDepositorAccount,
343
+ vaultEquity,
344
+ vaultAccount
345
+ );
346
+ }
347
+
348
+ public async calculateWithdrawableVaultDepositorEquityInDepositAsset(params: {
349
+ vaultDepositorAddress?: PublicKey;
350
+ vaultDepositor?: VaultDepositor;
351
+ vaultAddress?: PublicKey;
352
+ vault?: Vault;
353
+ }): Promise<BN> {
354
+ let vaultAccount: Vault;
355
+ if (params.vaultAddress !== undefined) {
356
+ vaultAccount = await this.program.account.vault.fetch(
357
+ params.vaultAddress
358
+ );
359
+ } else if (params.vault !== undefined) {
360
+ vaultAccount = params.vault;
361
+ } else {
362
+ throw new Error('Must supply vaultAddress or vault');
363
+ }
364
+
365
+ let vaultDepositorAccount: VaultDepositor;
366
+ if (params.vaultDepositorAddress !== undefined) {
367
+ vaultDepositorAccount = await this.program.account.vaultDepositor.fetch(
368
+ params.vaultDepositorAddress
369
+ );
370
+ } else if (params.vaultDepositor !== undefined) {
371
+ vaultDepositorAccount = params.vaultDepositor;
372
+ } else {
373
+ throw new Error('Must supply vaultDepositorAddress or vaultDepositor');
374
+ }
375
+
376
+ let vaultProtocol: VaultProtocol | undefined = undefined;
377
+ if (!vaultAccount.vaultProtocol.equals(SystemProgram.programId)) {
378
+ vaultProtocol = await this.program.account.vaultProtocol.fetch(
379
+ vaultAccount.vaultProtocol
380
+ );
381
+ }
382
+
383
+ const vaultEquity = await this.calculateVaultEquity({
384
+ vault: vaultAccount,
385
+ factorUnrealizedPNL: false,
386
+ });
387
+ const vdEquity = calculateRealizedVaultDepositorEquity(
388
+ vaultDepositorAccount,
389
+ vaultEquity,
390
+ vaultAccount,
391
+ vaultProtocol
392
+ );
393
+
394
+ const spotMarket = this.driftClient.getSpotMarketAccount(
395
+ vaultAccount.spotMarketIndex
396
+ );
397
+ const spotOracle = this.driftClient.getOracleDataForSpotMarket(
398
+ vaultAccount.spotMarketIndex
399
+ );
400
+ const spotPrecision = TEN.pow(new BN(spotMarket!.decimals));
401
+
402
+ return vdEquity.mul(spotPrecision).div(spotOracle.price);
403
+ }
404
+
405
+ public async calculateVaultProtocolEquity(params: {
406
+ vault: PublicKey;
407
+ }): Promise<BN> {
408
+ const vaultAccount = await this.program.account.vault.fetch(params.vault);
409
+ const vaultTotalEquity = await this.calculateVaultEquity({
410
+ vault: vaultAccount,
411
+ });
412
+ const vaultProtocol = this.getVaultProtocolAddress(params.vault);
413
+ const vpAccount = await this.program.account.vaultProtocol.fetch(
414
+ vaultProtocol
415
+ );
416
+ return depositSharesToVaultAmount(
417
+ vpAccount.protocolProfitAndFeeShares,
418
+ vaultAccount.totalShares,
419
+ vaultTotalEquity
420
+ );
421
+ }
422
+
253
423
  public async initializeVault(params: {
254
424
  name: number[];
255
425
  spotMarketIndex: number;
@@ -260,7 +430,17 @@ export class VaultClient {
260
430
  profitShare: number;
261
431
  hurdleRate: number;
262
432
  permissioned: boolean;
433
+ vaultProtocol?: VaultProtocolParams;
263
434
  }): Promise<TransactionSignature> {
435
+ // This is a workaround to make client backwards compatible.
436
+ // VaultProtocol is optionally undefined, but the anchor type is optionally null.
437
+ // Old clients will default to undefined which prevents old clients from having to pass in a null value.
438
+ // Instead, we can cast to null internally.
439
+ const _params: VaultParams = {
440
+ ...params,
441
+ vaultProtocol: params.vaultProtocol ? params.vaultProtocol : null,
442
+ };
443
+
264
444
  const vault = getVaultAddressSync(this.program.programId, params.name);
265
445
  const tokenAccount = getTokenVaultAddressSync(
266
446
  this.program.programId,
@@ -297,18 +477,44 @@ export class VaultClient {
297
477
  driftProgram: this.driftClient.program.programId,
298
478
  };
299
479
 
300
- return await this.program.methods
301
- .initializeVault(params)
302
- .preInstructions([
303
- ComputeBudgetProgram.setComputeUnitLimit({
304
- units: 400_000,
305
- }),
306
- ComputeBudgetProgram.setComputeUnitPrice({
307
- microLamports: 300_000,
308
- }),
309
- ])
310
- .accounts(accounts)
311
- .rpc();
480
+ if (params.vaultProtocol) {
481
+ const vaultProtocol = this.getVaultProtocolAddress(
482
+ getVaultAddressSync(this.program.programId, params.name)
483
+ );
484
+ const remainingAccounts: AccountMeta[] = [
485
+ {
486
+ pubkey: vaultProtocol,
487
+ isSigner: false,
488
+ isWritable: true,
489
+ },
490
+ ];
491
+ return await this.program.methods
492
+ .initializeVault(_params)
493
+ .preInstructions([
494
+ ComputeBudgetProgram.setComputeUnitLimit({
495
+ units: 400_000,
496
+ }),
497
+ ComputeBudgetProgram.setComputeUnitPrice({
498
+ microLamports: 300_000,
499
+ }),
500
+ ])
501
+ .accounts(accounts)
502
+ .remainingAccounts(remainingAccounts)
503
+ .rpc();
504
+ } else {
505
+ return await this.program.methods
506
+ .initializeVault(_params)
507
+ .preInstructions([
508
+ ComputeBudgetProgram.setComputeUnitLimit({
509
+ units: 400_000,
510
+ }),
511
+ ComputeBudgetProgram.setComputeUnitPrice({
512
+ microLamports: 300_000,
513
+ }),
514
+ ])
515
+ .accounts(accounts)
516
+ .rpc();
517
+ }
312
518
  }
313
519
 
314
520
  /**
@@ -344,7 +550,7 @@ export class VaultClient {
344
550
  /**
345
551
  * Updates the vault margin trading status.
346
552
  * @param vault vault address to update
347
- * @param enabeld whether to enable margin trading
553
+ * @param enabled whether to enable margin trading
348
554
  * @returns
349
555
  */
350
556
  public async updateMarginTradingEnabled(
@@ -388,6 +594,14 @@ export class VaultClient {
388
594
  userAccounts: [user.getUserAccount()],
389
595
  writableSpotMarketIndexes: [vaultAccount.spotMarketIndex],
390
596
  });
597
+ if (!vaultAccount.vaultProtocol.equals(SystemProgram.programId)) {
598
+ const vaultProtocol = this.getVaultProtocolAddress(vault);
599
+ remainingAccounts.push({
600
+ pubkey: vaultProtocol,
601
+ isSigner: false,
602
+ isWritable: true,
603
+ });
604
+ }
391
605
 
392
606
  return await this.program.methods
393
607
  .managerDeposit(amount)
@@ -435,6 +649,14 @@ export class VaultClient {
435
649
  userAccounts: [user.getUserAccount()],
436
650
  writableSpotMarketIndexes: [vaultAccount.spotMarketIndex],
437
651
  });
652
+ if (!vaultAccount.vaultProtocol.equals(SystemProgram.programId)) {
653
+ const vaultProtocol = this.getVaultProtocolAddress(vault);
654
+ remainingAccounts.push({
655
+ pubkey: vaultProtocol,
656
+ isSigner: false,
657
+ isWritable: true,
658
+ });
659
+ }
438
660
 
439
661
  const userStatsKey = getUserStatsAccountPublicKey(
440
662
  this.driftClient.program.programId,
@@ -499,6 +721,14 @@ export class VaultClient {
499
721
  const remainingAccounts = this.driftClient.getRemainingAccounts({
500
722
  userAccounts: [user.getUserAccount()],
501
723
  });
724
+ if (!vaultAccount.vaultProtocol.equals(SystemProgram.programId)) {
725
+ const vaultProtocol = this.getVaultProtocolAddress(vault);
726
+ remainingAccounts.push({
727
+ pubkey: vaultProtocol,
728
+ isSigner: false,
729
+ isWritable: true,
730
+ });
731
+ }
502
732
 
503
733
  if (this.cliMode) {
504
734
  return await this.program.methods
@@ -535,6 +765,14 @@ export class VaultClient {
535
765
  userAccounts: [user.getUserAccount()],
536
766
  writableSpotMarketIndexes: [vaultAccount.spotMarketIndex],
537
767
  });
768
+ if (!vaultAccount.vaultProtocol.equals(SystemProgram.programId)) {
769
+ const vaultProtocol = this.getVaultProtocolAddress(vault);
770
+ remainingAccounts.push({
771
+ pubkey: vaultProtocol,
772
+ isSigner: false,
773
+ isWritable: true,
774
+ });
775
+ }
538
776
 
539
777
  const spotMarket = this.driftClient.getSpotMarketAccount(
540
778
  vaultAccount.spotMarketIndex
@@ -728,6 +966,14 @@ export class VaultClient {
728
966
  userAccounts: [user.getUserAccount()],
729
967
  writableSpotMarketIndexes: [vaultAccount.spotMarketIndex],
730
968
  });
969
+ if (!vaultAccount.vaultProtocol.equals(SystemProgram.programId)) {
970
+ const vaultProtocol = this.getVaultProtocolAddress(vaultPubKey);
971
+ remainingAccounts.push({
972
+ pubkey: vaultProtocol,
973
+ isSigner: false,
974
+ isWritable: true,
975
+ });
976
+ }
731
977
 
732
978
  const userStatsKey = getUserStatsAccountPublicKey(
733
979
  this.driftClient.program.programId,
@@ -769,6 +1015,15 @@ export class VaultClient {
769
1015
  };
770
1016
  }
771
1017
 
1018
+ /**
1019
+ * Creates a transaction to deposit funds into the specified vault.
1020
+ * Uses the associated token account of the vault depositor authority and spot market mint,
1021
+ * and assumes it exists before calling this function.
1022
+ * @param vaultDepositor
1023
+ * @param amount
1024
+ * @param initVaultDepositor If true, will initialize the vault depositor account
1025
+ * @returns transaction
1026
+ */
772
1027
  public async createDepositTx(
773
1028
  vaultDepositor: PublicKey,
774
1029
  amount: BN,
@@ -781,23 +1036,28 @@ export class VaultClient {
781
1036
  const { vaultAccount, accounts, remainingAccounts } =
782
1037
  await this.prepDepositTx(vaultDepositor, amount, initVaultDepositor);
783
1038
 
784
- const depositIx = this.program.instruction.deposit(amount, {
785
- accounts: {
786
- authority: this.driftClient.wallet.publicKey,
787
- ...accounts,
788
- },
789
- remainingAccounts,
790
- });
1039
+ const ixs: TransactionInstruction[] = [];
791
1040
 
792
1041
  if (initVaultDepositor) {
793
- const initIx = this.createInitVaultDepositorIx(
794
- vaultAccount.pubkey,
795
- initVaultDepositor.authority
1042
+ ixs.push(
1043
+ this.createInitVaultDepositorIx(
1044
+ vaultAccount.pubkey,
1045
+ initVaultDepositor.authority
1046
+ )
796
1047
  );
797
- return await this.createTxn([initIx, depositIx], txParams);
798
- } else {
799
- return await this.createTxn([depositIx], txParams);
800
1048
  }
1049
+
1050
+ const depositIx = await this.program.methods
1051
+ .deposit(amount)
1052
+ .accounts({
1053
+ authority: this.driftClient.wallet.publicKey,
1054
+ ...accounts,
1055
+ })
1056
+ .remainingAccounts(remainingAccounts)
1057
+ .instruction();
1058
+ ixs.push(depositIx);
1059
+
1060
+ return await this.createTxn(ixs, txParams);
801
1061
  }
802
1062
 
803
1063
  /**
@@ -805,6 +1065,7 @@ export class VaultClient {
805
1065
  * @param vaultDepositor
806
1066
  * @param amount
807
1067
  * @param initVaultDepositor If true, will initialize the vault depositor account
1068
+ * @param txParams
808
1069
  * @returns
809
1070
  */
810
1071
  public async deposit(
@@ -859,6 +1120,16 @@ export class VaultClient {
859
1120
  const remainingAccounts = this.driftClient.getRemainingAccounts({
860
1121
  userAccounts: [user.getUserAccount()],
861
1122
  });
1123
+ if (!vaultAccount.vaultProtocol.equals(SystemProgram.programId)) {
1124
+ const vaultProtocol = this.getVaultProtocolAddress(
1125
+ vaultDepositorAccount.vault
1126
+ );
1127
+ remainingAccounts.push({
1128
+ pubkey: vaultProtocol,
1129
+ isSigner: false,
1130
+ isWritable: true,
1131
+ });
1132
+ }
862
1133
 
863
1134
  const userStatsKey = getUserStatsAccountPublicKey(
864
1135
  this.driftClient.program.programId,
@@ -915,6 +1186,16 @@ export class VaultClient {
915
1186
  userAccounts: [user.getUserAccount()],
916
1187
  writableSpotMarketIndexes: [vaultAccount.spotMarketIndex],
917
1188
  });
1189
+ const vaultProtocol = this.getVaultProtocolAddress(
1190
+ vaultDepositorAccount.vault
1191
+ );
1192
+ if (!vaultProtocol.equals(SystemProgram.programId)) {
1193
+ remainingAccounts.push({
1194
+ pubkey: vaultProtocol,
1195
+ isSigner: false,
1196
+ isWritable: true,
1197
+ });
1198
+ }
918
1199
 
919
1200
  const userStatsKey = getUserStatsAccountPublicKey(
920
1201
  this.driftClient.program.programId,
@@ -966,20 +1247,30 @@ export class VaultClient {
966
1247
  };
967
1248
 
968
1249
  if (this.cliMode) {
969
- return await this.program.methods
970
- .withdraw()
971
- .accounts(accounts)
972
- .remainingAccounts(remainingAccounts)
973
- .rpc();
1250
+ if (createAtaIx) {
1251
+ return await this.program.methods
1252
+ .withdraw()
1253
+ .accounts(accounts)
1254
+ .remainingAccounts(remainingAccounts)
1255
+ .preInstructions([createAtaIx])
1256
+ .rpc();
1257
+ } else {
1258
+ return await this.program.methods
1259
+ .withdraw()
1260
+ .accounts(accounts)
1261
+ .remainingAccounts(remainingAccounts)
1262
+ .rpc();
1263
+ }
974
1264
  } else {
975
1265
  const ixs = [
976
- this.program.instruction.withdraw({
977
- accounts: {
1266
+ await this.program.methods
1267
+ .withdraw()
1268
+ .accounts({
978
1269
  authority: this.driftClient.wallet.publicKey,
979
1270
  ...accounts,
980
- },
981
- remainingAccounts,
982
- }),
1271
+ })
1272
+ .remainingAccounts(remainingAccounts)
1273
+ .instruction(),
983
1274
  ];
984
1275
  if (createAtaIx) {
985
1276
  ixs.unshift(createAtaIx);
@@ -1006,6 +1297,16 @@ export class VaultClient {
1006
1297
  userAccounts: [user.getUserAccount()],
1007
1298
  writableSpotMarketIndexes: [vaultAccount.spotMarketIndex],
1008
1299
  });
1300
+ if (!vaultAccount.vaultProtocol.equals(SystemProgram.programId)) {
1301
+ const vaultProtocol = this.getVaultProtocolAddress(
1302
+ vaultDepositorAccount.vault
1303
+ );
1304
+ remainingAccounts.push({
1305
+ pubkey: vaultProtocol,
1306
+ isSigner: false,
1307
+ isWritable: true,
1308
+ });
1309
+ }
1009
1310
 
1010
1311
  const userStatsKey = getUserStatsAccountPublicKey(
1011
1312
  this.driftClient.program.programId,
@@ -1096,6 +1397,16 @@ export class VaultClient {
1096
1397
  const remainingAccounts = this.driftClient.getRemainingAccounts({
1097
1398
  userAccounts: [user.getUserAccount()],
1098
1399
  });
1400
+ if (!vaultAccount.vaultProtocol.equals(SystemProgram.programId)) {
1401
+ const vaultProtocol = this.getVaultProtocolAddress(
1402
+ vaultDepositorAccount.vault
1403
+ );
1404
+ remainingAccounts.push({
1405
+ pubkey: vaultProtocol,
1406
+ isSigner: false,
1407
+ isWritable: true,
1408
+ });
1409
+ }
1099
1410
 
1100
1411
  if (this.cliMode) {
1101
1412
  return await this.program.methods
@@ -1326,4 +1637,191 @@ export class VaultClient {
1326
1637
  })
1327
1638
  .rpc();
1328
1639
  }
1640
+
1641
+ public async protocolRequestWithdraw(
1642
+ vault: PublicKey,
1643
+ amount: BN,
1644
+ withdrawUnit: WithdrawUnit
1645
+ ): Promise<TransactionSignature> {
1646
+ // @ts-ignore
1647
+ const vaultAccount = (await this.program.account.vault.fetch(
1648
+ vault
1649
+ )) as Vault;
1650
+ const vp = this.getVaultProtocolAddress(vault);
1651
+ const vpAccount = (await this.program.account.vaultProtocol.fetch(
1652
+ vp
1653
+ )) as VaultProtocol;
1654
+
1655
+ if (!this.driftClient.wallet.publicKey.equals(vpAccount.protocol)) {
1656
+ throw new Error(`Only the protocol of the vault can request a withdraw.`);
1657
+ }
1658
+
1659
+ const user = await this.getSubscribedVaultUser(vaultAccount.user);
1660
+ const remainingAccounts = this.driftClient.getRemainingAccounts({
1661
+ userAccounts: [user.getUserAccount()],
1662
+ writableSpotMarketIndexes: [vaultAccount.spotMarketIndex],
1663
+ });
1664
+ if (!vaultAccount.vaultProtocol.equals(SystemProgram.programId)) {
1665
+ const vaultProtocol = this.getVaultProtocolAddress(vault);
1666
+ remainingAccounts.push({
1667
+ pubkey: vaultProtocol,
1668
+ isSigner: false,
1669
+ isWritable: true,
1670
+ });
1671
+ }
1672
+
1673
+ const userStatsKey = getUserStatsAccountPublicKey(
1674
+ this.driftClient.program.programId,
1675
+ vault
1676
+ );
1677
+
1678
+ const driftStateKey = await this.driftClient.getStatePublicKey();
1679
+
1680
+ const accounts = {
1681
+ vault,
1682
+ driftUserStats: userStatsKey,
1683
+ driftUser: vaultAccount.user,
1684
+ driftState: driftStateKey,
1685
+ };
1686
+
1687
+ if (this.cliMode) {
1688
+ return await this.program.methods
1689
+ // @ts-ignore, 0.29.0 anchor issues..
1690
+ .managerRequestWithdraw(amount, withdrawUnit)
1691
+ .accounts(accounts)
1692
+ .remainingAccounts(remainingAccounts)
1693
+ .rpc();
1694
+ } else {
1695
+ const requestWithdrawIx = this.program.instruction.managerRequestWithdraw(
1696
+ // @ts-ignore
1697
+ amount,
1698
+ withdrawUnit,
1699
+ {
1700
+ accounts: {
1701
+ manager: this.driftClient.wallet.publicKey,
1702
+ ...accounts,
1703
+ },
1704
+ remainingAccounts,
1705
+ }
1706
+ );
1707
+
1708
+ return await this.createAndSendTxn([requestWithdrawIx]);
1709
+ }
1710
+ }
1711
+
1712
+ public async protocolCancelWithdrawRequest(
1713
+ vault: PublicKey
1714
+ ): Promise<TransactionSignature> {
1715
+ const vaultAccount = await this.program.account.vault.fetch(vault);
1716
+
1717
+ const userStatsKey = getUserStatsAccountPublicKey(
1718
+ this.driftClient.program.programId,
1719
+ vault
1720
+ );
1721
+
1722
+ const driftStateKey = await this.driftClient.getStatePublicKey();
1723
+
1724
+ const accounts = {
1725
+ manager: this.driftClient.wallet.publicKey,
1726
+ vault,
1727
+ driftUserStats: userStatsKey,
1728
+ driftUser: vaultAccount.user,
1729
+ driftState: driftStateKey,
1730
+ };
1731
+
1732
+ const user = await this.getSubscribedVaultUser(vaultAccount.user);
1733
+ const remainingAccounts = this.driftClient.getRemainingAccounts({
1734
+ userAccounts: [user.getUserAccount()],
1735
+ });
1736
+ if (!vaultAccount.vaultProtocol.equals(SystemProgram.programId)) {
1737
+ const vaultProtocol = this.getVaultProtocolAddress(vault);
1738
+ remainingAccounts.push({
1739
+ pubkey: vaultProtocol,
1740
+ isSigner: false,
1741
+ isWritable: true,
1742
+ });
1743
+ }
1744
+
1745
+ if (this.cliMode) {
1746
+ return await this.program.methods
1747
+ .mangerCancelWithdrawRequest()
1748
+ .accounts(accounts)
1749
+ .remainingAccounts(remainingAccounts)
1750
+ .rpc();
1751
+ } else {
1752
+ const cancelRequestWithdrawIx =
1753
+ this.program.instruction.mangerCancelWithdrawRequest({
1754
+ accounts: {
1755
+ ...accounts,
1756
+ manager: this.driftClient.wallet.publicKey,
1757
+ },
1758
+ remainingAccounts,
1759
+ });
1760
+
1761
+ return await this.createAndSendTxn([cancelRequestWithdrawIx]);
1762
+ }
1763
+ }
1764
+
1765
+ public async protocolWithdraw(
1766
+ vault: PublicKey
1767
+ ): Promise<TransactionSignature> {
1768
+ const vaultAccount = await this.program.account.vault.fetch(vault);
1769
+
1770
+ if (!this.driftClient.wallet.publicKey.equals(vaultAccount.manager)) {
1771
+ throw new Error(`Only the manager of the vault can request a withdraw.`);
1772
+ }
1773
+
1774
+ const user = await this.getSubscribedVaultUser(vaultAccount.user);
1775
+
1776
+ const remainingAccounts = this.driftClient.getRemainingAccounts({
1777
+ userAccounts: [user.getUserAccount()],
1778
+ writableSpotMarketIndexes: [vaultAccount.spotMarketIndex],
1779
+ });
1780
+ if (!vaultAccount.vaultProtocol.equals(SystemProgram.programId)) {
1781
+ const vaultProtocol = this.getVaultProtocolAddress(vault);
1782
+ remainingAccounts.push({
1783
+ pubkey: vaultProtocol,
1784
+ isSigner: false,
1785
+ isWritable: true,
1786
+ });
1787
+ }
1788
+
1789
+ const spotMarket = this.driftClient.getSpotMarketAccount(
1790
+ vaultAccount.spotMarketIndex
1791
+ );
1792
+ if (!spotMarket) {
1793
+ throw new Error(
1794
+ `Spot market ${vaultAccount.spotMarketIndex} not found on driftClient`
1795
+ );
1796
+ }
1797
+
1798
+ const ix = this.program.instruction.managerWithdraw({
1799
+ accounts: {
1800
+ vault,
1801
+ manager: this.driftClient.wallet.publicKey,
1802
+ vaultTokenAccount: vaultAccount.tokenAccount,
1803
+ driftUser: await getUserAccountPublicKey(
1804
+ this.driftClient.program.programId,
1805
+ vault
1806
+ ),
1807
+ driftProgram: this.driftClient.program.programId,
1808
+ driftUserStats: getUserStatsAccountPublicKey(
1809
+ this.driftClient.program.programId,
1810
+ vault
1811
+ ),
1812
+ driftState: await this.driftClient.getStatePublicKey(),
1813
+ driftSpotMarketVault: spotMarket.vault,
1814
+ userTokenAccount: getAssociatedTokenAddressSync(
1815
+ spotMarket.mint,
1816
+ this.driftClient.wallet.publicKey
1817
+ ),
1818
+ driftSigner: this.driftClient.getStateAccount().signer,
1819
+ tokenProgram: TOKEN_PROGRAM_ID,
1820
+ },
1821
+ remainingAccounts,
1822
+ });
1823
+ return this.createAndSendTxn([ix], {
1824
+ cuLimit: 1_000_000,
1825
+ });
1826
+ }
1329
1827
  }