@drift-labs/vaults-sdk 0.1.0

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 (78) hide show
  1. package/.env.example +2 -0
  2. package/README.md +58 -0
  3. package/cli/cli.ts +105 -0
  4. package/cli/commands/applyProfitShare.ts +48 -0
  5. package/cli/commands/deposit.ts +35 -0
  6. package/cli/commands/index.ts +13 -0
  7. package/cli/commands/initVault.ts +61 -0
  8. package/cli/commands/initVaultDepositor.ts +43 -0
  9. package/cli/commands/listDepositorsForVault.ts +33 -0
  10. package/cli/commands/managerCancelWithdraw.ts +25 -0
  11. package/cli/commands/managerDeposit.ts +34 -0
  12. package/cli/commands/managerRequestWithdraw.ts +27 -0
  13. package/cli/commands/managerUpdateVault.ts +36 -0
  14. package/cli/commands/managerWithdraw.ts +25 -0
  15. package/cli/commands/requestWithdraw.ts +29 -0
  16. package/cli/commands/vaultDeposit.ts +43 -0
  17. package/cli/commands/vaultWithdraw.ts +43 -0
  18. package/cli/commands/viewVault.ts +30 -0
  19. package/cli/commands/viewVaultDepositor.ts +37 -0
  20. package/cli/commands/withdraw.ts +25 -0
  21. package/cli/utils.ts +119 -0
  22. package/lib/accountSubscribers/index.d.ts +2 -0
  23. package/lib/accountSubscribers/index.js +14 -0
  24. package/lib/accountSubscribers/pollingVaultDepositorSubscriber.d.ts +7 -0
  25. package/lib/accountSubscribers/pollingVaultDepositorSubscriber.js +44 -0
  26. package/lib/accountSubscribers/pollingVaultSubscriber.d.ts +7 -0
  27. package/lib/accountSubscribers/pollingVaultSubscriber.js +44 -0
  28. package/lib/accountSubscribers/pollingVaultsProgramAccountSubscriber.d.ts +28 -0
  29. package/lib/accountSubscribers/pollingVaultsProgramAccountSubscriber.js +68 -0
  30. package/lib/accounts/index.d.ts +2 -0
  31. package/lib/accounts/index.js +14 -0
  32. package/lib/accounts/vaultAccount.d.ts +15 -0
  33. package/lib/accounts/vaultAccount.js +53 -0
  34. package/lib/accounts/vaultDepositorAccount.d.ts +18 -0
  35. package/lib/accounts/vaultDepositorAccount.js +45 -0
  36. package/lib/accounts/vaultsProgramAccount.d.ts +13 -0
  37. package/lib/accounts/vaultsProgramAccount.js +25 -0
  38. package/lib/addresses.d.ts +4 -0
  39. package/lib/addresses.js +46 -0
  40. package/lib/constants/index.d.ts +3 -0
  41. package/lib/constants/index.js +5 -0
  42. package/lib/index.d.ts +9 -0
  43. package/lib/index.js +21 -0
  44. package/lib/name.d.ts +3 -0
  45. package/lib/name.js +19 -0
  46. package/lib/parsers/index.d.ts +1 -0
  47. package/lib/parsers/index.js +13 -0
  48. package/lib/parsers/logParser.d.ts +14 -0
  49. package/lib/parsers/logParser.js +21 -0
  50. package/lib/types/drift_vaults.d.ts +1475 -0
  51. package/lib/types/drift_vaults.js +1477 -0
  52. package/lib/types/types.d.ts +137 -0
  53. package/lib/types/types.js +20 -0
  54. package/lib/utils.d.ts +7 -0
  55. package/lib/utils.js +43 -0
  56. package/lib/vaultClient.d.ts +100 -0
  57. package/lib/vaultClient.js +596 -0
  58. package/package.json +29 -0
  59. package/src/accountSubscribers/index.ts +2 -0
  60. package/src/accountSubscribers/pollingVaultDepositorSubscriber.ts +68 -0
  61. package/src/accountSubscribers/pollingVaultSubscriber.ts +62 -0
  62. package/src/accountSubscribers/pollingVaultsProgramAccountSubscriber.ts +111 -0
  63. package/src/accounts/index.ts +2 -0
  64. package/src/accounts/vaultAccount.ts +86 -0
  65. package/src/accounts/vaultDepositorAccount.ts +67 -0
  66. package/src/accounts/vaultsProgramAccount.ts +37 -0
  67. package/src/addresses.ts +43 -0
  68. package/src/constants/index.ts +3 -0
  69. package/src/idl/drift_vaults.json +1547 -0
  70. package/src/index.ts +9 -0
  71. package/src/name.ts +18 -0
  72. package/src/parsers/index.ts +1 -0
  73. package/src/parsers/logParser.ts +28 -0
  74. package/src/types/drift_vaults.ts +2949 -0
  75. package/src/types/types.ts +158 -0
  76. package/src/utils.ts +33 -0
  77. package/src/vaultClient.ts +904 -0
  78. package/tsconfig.json +13 -0
@@ -0,0 +1,62 @@
1
+ import {
2
+ Vault,
3
+ VaultAccountEvents,
4
+ VaultAccountSubscriber,
5
+ } from '../types/types';
6
+ import { PollingVaultsProgramAccountSubscriber } from './pollingVaultsProgramAccountSubscriber';
7
+
8
+ export class PollingVaultSubscriber
9
+ extends PollingVaultsProgramAccountSubscriber<Vault, VaultAccountEvents>
10
+ implements VaultAccountSubscriber
11
+ {
12
+ async addToAccountLoader(): Promise<void> {
13
+ if (this.callbackId) {
14
+ console.log('Account for vault already added to account loader');
15
+ return;
16
+ }
17
+
18
+ this.callbackId = await this.accountLoader.addAccount(
19
+ this.pubkey,
20
+ (buffer, slot) => {
21
+ if (!buffer) return;
22
+
23
+ if (this.account && this.account.slot > slot) {
24
+ return;
25
+ }
26
+
27
+ const account = this.program.account.vault.coder.accounts.decode(
28
+ 'vault',
29
+ buffer
30
+ );
31
+ this.account = { data: account, slot };
32
+ this._eventEmitter.emit('vaultUpdate', account);
33
+ this._eventEmitter.emit('update');
34
+ }
35
+ );
36
+
37
+ this.errorCallbackId = this.accountLoader.addErrorCallbacks((error) => {
38
+ this._eventEmitter.emit('error', error);
39
+ });
40
+ }
41
+
42
+ async fetch(): Promise<void> {
43
+ await this.accountLoader.load();
44
+ const { buffer, slot } = this.accountLoader.getBufferAndSlot(this.pubkey);
45
+ const currentSlot = this.account?.slot ?? 0;
46
+ if (buffer && slot > currentSlot) {
47
+ const account = this.program.account.vault.coder.accounts.decode(
48
+ 'vault',
49
+ buffer
50
+ );
51
+ this.account = { data: account, slot };
52
+ }
53
+ }
54
+
55
+ updateData(vaultAcc: Vault, slot: number): void {
56
+ if (!this.account || this.account.slot < slot) {
57
+ this.account = { data: vaultAcc, slot };
58
+ this._eventEmitter.emit('vaultUpdate', vaultAcc);
59
+ this._eventEmitter.emit('update');
60
+ }
61
+ }
62
+ }
@@ -0,0 +1,111 @@
1
+ import {
2
+ BulkAccountLoader,
3
+ DataAndSlot,
4
+ NotSubscribedError,
5
+ PublicKey,
6
+ } from '@drift-labs/sdk';
7
+ import { Program } from '@coral-xyz/anchor';
8
+ import StrictEventEmitter from 'strict-event-emitter-types';
9
+ import { EventEmitter } from 'events';
10
+ import { DriftVaults } from '../types/drift_vaults';
11
+ import {
12
+ VaultsProgramAccountBaseEvents,
13
+ VaultsProgramAccountSubscriber,
14
+ } from '../types/types';
15
+
16
+ export abstract class PollingVaultsProgramAccountSubscriber<
17
+ Account,
18
+ AccountEvents extends VaultsProgramAccountBaseEvents
19
+ > implements VaultsProgramAccountSubscriber<Account, AccountEvents>
20
+ {
21
+ protected program: Program<DriftVaults>;
22
+ protected _isSubscribed: boolean;
23
+ protected pubkey: PublicKey;
24
+ protected account?: DataAndSlot<Account>;
25
+
26
+ protected _eventEmitter: StrictEventEmitter<EventEmitter, AccountEvents>;
27
+ protected accountLoader: BulkAccountLoader;
28
+ protected callbackId: string | null = null;
29
+ protected errorCallbackId: string | null = null;
30
+
31
+ constructor(
32
+ program: Program<DriftVaults>,
33
+ accountPubkey: PublicKey,
34
+ accountLoader: BulkAccountLoader
35
+ ) {
36
+ this.accountLoader = accountLoader;
37
+ this._isSubscribed = false;
38
+ this.pubkey = accountPubkey;
39
+ this.program = program;
40
+ // @ts-ignore
41
+ this._eventEmitter = new EventEmitter();
42
+ }
43
+
44
+ get isSubscribed(): boolean {
45
+ return this._isSubscribed;
46
+ }
47
+
48
+ get eventEmitter(): StrictEventEmitter<EventEmitter, AccountEvents> {
49
+ return this._eventEmitter;
50
+ }
51
+
52
+ async subscribe(): Promise<boolean> {
53
+ if (this._isSubscribed) {
54
+ return true;
55
+ }
56
+
57
+ try {
58
+ await this.addToAccountLoader();
59
+
60
+ await this.fetchIfUnloaded();
61
+ if (this.account) {
62
+ // @ts-ignore
63
+ this._eventEmitter.emit('update');
64
+ }
65
+
66
+ this._isSubscribed = true;
67
+ return true;
68
+ } catch (err) {
69
+ console.error(err);
70
+ this._isSubscribed = false;
71
+ return false;
72
+ }
73
+ }
74
+
75
+ async unsubscribe(): Promise<void> {
76
+ if (!this._isSubscribed) {
77
+ return;
78
+ }
79
+
80
+ this.accountLoader.removeAccount(this.pubkey, this.callbackId);
81
+ this.callbackId = undefined;
82
+
83
+ this.accountLoader.removeErrorCallbacks(this.errorCallbackId);
84
+ this.errorCallbackId = undefined;
85
+
86
+ this._isSubscribed = false;
87
+ }
88
+
89
+ async fetchIfUnloaded(): Promise<void> {
90
+ if (this.account === undefined) {
91
+ await this.fetch();
92
+ }
93
+ }
94
+
95
+ assertIsSubscribed(): void {
96
+ if (!this._isSubscribed) {
97
+ throw new NotSubscribedError(
98
+ 'You must call `subscribe` before using this function'
99
+ );
100
+ }
101
+ }
102
+
103
+ getAccountAndSlot(): DataAndSlot<Account> {
104
+ this.assertIsSubscribed();
105
+ return this.account;
106
+ }
107
+
108
+ abstract addToAccountLoader(): Promise<void>;
109
+ abstract fetch(): Promise<void>;
110
+ abstract updateData(account: Account, slot: number): void;
111
+ }
@@ -0,0 +1,2 @@
1
+ export * from './vaultDepositorAccount';
2
+ export * from './vaultAccount';
@@ -0,0 +1,86 @@
1
+ import { BN, Program } from '@coral-xyz/anchor';
2
+ import {
3
+ BulkAccountLoader,
4
+ ONE,
5
+ ONE_YEAR,
6
+ PERCENTAGE_PRECISION,
7
+ ZERO,
8
+ } from '@drift-labs/sdk';
9
+ import { PublicKey } from '@solana/web3.js';
10
+ import { DriftVaults } from '../types/drift_vaults';
11
+ import { Vault, VaultAccountEvents } from '../types/types';
12
+ import { PollingVaultSubscriber } from '../accountSubscribers';
13
+ import { VaultsProgramAccount } from './vaultsProgramAccount';
14
+ import { getVaultAddressSync } from '../addresses';
15
+ import { encodeName } from '../name';
16
+
17
+ export class VaultAccount extends VaultsProgramAccount<
18
+ Vault,
19
+ VaultAccountEvents
20
+ > {
21
+ constructor(
22
+ program: Program<DriftVaults>,
23
+ vaultPubkey: PublicKey,
24
+ accountLoader: BulkAccountLoader,
25
+ accountSubscriptionType: 'polling' | 'websocket' = 'polling'
26
+ ) {
27
+ super();
28
+
29
+ if (accountSubscriptionType === 'polling') {
30
+ this.accountSubscriber = new PollingVaultSubscriber(
31
+ program,
32
+ vaultPubkey,
33
+ accountLoader
34
+ );
35
+ } else {
36
+ throw new Error('Websocket subscription not yet implemented');
37
+ }
38
+ }
39
+
40
+ static getAddressSync(programId: PublicKey, vaultName: string): PublicKey {
41
+ return getVaultAddressSync(programId, encodeName(vaultName));
42
+ }
43
+
44
+ calcSharesAfterManagementFee(vaultEquity: BN): {
45
+ totalShares: BN;
46
+ managementFeeShares: BN;
47
+ } {
48
+ const accountData = this.accountSubscriber.getAccountAndSlot().data;
49
+
50
+ const depositorsEquity = accountData.userShares
51
+ .mul(vaultEquity)
52
+ .div(accountData.totalShares);
53
+
54
+ if (accountData.managementFee.eq(ZERO) || depositorsEquity.lte(ZERO)) {
55
+ return {
56
+ totalShares: accountData.totalShares,
57
+ managementFeeShares: ZERO,
58
+ };
59
+ }
60
+
61
+ const now = new BN(Date.now() / 1000);
62
+ const sinceLast = now.sub(accountData.lastFeeUpdateTs);
63
+
64
+ let managementFeeAmount = depositorsEquity
65
+ .mul(accountData.managementFee)
66
+ .div(PERCENTAGE_PRECISION)
67
+ .mul(sinceLast)
68
+ .div(ONE_YEAR);
69
+ managementFeeAmount = BN.min(
70
+ managementFeeAmount,
71
+ depositorsEquity.sub(ONE)
72
+ );
73
+
74
+ const newTotalSharesFactor = depositorsEquity
75
+ .mul(PERCENTAGE_PRECISION)
76
+ .div(depositorsEquity.sub(managementFeeAmount));
77
+ let newTotalShares = accountData.totalShares
78
+ .mul(newTotalSharesFactor)
79
+ .div(PERCENTAGE_PRECISION);
80
+ newTotalShares = BN.max(newTotalShares, accountData.userShares);
81
+
82
+ const managementFeeShares = newTotalShares.sub(accountData.totalShares);
83
+
84
+ return { totalShares: newTotalShares, managementFeeShares };
85
+ }
86
+ }
@@ -0,0 +1,67 @@
1
+ import { BN, Program } from '@coral-xyz/anchor';
2
+ import { BulkAccountLoader, PERCENTAGE_PRECISION, ZERO } from '@drift-labs/sdk';
3
+ import { PublicKey } from '@solana/web3.js';
4
+ import { DriftVaults } from '../types/drift_vaults';
5
+ import { VaultDepositor, VaultDepositorAccountEvents } from '../types/types';
6
+ import { PollingVaultDepositorSubscriber } from '../accountSubscribers';
7
+ import { VaultsProgramAccount } from './vaultsProgramAccount';
8
+ import { getVaultDepositorAddressSync } from '../addresses';
9
+
10
+ export class VaultDepositorAccount extends VaultsProgramAccount<
11
+ VaultDepositor,
12
+ VaultDepositorAccountEvents
13
+ > {
14
+ constructor(
15
+ program: Program<DriftVaults>,
16
+ vaultDepositorPubkey: PublicKey,
17
+ accountLoader: BulkAccountLoader,
18
+ accountSubscriptionType: 'polling' | 'websocket' = 'polling'
19
+ ) {
20
+ super();
21
+
22
+ if (accountSubscriptionType === 'polling') {
23
+ this.accountSubscriber = new PollingVaultDepositorSubscriber(
24
+ program,
25
+ vaultDepositorPubkey,
26
+ accountLoader
27
+ );
28
+ } else {
29
+ throw new Error('Websocket subscription not yet implemented');
30
+ }
31
+ }
32
+
33
+ static getAddressSync(
34
+ programId: PublicKey,
35
+ vault: PublicKey,
36
+ authority: PublicKey
37
+ ): PublicKey {
38
+ return getVaultDepositorAddressSync(programId, vault, authority);
39
+ }
40
+
41
+ /**
42
+ * Calculates the percentage of a depositor's equity that will be paid as profit share fees.
43
+ *
44
+ * @param vaultProfitShare Vault's profit share fee
45
+ * @param depositorEquity Vault depositor's equity amount
46
+ */
47
+ calcProfitShareFeesPct(vaultProfitShare: BN, depositorEquity: BN): BN {
48
+ const accountData = this.accountSubscriber.getAccountAndSlot().data;
49
+
50
+ const profit = depositorEquity
51
+ .sub(accountData.netDeposits)
52
+ .sub(accountData.cumulativeProfitShareAmount);
53
+
54
+ if (profit.lte(new BN(0))) {
55
+ return ZERO;
56
+ }
57
+
58
+ const profitShareAmount = profit
59
+ .mul(vaultProfitShare)
60
+ .div(PERCENTAGE_PRECISION);
61
+ const profitShareProportion = profitShareAmount
62
+ .mul(PERCENTAGE_PRECISION)
63
+ .div(depositorEquity);
64
+
65
+ return profitShareProportion;
66
+ }
67
+ }
@@ -0,0 +1,37 @@
1
+ import EventEmitter from 'events';
2
+ import StrictEventEmitter from 'strict-event-emitter-types';
3
+ import {
4
+ VaultsProgramAccountBaseEvents,
5
+ VaultsProgramAccountSubscriber,
6
+ } from '../types/types';
7
+
8
+ export abstract class VaultsProgramAccount<
9
+ Account,
10
+ AccountEvents extends VaultsProgramAccountBaseEvents
11
+ > {
12
+ accountSubscriber: VaultsProgramAccountSubscriber<Account, AccountEvents>;
13
+
14
+ get isSubscribed(): boolean {
15
+ return this.accountSubscriber.isSubscribed;
16
+ }
17
+
18
+ get eventEmitter(): StrictEventEmitter<EventEmitter, AccountEvents> {
19
+ return this.accountSubscriber.eventEmitter;
20
+ }
21
+
22
+ async subscribe(): Promise<boolean> {
23
+ return await this.accountSubscriber.subscribe();
24
+ }
25
+
26
+ async unsubscribe(): Promise<void> {
27
+ return await this.accountSubscriber.unsubscribe();
28
+ }
29
+
30
+ getData(): Account {
31
+ return this.accountSubscriber.getAccountAndSlot()?.data;
32
+ }
33
+
34
+ async updateData(newData: Account, slot: number): Promise<void> {
35
+ return await this.accountSubscriber.updateData(newData, slot);
36
+ }
37
+ }
@@ -0,0 +1,43 @@
1
+ import { PublicKey } from '@solana/web3.js';
2
+ import * as anchor from '@coral-xyz/anchor';
3
+
4
+ export function getVaultAddressSync(
5
+ programId: PublicKey,
6
+ encodedName: number[]
7
+ ): PublicKey {
8
+ return PublicKey.findProgramAddressSync(
9
+ [
10
+ Buffer.from(anchor.utils.bytes.utf8.encode('vault')),
11
+ Buffer.from(encodedName),
12
+ ],
13
+ programId
14
+ )[0];
15
+ }
16
+
17
+ export function getVaultDepositorAddressSync(
18
+ programId: PublicKey,
19
+ vault: PublicKey,
20
+ authority: PublicKey
21
+ ): PublicKey {
22
+ return PublicKey.findProgramAddressSync(
23
+ [
24
+ Buffer.from(anchor.utils.bytes.utf8.encode('vault_depositor')),
25
+ vault.toBuffer(),
26
+ authority.toBuffer(),
27
+ ],
28
+ programId
29
+ )[0];
30
+ }
31
+
32
+ export function getTokenVaultAddressSync(
33
+ programId: PublicKey,
34
+ vault: PublicKey
35
+ ): PublicKey {
36
+ return PublicKey.findProgramAddressSync(
37
+ [
38
+ Buffer.from(anchor.utils.bytes.utf8.encode('vault_token_account')),
39
+ vault.toBuffer(),
40
+ ],
41
+ programId
42
+ )[0];
43
+ }
@@ -0,0 +1,3 @@
1
+ import { BN } from '@drift-labs/sdk';
2
+
3
+ export const VAULT_SHARES_PRECISION_EXP = new BN(6);