@drift-labs/vaults-sdk 0.8.29 → 0.9.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.
@@ -104,13 +104,27 @@ export type Vault = {
104
104
  vaultProtocol: boolean;
105
105
  fuelDistributionMode: FuelDistributionMode;
106
106
  feeUpdateStatus: FeeUpdateStatus;
107
- padding1: number[];
107
+ vaultClass: VaultClass;
108
108
  lastCumulativeFuelPerShareTs: number;
109
109
  cumulativeFuelPerShare: BN;
110
110
  cumulativeFuel: BN;
111
+ managerBorrowedValue: BN;
111
112
  padding: BN[];
112
113
  };
113
114
 
115
+ export enum VaultClass {
116
+ NORMAL = 0,
117
+ TRUSTED = 1,
118
+ }
119
+
120
+ export function isNormalVaultClass(vaultClass: number | VaultClass): boolean {
121
+ return (vaultClass & VaultClass.NORMAL) === VaultClass.NORMAL;
122
+ }
123
+
124
+ export function isTrustedVaultClass(vaultClass: number | VaultClass): boolean {
125
+ return (vaultClass & VaultClass.TRUSTED) === VaultClass.TRUSTED;
126
+ }
127
+
114
128
  export enum FuelDistributionMode {
115
129
  UsersOnly = 0,
116
130
  UsersAndManager = 1,
@@ -121,6 +135,13 @@ export enum FeeUpdateStatus {
121
135
  PendingFeeUpdate = 1,
122
136
  }
123
137
 
138
+ export function hasPendingFeeUpdate(status: number | FeeUpdateStatus): boolean {
139
+ return (
140
+ (status & FeeUpdateStatus.PendingFeeUpdate) ===
141
+ FeeUpdateStatus.PendingFeeUpdate
142
+ );
143
+ }
144
+
124
145
  export type FeeUpdate = {
125
146
  incomingUpdateTs: BN;
126
147
  incomingManagementFee: BN;
@@ -309,3 +330,30 @@ export type FeeUpdateRecord = {
309
330
  newProfitShare: number;
310
331
  newHurdleRate: number;
311
332
  };
333
+
334
+ export type ManagerBorrowRecord = {
335
+ ts: BN;
336
+ vault: PublicKey;
337
+ manager: PublicKey;
338
+ borrowAmount: BN;
339
+ borrowValue: BN;
340
+ borrowSpotMarketIndex: number;
341
+ borrowOraclePrice: BN;
342
+ depositSpotMarketIndex: number;
343
+ depositOraclePrice: BN;
344
+ vaultEquity: BN;
345
+ };
346
+
347
+ export type ManagerRepayRecord = {
348
+ ts: BN;
349
+ vault: PublicKey;
350
+ manager: PublicKey;
351
+ repayAmount: BN;
352
+ repayValue: BN;
353
+ repaySpotMarketIndex: number;
354
+ repayOraclePrice: BN;
355
+ depositSpotMarketIndex: number;
356
+ depositOraclePrice: BN;
357
+ vaultEquityBefore: BN;
358
+ vaultEquityAfter: BN;
359
+ };
@@ -51,9 +51,10 @@ import {
51
51
  } from '@solana/spl-token';
52
52
  import {
53
53
  FeeUpdate,
54
- FeeUpdateStatus,
55
54
  FuelDistributionMode,
55
+ hasPendingFeeUpdate,
56
56
  Vault,
57
+ VaultClass,
57
58
  VaultDepositor,
58
59
  VaultParams,
59
60
  VaultProtocol,
@@ -152,9 +153,7 @@ export class VaultClient {
152
153
  (userStats.fuelOverflowStatus & FuelOverflowStatus.Exists) ===
153
154
  FuelOverflowStatus.Exists;
154
155
 
155
- const hasFeeUpdate =
156
- (vaultAccount.feeUpdateStatus & FeeUpdateStatus.PendingFeeUpdate) ===
157
- FeeUpdateStatus.PendingFeeUpdate;
156
+ const hasFeeUpdate = hasPendingFeeUpdate(vaultAccount.feeUpdateStatus);
158
157
 
159
158
  if (hasFeeUpdate && !skipFeeUpdate) {
160
159
  const feeUpdate = getFeeUpdateAddressSync(
@@ -382,6 +381,7 @@ export class VaultClient {
382
381
  address?: PublicKey;
383
382
  vault?: Vault;
384
383
  factorUnrealizedPNL?: boolean;
384
+ includeManagerBorrowedValue?: boolean;
385
385
  }): Promise<BN> {
386
386
  try {
387
387
  // defaults to true if undefined
@@ -390,6 +390,11 @@ export class VaultClient {
390
390
  factorUnrealizedPNL = params.factorUnrealizedPNL;
391
391
  }
392
392
 
393
+ let includeManagerBorrowedValue = true;
394
+ if (params.includeManagerBorrowedValue !== undefined) {
395
+ includeManagerBorrowedValue = params.includeManagerBorrowedValue;
396
+ }
397
+
393
398
  let vaultAccount: Vault;
394
399
  if (params.address !== undefined) {
395
400
  // @ts-ignore
@@ -402,14 +407,18 @@ export class VaultClient {
402
407
 
403
408
  const user = await this.getSubscribedVaultUser(vaultAccount.user);
404
409
 
405
- const netSpotValue = user.getNetSpotMarketValue();
410
+ let netSpotValue = user.getNetSpotMarketValue();
406
411
 
407
412
  if (factorUnrealizedPNL) {
408
413
  const unrealizedPnl = user.getUnrealizedPNL(true, undefined, undefined);
409
- return netSpotValue.add(unrealizedPnl);
410
- } else {
411
- return netSpotValue;
414
+ netSpotValue = netSpotValue.add(unrealizedPnl);
415
+ }
416
+
417
+ if (includeManagerBorrowedValue) {
418
+ netSpotValue = netSpotValue.add(vaultAccount.managerBorrowedValue);
412
419
  }
420
+
421
+ return netSpotValue;
413
422
  } catch (err) {
414
423
  console.error('VaultClient ~ err:', err);
415
424
  return ZERO;
@@ -711,24 +720,31 @@ export class VaultClient {
711
720
  delegate: PublicKey,
712
721
  uiTxParams?: TxParams
713
722
  ): Promise<TransactionSignature> {
714
- const updateDelegateIx = await this.getUpdateDelegateIx(vault, delegate);
723
+ const vaultAccount = await this.program.account.vault.fetch(vault);
724
+ const updateDelegateIx = await this.getUpdateDelegateIx(
725
+ vault,
726
+ delegate,
727
+ vaultAccount.user,
728
+ vaultAccount.manager
729
+ );
715
730
  return await this.createAndSendTxn([updateDelegateIx], uiTxParams);
716
731
  }
717
732
 
718
733
  public async getUpdateDelegateIx(
719
734
  vault: PublicKey,
720
- delegate: PublicKey
735
+ delegate: PublicKey,
736
+ vaultDriftUser: PublicKey,
737
+ vaultManager: PublicKey
721
738
  ): Promise<TransactionInstruction> {
722
- const vaultAccount = await this.program.account.vault.fetch(vault);
723
739
  const accounts = {
724
740
  vault: vault,
725
- driftUser: vaultAccount.user,
741
+ driftUser: vaultDriftUser,
726
742
  driftProgram: this.driftClient.program.programId,
727
743
  };
728
744
 
729
745
  return await this.program.methods
730
746
  .updateDelegate(delegate)
731
- .accounts({ ...accounts, manager: vaultAccount.manager })
747
+ .accounts({ ...accounts, manager: vaultManager })
732
748
  .instruction();
733
749
  }
734
750
 
@@ -1172,6 +1188,315 @@ export class VaultClient {
1172
1188
  });
1173
1189
  }
1174
1190
 
1191
+ public async managerBorrow(
1192
+ vault: PublicKey,
1193
+ borrowSpotMarketIndex: number,
1194
+ borrowAmount: BN,
1195
+ managerTokenAccount?: PublicKey,
1196
+ txParams?: TxParams
1197
+ ): Promise<TransactionSignature> {
1198
+ const ixs = await this.getManagerBorrowIx(
1199
+ vault,
1200
+ borrowSpotMarketIndex,
1201
+ borrowAmount,
1202
+ managerTokenAccount
1203
+ );
1204
+ return await this.createAndSendTxn(ixs, txParams);
1205
+ }
1206
+
1207
+ public async getManagerBorrowIx(
1208
+ vault: PublicKey,
1209
+ borrowSpotMarketIndex: number,
1210
+ borrowAmount: BN,
1211
+ managerTokenAccount?: PublicKey
1212
+ ): Promise<TransactionInstruction[]> {
1213
+ const vaultAccount = await this.program.account.vault.fetch(vault);
1214
+
1215
+ const spotMarket = this.driftClient.getSpotMarketAccount(
1216
+ borrowSpotMarketIndex
1217
+ );
1218
+ if (!spotMarket) {
1219
+ throw new Error(
1220
+ `Spot market ${borrowSpotMarketIndex} not found on driftClient`
1221
+ );
1222
+ }
1223
+
1224
+ if (!managerTokenAccount) {
1225
+ managerTokenAccount = getAssociatedTokenAddressSync(
1226
+ spotMarket.mint,
1227
+ this.driftClient.wallet.publicKey,
1228
+ true
1229
+ );
1230
+ }
1231
+
1232
+ const user = await this.getSubscribedVaultUser(vaultAccount.user);
1233
+ const userStatsKey = getUserStatsAccountPublicKey(
1234
+ this.driftClient.program.programId,
1235
+ vault
1236
+ );
1237
+ const userStats = (await this.driftClient.program.account.userStats.fetch(
1238
+ userStatsKey
1239
+ )) as UserStatsAccount;
1240
+ const remainingAccounts = this.getRemainingAccountsForUser(
1241
+ [user.getUserAccount()],
1242
+ [borrowSpotMarketIndex, vaultAccount.spotMarketIndex],
1243
+ vaultAccount,
1244
+ userStats,
1245
+ false,
1246
+ false,
1247
+ false
1248
+ );
1249
+
1250
+ const preIxs = [];
1251
+ const postIxs = [];
1252
+
1253
+ const managerTokenAccountExists =
1254
+ await this.driftClient.connection.getAccountInfo(managerTokenAccount);
1255
+ if (managerTokenAccountExists === null) {
1256
+ preIxs.push(
1257
+ createAssociatedTokenAccountInstruction(
1258
+ vaultAccount.manager,
1259
+ managerTokenAccount,
1260
+ vaultAccount.manager,
1261
+ spotMarket.mint
1262
+ )
1263
+ );
1264
+ }
1265
+
1266
+ const vaultBorrowTokenAccount = getAssociatedTokenAddressSync(
1267
+ spotMarket.mint,
1268
+ vault,
1269
+ true
1270
+ );
1271
+ const vaultBorrowTokenAccountExists =
1272
+ await this.driftClient.connection.getAccountInfo(vaultBorrowTokenAccount);
1273
+ if (vaultBorrowTokenAccountExists === null) {
1274
+ preIxs.push(
1275
+ createAssociatedTokenAccountInstruction(
1276
+ this.driftClient.wallet.publicKey,
1277
+ vaultBorrowTokenAccount,
1278
+ vault,
1279
+ spotMarket.mint
1280
+ )
1281
+ );
1282
+ }
1283
+
1284
+ if (spotMarket.mint.equals(WRAPPED_SOL_MINT)) {
1285
+ postIxs.push(
1286
+ createCloseAccountInstruction(
1287
+ managerTokenAccount,
1288
+ vaultAccount.manager,
1289
+ vaultAccount.manager,
1290
+ []
1291
+ )
1292
+ );
1293
+ }
1294
+
1295
+ return [
1296
+ ...preIxs,
1297
+ await this.program.methods
1298
+ .managerBorrow(borrowSpotMarketIndex, borrowAmount)
1299
+ .accounts({
1300
+ vault,
1301
+ vaultTokenAccount: vaultBorrowTokenAccount,
1302
+ manager: vaultAccount.manager,
1303
+ driftUserStats: userStatsKey,
1304
+ driftUser: vaultAccount.user,
1305
+ driftState: await this.driftClient.getStatePublicKey(),
1306
+ driftSpotMarketVault: spotMarket.vault,
1307
+ driftSigner: this.driftClient.getStateAccount().signer,
1308
+ userTokenAccount: managerTokenAccount,
1309
+ driftProgram: this.driftClient.program.programId,
1310
+ tokenProgram: TOKEN_PROGRAM_ID,
1311
+ })
1312
+ .remainingAccounts(remainingAccounts)
1313
+ .instruction(),
1314
+ ...postIxs,
1315
+ ];
1316
+ }
1317
+
1318
+ public async managerRepay(
1319
+ vault: PublicKey,
1320
+ repaySpotMarketIndex: number,
1321
+ repayAmount: BN,
1322
+ repayValue: BN | null,
1323
+ managerTokenAccount?: PublicKey,
1324
+ uiTxParams?: TxParams
1325
+ ): Promise<TransactionSignature> {
1326
+ const ixs = await this.getManagerRepayIxs(
1327
+ vault,
1328
+ repaySpotMarketIndex,
1329
+ repayAmount,
1330
+ repayValue,
1331
+ managerTokenAccount
1332
+ );
1333
+ return this.createAndSendTxn(ixs, uiTxParams);
1334
+ }
1335
+
1336
+ /**
1337
+ * Get the instructions for the manager repay transaction
1338
+ * @param vault - The vault to repay
1339
+ * @param repaySpotMarketIndex - The spot market index to repay
1340
+ * @param repayAmount - The amount to repay
1341
+ * @param repayValue - The value of the repay
1342
+ * @param managerTokenAccount - The manager token account to use, if depositing SOL, leave undefined to automatically wrap the SOL
1343
+ * @returns The instructions for the manager repay transaction
1344
+ */
1345
+ public async getManagerRepayIxs(
1346
+ vault: PublicKey,
1347
+ repaySpotMarketIndex: number,
1348
+ repayAmount: BN,
1349
+ repayValue: BN | null,
1350
+ managerTokenAccount?: PublicKey
1351
+ ): Promise<TransactionInstruction[]> {
1352
+ const vaultAccount = await this.program.account.vault.fetch(vault);
1353
+ const spotMarket =
1354
+ this.driftClient.getSpotMarketAccount(repaySpotMarketIndex);
1355
+ if (!spotMarket) {
1356
+ throw new Error(
1357
+ `Spot market ${repaySpotMarketIndex} not found on driftClient`
1358
+ );
1359
+ }
1360
+ const isSolMarket = spotMarket.mint.equals(WRAPPED_SOL_MINT);
1361
+
1362
+ const preIxs: TransactionInstruction[] = [];
1363
+ const postIxs: TransactionInstruction[] = [];
1364
+ let createdWsolAccount = false;
1365
+
1366
+ if (!managerTokenAccount) {
1367
+ if (isSolMarket) {
1368
+ // create wSOL
1369
+ const { ixs, pubkey } =
1370
+ await this.driftClient.getWrappedSolAccountCreationIxs(
1371
+ repayAmount,
1372
+ true
1373
+ );
1374
+ preIxs.push(...ixs);
1375
+ managerTokenAccount = pubkey;
1376
+ createdWsolAccount = true;
1377
+ } else {
1378
+ managerTokenAccount = getAssociatedTokenAddressSync(
1379
+ spotMarket.mint,
1380
+ vaultAccount.manager,
1381
+ true
1382
+ );
1383
+ }
1384
+ }
1385
+
1386
+ const vaultRepayTokenAccount = getAssociatedTokenAddressSync(
1387
+ spotMarket.mint,
1388
+ vault,
1389
+ true
1390
+ );
1391
+ const vaultRepayTokenAccountExists =
1392
+ await this.driftClient.connection.getAccountInfo(vaultRepayTokenAccount);
1393
+ if (vaultRepayTokenAccountExists === null) {
1394
+ preIxs.push(
1395
+ createAssociatedTokenAccountInstruction(
1396
+ this.driftClient.wallet.publicKey,
1397
+ vaultRepayTokenAccount,
1398
+ vault,
1399
+ spotMarket.mint
1400
+ )
1401
+ );
1402
+ }
1403
+
1404
+ if (createdWsolAccount) {
1405
+ postIxs.push(
1406
+ createCloseAccountInstruction(
1407
+ managerTokenAccount,
1408
+ vaultAccount.manager,
1409
+ vaultAccount.manager,
1410
+ []
1411
+ )
1412
+ );
1413
+ }
1414
+
1415
+ const userStatsKey = getUserStatsAccountPublicKey(
1416
+ this.driftClient.program.programId,
1417
+ vault
1418
+ );
1419
+ const user = await this.getSubscribedVaultUser(vaultAccount.user);
1420
+ const userStats = (await this.driftClient.program.account.userStats.fetch(
1421
+ userStatsKey
1422
+ )) as UserStatsAccount;
1423
+ const remainingAccounts = this.getRemainingAccountsForUser(
1424
+ [user.getUserAccount()],
1425
+ [repaySpotMarketIndex, vaultAccount.spotMarketIndex],
1426
+ vaultAccount,
1427
+ userStats,
1428
+ false,
1429
+ false,
1430
+ false
1431
+ );
1432
+
1433
+ return [
1434
+ ...preIxs,
1435
+ await this.program.methods
1436
+ .managerRepay(repaySpotMarketIndex, repayAmount, repayValue)
1437
+ .accounts({
1438
+ vault,
1439
+ vaultTokenAccount: vaultRepayTokenAccount,
1440
+ manager: vaultAccount.manager,
1441
+ driftUserStats: userStatsKey,
1442
+ driftUser: vaultAccount.user,
1443
+ driftState: await this.driftClient.getStatePublicKey(),
1444
+ driftSpotMarketVault: spotMarket.vault,
1445
+ driftSigner: this.driftClient.getStateAccount().signer,
1446
+ userTokenAccount: managerTokenAccount,
1447
+ driftProgram: this.driftClient.program.programId,
1448
+ tokenProgram: TOKEN_PROGRAM_ID,
1449
+ })
1450
+ .remainingAccounts(remainingAccounts)
1451
+ .instruction(),
1452
+ ...postIxs,
1453
+ ];
1454
+ }
1455
+
1456
+ public async managerUpdateBorrow(
1457
+ vault: PublicKey,
1458
+ newBorrowValue: BN,
1459
+ txParams?: TxParams
1460
+ ): Promise<TransactionSignature> {
1461
+ const ix = await this.getManagerUpdateBorrowIx(vault, newBorrowValue);
1462
+ return await this.createAndSendTxn([ix], txParams);
1463
+ }
1464
+
1465
+ public async getManagerUpdateBorrowIx(
1466
+ vault: PublicKey,
1467
+ newBorrowValue: BN
1468
+ ): Promise<TransactionInstruction> {
1469
+ const vaultAccount = await this.program.account.vault.fetch(vault);
1470
+
1471
+ const user = await this.getSubscribedVaultUser(vaultAccount.user);
1472
+ const userStatsKey = getUserStatsAccountPublicKey(
1473
+ this.driftClient.program.programId,
1474
+ vault
1475
+ );
1476
+ const userStats = (await this.driftClient.program.account.userStats.fetch(
1477
+ userStatsKey
1478
+ )) as UserStatsAccount;
1479
+ const remainingAccounts = this.getRemainingAccountsForUser(
1480
+ [user.getUserAccount()],
1481
+ [],
1482
+ vaultAccount,
1483
+ userStats,
1484
+ false,
1485
+ false,
1486
+ false
1487
+ );
1488
+
1489
+ return this.program.instruction.managerUpdateBorrow(newBorrowValue, {
1490
+ accounts: {
1491
+ vault,
1492
+ manager: vaultAccount.manager,
1493
+ driftUserStats: userStatsKey,
1494
+ driftUser: vaultAccount.user,
1495
+ },
1496
+ remainingAccounts,
1497
+ });
1498
+ }
1499
+
1175
1500
  public async managerUpdateVault(
1176
1501
  vault: PublicKey,
1177
1502
  params: {
@@ -2381,7 +2706,10 @@ export class VaultClient {
2381
2706
  units: txParams?.cuLimit ?? 400_000,
2382
2707
  }),
2383
2708
  ComputeBudgetProgram.setComputeUnitPrice({
2384
- microLamports: txParams?.cuPriceMicroLamports ?? 50_000,
2709
+ microLamports:
2710
+ txParams?.cuPriceMicroLamports === undefined
2711
+ ? 50_000
2712
+ : txParams.cuPriceMicroLamports,
2385
2713
  }),
2386
2714
  ...vaultIxs,
2387
2715
  ];
@@ -2407,7 +2735,10 @@ export class VaultClient {
2407
2735
  units: txParams?.cuLimit ?? 400_000,
2408
2736
  }),
2409
2737
  ComputeBudgetProgram.setComputeUnitPrice({
2410
- microLamports: txParams?.cuPriceMicroLamports ?? 50_000,
2738
+ microLamports:
2739
+ txParams?.cuPriceMicroLamports === undefined
2740
+ ? 50_000
2741
+ : txParams.cuPriceMicroLamports,
2411
2742
  }),
2412
2743
  ...vaultIxs,
2413
2744
  ];
@@ -3426,6 +3757,28 @@ export class VaultClient {
3426
3757
  });
3427
3758
  }
3428
3759
 
3760
+ public async adminUpdateVaultClass(
3761
+ vault: PublicKey,
3762
+ newVaultClass: VaultClass,
3763
+ uiTxParams?: TxParams
3764
+ ): Promise<TransactionSignature> {
3765
+ const ix = await this.getAdminUpdateVaultClassIx(vault, newVaultClass);
3766
+ return await this.createAndSendTxn([ix], uiTxParams);
3767
+ }
3768
+
3769
+ public async getAdminUpdateVaultClassIx(
3770
+ vault: PublicKey,
3771
+ newVaultClass: VaultClass
3772
+ ): Promise<TransactionInstruction> {
3773
+ return this.program.methods
3774
+ .adminUpdateVaultClass(newVaultClass as any)
3775
+ .accounts({
3776
+ vault,
3777
+ admin: this.driftClient.wallet.publicKey,
3778
+ })
3779
+ .instruction();
3780
+ }
3781
+
3429
3782
  public async managerUpdateFees(
3430
3783
  vault: PublicKey,
3431
3784
  params: {