@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
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "@velocity-exchange/vaults-sdk",
3
+ "version": "0.0.1",
4
+ "main": "lib/index.js",
5
+ "types": "lib/index.d.ts",
6
+ "directories": {
7
+ "lib": "lib"
8
+ },
9
+ "dependencies": {
10
+ "@metaplex-foundation/js": "0.20.1",
11
+ "@velocity-exchange/sdk": "0.0.3",
12
+ "commander": "11.1.0",
13
+ "dotenv": "16.4.5",
14
+ "strict-event-emitter-types": "2.0.0",
15
+ "ts-node": "10.9.2",
16
+ "typescript": "5.6.3"
17
+ },
18
+ "peerDependencies": {
19
+ "@coral-xyz/anchor": "npm:@anchor-lang/core@1.0.1",
20
+ "@solana/web3.js": "1.98.0"
21
+ },
22
+ "optionalDependencies": {
23
+ "@ledgerhq/hw-app-solana": "7.2.4",
24
+ "@ledgerhq/hw-transport": "6.31.4",
25
+ "@ledgerhq/hw-transport-node-hid": "6.29.5",
26
+ "@solana/wallet-adapter-ledger": "0.9.25"
27
+ },
28
+ "devDependencies": {
29
+ "@coral-xyz/anchor": "npm:@anchor-lang/core@1.0.1",
30
+ "@types/bn.js": "5.1.6"
31
+ },
32
+ "engines": {
33
+ "node": "^24.0.0"
34
+ },
35
+ "scripts": {
36
+ "cli": "ts-node cli/cli.ts",
37
+ "clean": "rm -rf lib",
38
+ "build": "yarn clean && tsc"
39
+ },
40
+ "resolutions": {
41
+ "@types/bn.js": "5.1.6",
42
+ "rpc-websockets": "10.0.0",
43
+ "@solana/web3.js": "1.98.0",
44
+ "@drift-labs/sdk": "npm:@velocity-exchange/sdk@0.0.3",
45
+ "@velocity-exchange/sdk": "0.0.3",
46
+ "@coral-xyz/anchor": "npm:@anchor-lang/core@1.0.1"
47
+ }
48
+ }
@@ -0,0 +1,2 @@
1
+ export * from './pollingVaultDepositorSubscriber';
2
+ export * from './pollingVaultSubscriber';
@@ -0,0 +1,69 @@
1
+ import {
2
+ VaultDepositor,
3
+ VaultDepositorAccountEvents,
4
+ VaultDepositorAccountSubscriber,
5
+ } from '../types/types';
6
+ import { PollingVaultsProgramAccountSubscriber } from './pollingVaultsProgramAccountSubscriber';
7
+
8
+ export class PollingVaultDepositorSubscriber
9
+ extends PollingVaultsProgramAccountSubscriber<
10
+ VaultDepositor,
11
+ VaultDepositorAccountEvents
12
+ >
13
+ implements VaultDepositorAccountSubscriber
14
+ {
15
+ async addToAccountLoader(): Promise<void> {
16
+ if (this.callbackId) {
17
+ console.log(
18
+ 'Account for vault depositor already added to account loader'
19
+ );
20
+ return;
21
+ }
22
+
23
+ this.callbackId = await this.accountLoader.addAccount(
24
+ this.pubkey,
25
+ (buffer, slot) => {
26
+ if (!buffer) return;
27
+
28
+ if (this.account && this.account.slot > slot) {
29
+ return;
30
+ }
31
+
32
+ const account =
33
+ this.program.account.vaultDepositor.coder.accounts.decode(
34
+ 'vaultDepositor',
35
+ buffer
36
+ );
37
+ this.account = { data: account, slot };
38
+ this._eventEmitter.emit('vaultDepositorUpdate', account);
39
+ this._eventEmitter.emit('update');
40
+ }
41
+ );
42
+
43
+ this.errorCallbackId = this.accountLoader.addErrorCallbacks((error) => {
44
+ this._eventEmitter.emit('error', error);
45
+ });
46
+ }
47
+
48
+ async fetch(): Promise<void> {
49
+ await this.accountLoader.load();
50
+ const bufferAndSlot = this.accountLoader.getBufferAndSlot(this.pubkey);
51
+ if (!bufferAndSlot) return;
52
+ const currentSlot = this.account?.slot ?? 0;
53
+ if (bufferAndSlot.buffer && bufferAndSlot.slot > currentSlot) {
54
+ const account = this.program.account.vaultDepositor.coder.accounts.decode(
55
+ 'vaultDepositor',
56
+ bufferAndSlot.buffer
57
+ );
58
+ this.account = { data: account, slot: bufferAndSlot.slot };
59
+ }
60
+ }
61
+
62
+ updateData(vaultDepositorAcc: VaultDepositor, slot: number): void {
63
+ if (!this.account || this.account.slot < slot) {
64
+ this.account = { data: vaultDepositorAcc, slot };
65
+ this._eventEmitter.emit('vaultDepositorUpdate', vaultDepositorAcc);
66
+ this._eventEmitter.emit('update');
67
+ }
68
+ }
69
+ }
@@ -0,0 +1,63 @@
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 bufferAndSlot = this.accountLoader.getBufferAndSlot(this.pubkey);
45
+ if (!bufferAndSlot) return;
46
+ const currentSlot = this.account?.slot ?? 0;
47
+ if (bufferAndSlot.buffer && bufferAndSlot.slot > currentSlot) {
48
+ const account = this.program.account.vault.coder.accounts.decode(
49
+ 'vault',
50
+ bufferAndSlot.buffer
51
+ );
52
+ this.account = { data: account, slot: bufferAndSlot.slot };
53
+ }
54
+ }
55
+
56
+ updateData(vaultAcc: Vault, slot: number): void {
57
+ if (!this.account || this.account.slot < slot) {
58
+ this.account = { data: vaultAcc, slot };
59
+ this._eventEmitter.emit('vaultUpdate', vaultAcc);
60
+ this._eventEmitter.emit('update');
61
+ }
62
+ }
63
+ }
@@ -0,0 +1,114 @@
1
+ import {
2
+ BulkAccountLoader,
3
+ DataAndSlot,
4
+ NotSubscribedError,
5
+ PublicKey,
6
+ } from '@velocity-exchange/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 = null;
82
+
83
+ this.accountLoader.removeErrorCallbacks(this.errorCallbackId!);
84
+ this.errorCallbackId = null;
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
+ if (!this.account) {
106
+ throw new Error('Account not loaded');
107
+ }
108
+ return this.account;
109
+ }
110
+
111
+ abstract addToAccountLoader(): Promise<void>;
112
+ abstract fetch(): Promise<void>;
113
+ abstract updateData(account: Account, slot: number): void;
114
+ }
@@ -0,0 +1,2 @@
1
+ export * from './vaultDepositorAccount';
2
+ export * from './vaultAccount';
@@ -0,0 +1,255 @@
1
+ import { Program } from '@coral-xyz/anchor';
2
+ import {
3
+ BulkAccountLoader,
4
+ ONE,
5
+ ONE_YEAR,
6
+ PERCENTAGE_PRECISION,
7
+ ZERO,
8
+ BN,
9
+ } from '@velocity-exchange/sdk';
10
+ import { PublicKey } from '@solana/web3.js';
11
+ import { DriftVaults } from '../types/drift_vaults';
12
+ import { Vault, VaultAccountEvents, VaultProtocol } from '../types/types';
13
+ import { PollingVaultSubscriber } from '../accountSubscribers';
14
+ import { VaultsProgramAccount } from './vaultsProgramAccount';
15
+ import { getVaultAddressSync } from '../addresses';
16
+ import { encodeName } from '../name';
17
+
18
+ export class VaultAccount extends VaultsProgramAccount<
19
+ Vault,
20
+ VaultAccountEvents
21
+ > {
22
+ constructor(
23
+ program: Program<DriftVaults>,
24
+ vaultPubkey: PublicKey,
25
+ accountLoader: BulkAccountLoader,
26
+ accountSubscriptionType: 'polling' | 'websocket' = 'polling'
27
+ ) {
28
+ super();
29
+
30
+ if (accountSubscriptionType === 'polling') {
31
+ this.accountSubscriber = new PollingVaultSubscriber(
32
+ program,
33
+ vaultPubkey,
34
+ accountLoader
35
+ );
36
+ } else {
37
+ throw new Error('Websocket subscription not yet implemented');
38
+ }
39
+ }
40
+
41
+ static getAddressSync(programId: PublicKey, vaultName: string): PublicKey {
42
+ return getVaultAddressSync(programId, encodeName(vaultName));
43
+ }
44
+
45
+ /**
46
+ * Calculates the new total shares and management fee shares after a management fee is applied.
47
+ * Only applies to deposits.
48
+ * Management fee is applied to a depositor's existing equity, and the total shares are updated (increased) accordingly.
49
+ * @param vaultEquity - The equity of the vault.
50
+ * @returns An object containing the new total shares and management fee shares.
51
+ */
52
+ calcSharesAfterManagementFee(vaultEquity: BN): {
53
+ totalShares: BN;
54
+ managementFeeShares: BN;
55
+ } {
56
+ const accountData = this.accountSubscriber.getAccountAndSlot().data;
57
+
58
+ const depositorsEquity = accountData.userShares
59
+ .mul(vaultEquity)
60
+ .div(accountData.totalShares);
61
+
62
+ if (accountData.managementFee.eq(ZERO) || depositorsEquity.lte(ZERO)) {
63
+ return {
64
+ totalShares: accountData.totalShares,
65
+ managementFeeShares: ZERO,
66
+ };
67
+ }
68
+ const now = new BN(Date.now() / 1000);
69
+ const sinceLast = now.sub(accountData.lastFeeUpdateTs);
70
+
71
+ let managementFeeAmount = depositorsEquity
72
+ .mul(accountData.managementFee)
73
+ .div(PERCENTAGE_PRECISION)
74
+ .mul(sinceLast)
75
+ .div(ONE_YEAR);
76
+ managementFeeAmount = BN.min(
77
+ managementFeeAmount,
78
+ depositorsEquity.sub(ONE)
79
+ );
80
+
81
+ const newTotalSharesFactor = depositorsEquity
82
+ .mul(PERCENTAGE_PRECISION)
83
+ .div(depositorsEquity.sub(managementFeeAmount));
84
+ let newTotalShares = accountData.totalShares
85
+ .mul(newTotalSharesFactor)
86
+ .div(PERCENTAGE_PRECISION);
87
+ newTotalShares = BN.max(newTotalShares, accountData.userShares);
88
+
89
+ const managementFeeShares = newTotalShares.sub(accountData.totalShares);
90
+
91
+ return { totalShares: newTotalShares, managementFeeShares };
92
+ }
93
+
94
+ calcSharesAfterManagementAndProtocolFee(
95
+ vaultEquity: BN,
96
+ vaultProtocol: VaultProtocol
97
+ ): {
98
+ totalShares: BN;
99
+ managementFeeShares: BN;
100
+ protocolFeeShares: BN;
101
+ } {
102
+ const accountData = this.accountSubscriber.getAccountAndSlot().data;
103
+
104
+ if (!accountData.vaultProtocol) {
105
+ throw new Error('VaultProtocol does not exist for vault');
106
+ }
107
+
108
+ const depositorsEquity = accountData.userShares
109
+ .mul(vaultEquity)
110
+ .div(accountData.totalShares);
111
+
112
+ const now = new BN(Date.now() / 1000);
113
+ const sinceLast = now.sub(accountData.lastFeeUpdateTs);
114
+
115
+ if (
116
+ !accountData.managementFee.eq(ZERO) &&
117
+ !vaultProtocol.protocolFee.eq(ZERO) &&
118
+ depositorsEquity.gt(ZERO)
119
+ ) {
120
+ const totalFee = accountData.managementFee.add(vaultProtocol.protocolFee);
121
+ const totalFeePayment = depositorsEquity
122
+ .mul(totalFee)
123
+ .div(PERCENTAGE_PRECISION)
124
+ .mul(sinceLast)
125
+ .div(ONE_YEAR);
126
+ const managementFeePayment = depositorsEquity
127
+ .mul(accountData.managementFee)
128
+ .div(PERCENTAGE_PRECISION)
129
+ .mul(sinceLast)
130
+ .div(ONE_YEAR);
131
+ const protocolFeePayment = BN.min(
132
+ totalFeePayment,
133
+ depositorsEquity.sub(new BN(1))
134
+ )
135
+ .mul(vaultProtocol.protocolFee)
136
+ .div(totalFee);
137
+
138
+ const newTotalSharesFactor = depositorsEquity
139
+ .mul(PERCENTAGE_PRECISION)
140
+ .div(
141
+ depositorsEquity.sub(managementFeePayment).sub(protocolFeePayment)
142
+ );
143
+ const newTotalShares = BN.max(
144
+ accountData.totalShares
145
+ .mul(newTotalSharesFactor)
146
+ .div(PERCENTAGE_PRECISION),
147
+ accountData.userShares
148
+ );
149
+
150
+ if (
151
+ (managementFeePayment.eq(ZERO) && protocolFeePayment.eq(ZERO)) ||
152
+ accountData.totalShares.eq(newTotalShares)
153
+ ) {
154
+ return {
155
+ totalShares: accountData.totalShares,
156
+ managementFeeShares: ZERO,
157
+ protocolFeeShares: ZERO,
158
+ };
159
+ }
160
+
161
+ const managementFeeShares = newTotalShares.sub(accountData.totalShares);
162
+ const protocolFeeShares = newTotalShares.sub(accountData.totalShares);
163
+
164
+ return {
165
+ totalShares: newTotalShares,
166
+ managementFeeShares,
167
+ protocolFeeShares,
168
+ };
169
+ } else if (
170
+ accountData.managementFee.eq(ZERO) &&
171
+ !vaultProtocol.protocolFee.eq(ZERO) &&
172
+ depositorsEquity.gt(ZERO)
173
+ ) {
174
+ const protocolFeePayment = depositorsEquity
175
+ .mul(vaultProtocol.protocolFee)
176
+ .div(PERCENTAGE_PRECISION)
177
+ .mul(sinceLast)
178
+ .div(ONE_YEAR);
179
+
180
+ const newTotalSharesFactor = depositorsEquity
181
+ .mul(PERCENTAGE_PRECISION)
182
+ .div(depositorsEquity.sub(protocolFeePayment));
183
+ const newTotalShares = BN.max(
184
+ accountData.totalShares
185
+ .mul(newTotalSharesFactor)
186
+ .div(PERCENTAGE_PRECISION),
187
+ accountData.userShares
188
+ );
189
+
190
+ if (
191
+ protocolFeePayment.eq(ZERO) ||
192
+ accountData.totalShares.eq(newTotalShares)
193
+ ) {
194
+ return {
195
+ totalShares: accountData.totalShares,
196
+ managementFeeShares: ZERO,
197
+ protocolFeeShares: ZERO,
198
+ };
199
+ }
200
+
201
+ const protocolFeeShares = newTotalShares.sub(accountData.totalShares);
202
+ return {
203
+ totalShares: newTotalShares,
204
+ managementFeeShares: ZERO,
205
+ protocolFeeShares,
206
+ };
207
+ } else if (
208
+ !accountData.managementFee.eq(ZERO) &&
209
+ vaultProtocol.protocolFee.eq(ZERO) &&
210
+ depositorsEquity.gt(ZERO)
211
+ ) {
212
+ const managementFeePayment = BN.min(
213
+ depositorsEquity
214
+ .mul(accountData.managementFee)
215
+ .div(PERCENTAGE_PRECISION)
216
+ .mul(sinceLast)
217
+ .div(ONE_YEAR),
218
+ depositorsEquity.sub(ONE)
219
+ );
220
+ const newTotalSharesFactor = depositorsEquity
221
+ .mul(PERCENTAGE_PRECISION)
222
+ .div(depositorsEquity.sub(managementFeePayment));
223
+ const newTotalShares = BN.max(
224
+ accountData.totalShares
225
+ .mul(newTotalSharesFactor)
226
+ .div(PERCENTAGE_PRECISION),
227
+ accountData.userShares
228
+ );
229
+
230
+ if (
231
+ managementFeePayment.eq(ZERO) ||
232
+ accountData.totalShares.eq(newTotalShares)
233
+ ) {
234
+ return {
235
+ totalShares: accountData.totalShares,
236
+ managementFeeShares: ZERO,
237
+ protocolFeeShares: ZERO,
238
+ };
239
+ }
240
+
241
+ const managementFeeShares = newTotalShares.sub(accountData.totalShares);
242
+ return {
243
+ totalShares: newTotalShares,
244
+ managementFeeShares,
245
+ protocolFeeShares: ZERO,
246
+ };
247
+ } else {
248
+ return {
249
+ totalShares: accountData.totalShares,
250
+ managementFeeShares: ZERO,
251
+ protocolFeeShares: ZERO,
252
+ };
253
+ }
254
+ }
255
+ }
@@ -0,0 +1,77 @@
1
+ import { Program } from '@coral-xyz/anchor';
2
+ import {
3
+ BN,
4
+ BulkAccountLoader,
5
+ PERCENTAGE_PRECISION,
6
+ ZERO,
7
+ } from '@velocity-exchange/sdk';
8
+ import { PublicKey } from '@solana/web3.js';
9
+ import { DriftVaults } from '../types/drift_vaults';
10
+ import { VaultDepositor, VaultDepositorAccountEvents } from '../types/types';
11
+ import { PollingVaultDepositorSubscriber } from '../accountSubscribers';
12
+ import { VaultsProgramAccount } from './vaultsProgramAccount';
13
+ import { getVaultDepositorAddressSync } from '../addresses';
14
+
15
+ export class VaultDepositorAccount extends VaultsProgramAccount<
16
+ VaultDepositor,
17
+ VaultDepositorAccountEvents
18
+ > {
19
+ constructor(
20
+ program: Program<DriftVaults>,
21
+ vaultDepositorPubkey: PublicKey,
22
+ accountLoader: BulkAccountLoader,
23
+ accountSubscriptionType: 'polling' | 'websocket' = 'polling'
24
+ ) {
25
+ super();
26
+
27
+ if (accountSubscriptionType === 'polling') {
28
+ this.accountSubscriber = new PollingVaultDepositorSubscriber(
29
+ program,
30
+ vaultDepositorPubkey,
31
+ accountLoader
32
+ );
33
+ } else {
34
+ throw new Error('Websocket subscription not yet implemented');
35
+ }
36
+ }
37
+
38
+ static getAddressSync(
39
+ programId: PublicKey,
40
+ vault: PublicKey,
41
+ authority: PublicKey
42
+ ): PublicKey {
43
+ return getVaultDepositorAddressSync(programId, vault, authority);
44
+ }
45
+
46
+ /**
47
+ * Calculates the percentage of a depositor's equity that will be paid as profit share fees.
48
+ *
49
+ * @param vaultProfitShare Vault's profit share fee
50
+ * @param depositorEquity Vault depositor's equity amount
51
+ */
52
+ calcProfitShareFeesPct(vaultProfitShare: BN, depositorEquity: BN): BN {
53
+ const accountData = this.accountSubscriber.getAccountAndSlot().data;
54
+
55
+ const profit = depositorEquity
56
+ .sub(accountData.netDeposits)
57
+ .sub(accountData.cumulativeProfitShareAmount);
58
+
59
+ if (profit.lte(new BN(0)) || depositorEquity.isZero()) {
60
+ return ZERO;
61
+ }
62
+
63
+ const profitShareAmount = profit
64
+ .mul(vaultProfitShare)
65
+ .div(PERCENTAGE_PRECISION);
66
+
67
+ if (depositorEquity.eq(new BN(0))) {
68
+ return ZERO;
69
+ }
70
+
71
+ const profitShareProportion = profitShareAmount
72
+ .mul(PERCENTAGE_PRECISION)
73
+ .div(depositorEquity);
74
+
75
+ return profitShareProportion;
76
+ }
77
+ }
@@ -0,0 +1,38 @@
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
+ // @ts-ignore
13
+ accountSubscriber: VaultsProgramAccountSubscriber<Account, AccountEvents>;
14
+
15
+ get isSubscribed(): boolean {
16
+ return this.accountSubscriber.isSubscribed;
17
+ }
18
+
19
+ get eventEmitter(): StrictEventEmitter<EventEmitter, AccountEvents> {
20
+ return this.accountSubscriber.eventEmitter;
21
+ }
22
+
23
+ async subscribe(): Promise<boolean> {
24
+ return await this.accountSubscriber.subscribe();
25
+ }
26
+
27
+ async unsubscribe(): Promise<void> {
28
+ return await this.accountSubscriber.unsubscribe();
29
+ }
30
+
31
+ getData(): Account {
32
+ return this.accountSubscriber.getAccountAndSlot()?.data;
33
+ }
34
+
35
+ async updateData(newData: Account, slot: number): Promise<void> {
36
+ return await this.accountSubscriber.updateData(newData, slot);
37
+ }
38
+ }