@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,94 @@
1
+ import { Command, OptionValues } from 'commander';
2
+ import { PublicKey } from '@solana/web3.js';
3
+ import { BN, TEN } from '@velocity-exchange/sdk';
4
+ import { dumpTransactionMessage, getCommandContext } from '../utils';
5
+
6
+ export async function managerRepay(
7
+ program: Command,
8
+ cmdOpts: OptionValues
9
+ ): Promise<void> {
10
+ const {
11
+ vaultAddress,
12
+ repaySpotMarketIndex,
13
+ repayAmount,
14
+ repayValue,
15
+ managerTokenAccount,
16
+ dumpTransactionMessage: dumpTx,
17
+ } = cmdOpts;
18
+
19
+ const { driftClient, driftVault } = await getCommandContext(program, true);
20
+
21
+ if (!vaultAddress) {
22
+ throw new Error('Must provide vault address with --vault-address');
23
+ }
24
+
25
+ if (!repaySpotMarketIndex) {
26
+ throw new Error(
27
+ 'Must provide repay spot market index with --repay-spot-market-index'
28
+ );
29
+ }
30
+
31
+ if (!repayAmount) {
32
+ throw new Error('Must provide repay amount with --repay-amount');
33
+ }
34
+
35
+ const vault = new PublicKey(vaultAddress);
36
+ const repayIndex = parseInt(repaySpotMarketIndex);
37
+
38
+ const repaySpotMarket = driftClient.getSpotMarketAccount(repayIndex);
39
+ if (!repaySpotMarket) {
40
+ throw new Error('No repay spot market found');
41
+ }
42
+
43
+ const vaultAccount = await driftVault.program.account.vault.fetch(vault);
44
+ const depositSpotMarket = driftClient.getSpotMarketAccount(
45
+ vaultAccount.spotMarketIndex
46
+ );
47
+ if (!depositSpotMarket) {
48
+ throw new Error('No deposit spot market found');
49
+ }
50
+ const depositPrecision = TEN.pow(new BN(depositSpotMarket.decimals));
51
+
52
+ const repayPrecision = TEN.pow(new BN(repaySpotMarket.decimals));
53
+ const repayBN = new BN(repayAmount * repayPrecision.toNumber());
54
+
55
+ const valueBN = repayValue
56
+ ? new BN(repayValue * depositPrecision.toNumber())
57
+ : null;
58
+
59
+ const managerTokenAccountPubkey = managerTokenAccount
60
+ ? new PublicKey(managerTokenAccount)
61
+ : undefined;
62
+
63
+ try {
64
+ if (dumpTx) {
65
+ const ixs = await driftVault.getManagerRepayIxs(
66
+ vault,
67
+ repayIndex,
68
+ repayBN,
69
+ valueBN,
70
+ managerTokenAccountPubkey
71
+ );
72
+ console.log('Transaction Instructions:');
73
+ console.log(dumpTransactionMessage(driftClient.wallet.publicKey, ixs));
74
+ return;
75
+ }
76
+
77
+ const txSig = await driftVault.managerRepay(
78
+ vault,
79
+ repayIndex,
80
+ repayBN,
81
+ valueBN,
82
+ managerTokenAccountPubkey
83
+ );
84
+ console.log(`Manager repay transaction signature: ${txSig}`);
85
+ console.log(
86
+ `Transaction: https://solana.fm/tx/${txSig}${
87
+ driftClient.env === 'devnet' ? '?cluster=devnet-solana' : ''
88
+ }`
89
+ );
90
+ } catch (error) {
91
+ console.error('Error repaying:', error);
92
+ throw error;
93
+ }
94
+ }
@@ -0,0 +1,86 @@
1
+ import { BN, TEN, decodeName, numberToSafeBN } from '@velocity-exchange/sdk';
2
+ import { PublicKey } from '@solana/web3.js';
3
+ import { OptionValues, Command } from 'commander';
4
+ import { dumpTransactionMessage, getCommandContext } from '../utils';
5
+ import { WithdrawUnit } from '../../src/types/types';
6
+
7
+ export const managerRequestWithdraw = async (
8
+ program: Command,
9
+ cmdOpts: OptionValues
10
+ ) => {
11
+ let vaultAddress: PublicKey;
12
+ try {
13
+ vaultAddress = new PublicKey(cmdOpts.vaultAddress as string);
14
+ } catch (err) {
15
+ console.error('Invalid vault address');
16
+ process.exit(1);
17
+ }
18
+
19
+ const { driftClient, driftVault } = await getCommandContext(program, true);
20
+
21
+ if (!cmdOpts.shares && !cmdOpts.amount) {
22
+ console.error('One of --shares or --amount must be provided.');
23
+ process.exit(1);
24
+ }
25
+
26
+ if (cmdOpts.shares && !cmdOpts.amount) {
27
+ if (cmdOpts.dumpTransactionMessage) {
28
+ const tx = await driftVault.getManagerRequestWithdrawIx(
29
+ vaultAddress,
30
+ new BN(cmdOpts.shares),
31
+ WithdrawUnit.SHARES
32
+ );
33
+ console.log(dumpTransactionMessage(driftClient.wallet.publicKey, [tx]));
34
+ } else {
35
+ const tx = await driftVault.managerRequestWithdraw(
36
+ vaultAddress,
37
+ new BN(cmdOpts.shares),
38
+ WithdrawUnit.SHARES
39
+ );
40
+ console.log(
41
+ `Requested to withraw ${
42
+ cmdOpts.shares
43
+ } shares as vault manager: https://solana.fm/tx/${tx}${
44
+ driftClient.env === 'devnet' ? '?cluster=devnet-solana' : ''
45
+ }`
46
+ );
47
+ }
48
+ } else if (cmdOpts.amount && !cmdOpts.shares) {
49
+ const vault = await driftVault.getVault(vaultAddress);
50
+ const spotMarket = driftClient.getSpotMarketAccount(vault.spotMarketIndex);
51
+ if (!spotMarket) {
52
+ console.error('Error: Spot market not found');
53
+ process.exit(1);
54
+ }
55
+ const spotPrecision = TEN.pow(new BN(spotMarket.decimals));
56
+ const amount = parseFloat(cmdOpts.amount);
57
+ const amountBN = numberToSafeBN(amount, spotPrecision);
58
+
59
+ if (cmdOpts.dumpTransactionMessage) {
60
+ const tx = await driftVault.getManagerRequestWithdrawIx(
61
+ vaultAddress,
62
+ amountBN,
63
+ WithdrawUnit.TOKEN
64
+ );
65
+ console.log(dumpTransactionMessage(driftClient.wallet.publicKey, [tx]));
66
+ } else {
67
+ const tx = await driftVault.managerRequestWithdraw(
68
+ vaultAddress,
69
+ amountBN,
70
+ WithdrawUnit.TOKEN
71
+ );
72
+ console.log(
73
+ `Requested to withdraw ${amount} ${decodeName(
74
+ spotMarket.name
75
+ )} as vault manager: https://solana.fm/tx/${tx}${
76
+ driftClient.env === 'devnet' ? '?cluster=devnet-solana' : ''
77
+ }`
78
+ );
79
+ }
80
+ } else {
81
+ console.error(
82
+ 'Error: Either shares or amount must be provided, but not both.'
83
+ );
84
+ process.exit(1);
85
+ }
86
+ };
@@ -0,0 +1,56 @@
1
+ import { Command, OptionValues } from 'commander';
2
+ import { PublicKey } from '@solana/web3.js';
3
+ import { BN, TEN } from '@velocity-exchange/sdk';
4
+ import { dumpTransactionMessage, getCommandContext } from '../utils';
5
+
6
+ export async function managerUpdateBorrow(
7
+ program: Command,
8
+ cmdOpts: OptionValues
9
+ ): Promise<void> {
10
+ const {
11
+ vaultAddress,
12
+ newBorrowValue,
13
+ dumpTransactionMessage: dumpTx,
14
+ } = cmdOpts;
15
+
16
+ const { driftClient, driftVault } = await getCommandContext(program, true);
17
+
18
+ if (!vaultAddress) {
19
+ throw new Error('Must provide vault address with --vault-address');
20
+ }
21
+
22
+ if (!newBorrowValue) {
23
+ throw new Error('Must provide new borrow value with --new-borrow-value');
24
+ }
25
+
26
+ const vault = new PublicKey(vaultAddress);
27
+ const vaultAccount = await driftVault.program.account.vault.fetch(vault);
28
+ const depositSpotMarket = driftClient.getSpotMarketAccount(
29
+ vaultAccount.spotMarketIndex
30
+ );
31
+ if (!depositSpotMarket) {
32
+ throw new Error('No deposit spot market found');
33
+ }
34
+ const depositPrecision = TEN.pow(new BN(depositSpotMarket.decimals));
35
+ const borrowValue = new BN(newBorrowValue * depositPrecision.toNumber());
36
+
37
+ try {
38
+ if (dumpTx) {
39
+ const ix = await driftVault.getManagerUpdateBorrowIx(vault, borrowValue);
40
+ console.log('Transaction Instruction:');
41
+ console.log(dumpTransactionMessage(driftClient.wallet.publicKey, [ix]));
42
+ return;
43
+ }
44
+
45
+ const txSig = await driftVault.managerUpdateBorrow(vault, borrowValue);
46
+ console.log(`Manager update borrow transaction signature: ${txSig}`);
47
+ console.log(
48
+ `Transaction: https://solana.fm/tx/${txSig}${
49
+ driftClient.env === 'devnet' ? '?cluster=devnet-solana' : ''
50
+ }`
51
+ );
52
+ } catch (error) {
53
+ console.error('Error updating borrow:', error);
54
+ throw error;
55
+ }
56
+ }
@@ -0,0 +1,156 @@
1
+ import { PublicKey } from '@solana/web3.js';
2
+ import { OptionValues, Command } from 'commander';
3
+ import { dumpTransactionMessage, getCommandContext } from '../utils';
4
+ import {
5
+ BN,
6
+ PERCENTAGE_PRECISION,
7
+ convertToNumber,
8
+ } from '@velocity-exchange/sdk';
9
+
10
+ export const managerUpdateFees = async (
11
+ program: Command,
12
+ cmdOpts: OptionValues
13
+ ) => {
14
+ let vaultAddress: PublicKey;
15
+ try {
16
+ vaultAddress = new PublicKey(cmdOpts.vaultAddress as string);
17
+ } catch (err) {
18
+ console.error('Invalid vault address');
19
+ process.exit(1);
20
+ }
21
+
22
+ const { driftVault, driftClient } = await getCommandContext(program, true);
23
+
24
+ const vault = await driftVault.getVault(vaultAddress);
25
+
26
+ const timelockDuration = cmdOpts.timelockDuration;
27
+ let timelockDurationBN: BN | null = null;
28
+ const minTimelockDurationBN = new BN(
29
+ Math.max(7 * 24 * 60 * 60, vault.redeemPeriod.toNumber() * 2)
30
+ );
31
+ if (timelockDuration !== undefined && timelockDuration !== null) {
32
+ timelockDurationBN = new BN(parseInt(timelockDuration));
33
+ if (timelockDurationBN.lt(minTimelockDurationBN)) {
34
+ throw new Error(
35
+ `Timelock duration must be at least ${minTimelockDurationBN.toNumber()} seconds`
36
+ );
37
+ }
38
+ } else {
39
+ timelockDurationBN = minTimelockDurationBN;
40
+ }
41
+
42
+ let managementFee = cmdOpts.managementFee;
43
+ let managementFeeBN: BN | null = null;
44
+ if (managementFee !== undefined && managementFee !== null) {
45
+ managementFee = parseInt(managementFee);
46
+ managementFeeBN = new BN(managementFee)
47
+ .mul(PERCENTAGE_PRECISION)
48
+ .div(new BN(100));
49
+ }
50
+
51
+ let profitShare = cmdOpts.profitShare;
52
+ let profitShareNumber: number | null = null;
53
+ if (profitShare !== undefined && profitShare !== null) {
54
+ profitShare = parseInt(profitShare);
55
+ profitShareNumber = (profitShare * PERCENTAGE_PRECISION.toNumber()) / 100.0;
56
+ }
57
+
58
+ let hurdleRate = cmdOpts.hurdleRate;
59
+ let hurdleRateNumber: number | null = null;
60
+ if (hurdleRate !== undefined && hurdleRate !== null) {
61
+ hurdleRate = parseInt(hurdleRate);
62
+ hurdleRateNumber = (hurdleRate * PERCENTAGE_PRECISION.toNumber()) / 100.0;
63
+ }
64
+
65
+ console.log(
66
+ `Updating fees, effective in ${timelockDurationBN?.toNumber()} seconds`
67
+ );
68
+
69
+ const managementFeeBefore =
70
+ convertToNumber(vault.managementFee, PERCENTAGE_PRECISION) * 100.0;
71
+ const managementFeeAfter = managementFeeBN
72
+ ? `${convertToNumber(managementFeeBN, PERCENTAGE_PRECISION) * 100.0}%`
73
+ : 'unchanged';
74
+ console.log(
75
+ ` ManagementFee: ${managementFeeBefore}% -> ${managementFeeAfter}`
76
+ );
77
+
78
+ const profitShareBefore =
79
+ (vault.profitShare / PERCENTAGE_PRECISION.toNumber()) * 100.0;
80
+ const profitShareAfter =
81
+ profitShareNumber !== null
82
+ ? `${(profitShareNumber / PERCENTAGE_PRECISION.toNumber()) * 100.0}%`
83
+ : 'unchanged';
84
+ console.log(
85
+ ` ProfitShare: ${profitShareBefore}% -> ${profitShareAfter}`
86
+ );
87
+
88
+ const hurdleRateBefore =
89
+ (vault.hurdleRate / PERCENTAGE_PRECISION.toNumber()) * 100.0;
90
+ const hurdleRateAfter =
91
+ hurdleRateNumber !== null
92
+ ? `${(hurdleRateNumber / PERCENTAGE_PRECISION.toNumber()) * 100.0}%`
93
+ : 'unchanged';
94
+ console.log(
95
+ ` HurdleRate: ${hurdleRateBefore}% -> ${hurdleRateAfter}`
96
+ );
97
+
98
+ const readline = require('readline').createInterface({
99
+ input: process.stdin,
100
+ output: process.stdout,
101
+ });
102
+ console.log('');
103
+ const answer = await new Promise((resolve) => {
104
+ readline.question(
105
+ 'Is the above information correct? (yes/no) ',
106
+ (answer: string) => {
107
+ readline.close();
108
+ resolve(answer);
109
+ }
110
+ );
111
+ });
112
+ if ((answer as string).toLowerCase() !== 'yes') {
113
+ console.log('Fee update canceled.');
114
+ readline.close();
115
+ process.exit(0);
116
+ }
117
+ console.log('Updating fees...');
118
+
119
+ const newParams = {
120
+ timelockDuration: timelockDurationBN,
121
+ newManagementFee: managementFeeBN,
122
+ newProfitShare: profitShareNumber,
123
+ newHurdleRate: hurdleRateNumber,
124
+ };
125
+
126
+ let done = false;
127
+ while (!done) {
128
+ try {
129
+ if (cmdOpts.dumpTransactionMessage) {
130
+ const tx = await driftVault.getManagerUpdateFeesIx(
131
+ vaultAddress,
132
+ newParams
133
+ );
134
+ console.log(dumpTransactionMessage(driftClient.wallet.publicKey, [tx]));
135
+ } else {
136
+ const tx = await driftVault.managerUpdateFees(vaultAddress, newParams);
137
+ console.log(
138
+ `Updated vault fees as vault manager: https://solana.fm/tx/${tx}${
139
+ driftClient.env === 'devnet' ? '?cluster=devnet-solana' : ''
140
+ }`
141
+ );
142
+ done = true;
143
+ }
144
+ break;
145
+ } catch (e) {
146
+ const err = e as Error;
147
+ if (err.message.includes('TransactionExpiredTimeoutError')) {
148
+ console.log(err.message);
149
+ console.log('Transaction timeout. Retrying...');
150
+ await new Promise((resolve) => setTimeout(resolve, 5000));
151
+ } else {
152
+ throw err;
153
+ }
154
+ }
155
+ }
156
+ };
@@ -0,0 +1,32 @@
1
+ import { PublicKey } from "@solana/web3.js";
2
+ import {
3
+ OptionValues,
4
+ Command
5
+ } from "commander";
6
+ import { dumpTransactionMessage, getCommandContext } from "../utils";
7
+
8
+ export const managerUpdateMarginTradingEnabled = async (program: Command, cmdOpts: OptionValues) => {
9
+
10
+ let vaultAddress: PublicKey;
11
+ try {
12
+ vaultAddress = new PublicKey(cmdOpts.vaultAddress as string);
13
+ } catch (err) {
14
+ console.error("Invalid vault address");
15
+ process.exit(1);
16
+ }
17
+
18
+ const {
19
+ driftVault,
20
+ driftClient
21
+ } = await getCommandContext(program, true);
22
+
23
+ const enabled = cmdOpts.enabled ? (cmdOpts.enabled as string).toLowerCase() === "true" : false;
24
+
25
+ if (cmdOpts.dumpTransactionMessage) {
26
+ const tx = await driftVault.getUpdateMarginTradingEnabledIx(vaultAddress, enabled);
27
+ console.log(dumpTransactionMessage(driftClient.wallet.publicKey, [tx]));
28
+ } else {
29
+ const tx = await driftVault.updateMarginTradingEnabled(vaultAddress, enabled);
30
+ console.log(`Updated margin trading vault manager: https://solana.fm/tx/${tx}${driftClient.env === "devnet" ? "?cluster=devnet-solana" : ""}`);
31
+ }
32
+ };
@@ -0,0 +1,36 @@
1
+ import { PublicKey } from "@solana/web3.js";
2
+ import {
3
+ OptionValues,
4
+ Command
5
+ } from "commander";
6
+ import { dumpTransactionMessage, getCommandContext } from "../utils";
7
+
8
+ export const managerUpdatePoolId = async (program: Command, cmdOpts: OptionValues) => {
9
+
10
+ let vaultAddress: PublicKey;
11
+ try {
12
+ vaultAddress = new PublicKey(cmdOpts.vaultAddress as string);
13
+ } catch (err) {
14
+ console.error("Invalid vault address");
15
+ process.exit(1);
16
+ }
17
+
18
+ const {
19
+ driftVault,
20
+ driftClient
21
+ } = await getCommandContext(program, true);
22
+
23
+ const poolId = cmdOpts.poolId ? Number(cmdOpts.poolId) : null;
24
+ if (poolId === null) {
25
+ console.error("Invalid pool id");
26
+ process.exit(1);
27
+ }
28
+
29
+ if (cmdOpts.dumpTransactionMessage) {
30
+ const tx = await driftVault.getUpdatePoolIdIx(vaultAddress, poolId);
31
+ console.log(dumpTransactionMessage(driftClient.wallet.publicKey, [tx]));
32
+ } else {
33
+ const tx = await driftVault.updateUserPoolId(vaultAddress, poolId);
34
+ console.log(`Updated pool id vault manager: https://solana.fm/tx/${tx}${driftClient.env === "devnet" ? "?cluster=devnet-solana" : ""}`);
35
+ }
36
+ };
@@ -0,0 +1,210 @@
1
+ import { PublicKey } from '@solana/web3.js';
2
+ import { OptionValues, Command } from 'commander';
3
+ import { dumpTransactionMessage, getCommandContext } from '../utils';
4
+ import {
5
+ BN,
6
+ PERCENTAGE_PRECISION,
7
+ TEN,
8
+ convertToNumber,
9
+ decodeName,
10
+ } from '@velocity-exchange/sdk';
11
+
12
+ export const managerUpdateVault = async (
13
+ program: Command,
14
+ cmdOpts: OptionValues
15
+ ) => {
16
+ let vaultAddress: PublicKey;
17
+ try {
18
+ vaultAddress = new PublicKey(cmdOpts.vaultAddress as string);
19
+ } catch (err) {
20
+ console.error('Invalid vault address');
21
+ process.exit(1);
22
+ }
23
+
24
+ const { driftVault, driftClient } = await getCommandContext(program, true);
25
+
26
+ const vault = await driftVault.getVault(vaultAddress);
27
+ const spotMarket = driftClient.getSpotMarketAccount(vault.spotMarketIndex);
28
+ if (!spotMarket) {
29
+ throw new Error('No spot market found');
30
+ }
31
+ const spotPrecision = TEN.pow(new BN(spotMarket.decimals));
32
+ const spotMarketName = decodeName(spotMarket.name);
33
+
34
+ let redeemPeriodSec = cmdOpts.redeemPeriod ?? null;
35
+ let redeemPeriodBN: BN | null = null;
36
+ if (redeemPeriodSec !== undefined && redeemPeriodSec !== null) {
37
+ redeemPeriodSec = parseInt(redeemPeriodSec);
38
+ redeemPeriodBN = new BN(redeemPeriodSec);
39
+ }
40
+
41
+ let maxTokens = cmdOpts.maxTokens;
42
+ let maxTokensBN: BN | null = null;
43
+ if (maxTokens !== undefined && maxTokens !== null) {
44
+ maxTokens = parseInt(maxTokens);
45
+ maxTokensBN = new BN(maxTokens).mul(spotPrecision);
46
+ }
47
+
48
+ let managementFee = cmdOpts.managementFee;
49
+ let managementFeeBN: BN | null = null;
50
+ if (managementFee !== undefined && managementFee !== null) {
51
+ managementFee = parseInt(managementFee);
52
+ managementFeeBN = new BN(managementFee)
53
+ .mul(PERCENTAGE_PRECISION)
54
+ .div(new BN(100));
55
+ }
56
+
57
+ let profitShare = cmdOpts.profitShare;
58
+ let profitShareNumber: number | null = null;
59
+ if (profitShare !== undefined && profitShare !== null) {
60
+ profitShare = parseInt(profitShare);
61
+ profitShareNumber = (profitShare * PERCENTAGE_PRECISION.toNumber()) / 100.0;
62
+ }
63
+
64
+ let hurdleRate = cmdOpts.hurdleRate;
65
+ let hurdleRateNumber: number | null = null;
66
+ if (hurdleRate !== undefined && hurdleRate !== null) {
67
+ hurdleRate = parseInt(hurdleRate);
68
+ hurdleRateNumber = (hurdleRate * PERCENTAGE_PRECISION.toNumber()) / 100.0;
69
+ }
70
+
71
+ let minDepositAmount = cmdOpts.minDepositAmount;
72
+ let minDepositAmountBN: BN | null = null;
73
+ if (minDepositAmount !== undefined && minDepositAmount !== null) {
74
+ minDepositAmount = parseInt(minDepositAmount);
75
+ minDepositAmountBN = new BN(minDepositAmount).mul(spotPrecision);
76
+ }
77
+
78
+ console.log(`Updating params:`);
79
+ const redeemPeriodBefore = vault.redeemPeriod.toNumber();
80
+ const redeemPeriodAfter = redeemPeriodBN
81
+ ? redeemPeriodBN.toNumber()
82
+ : 'unchanged';
83
+ console.log(
84
+ ` RedeemPeriod: ${redeemPeriodBefore} -> ${redeemPeriodAfter}`
85
+ );
86
+
87
+ const maxTokensBefore = `${convertToNumber(
88
+ vault.maxTokens,
89
+ spotPrecision
90
+ )} ${spotMarketName}`;
91
+ const maxTokensAfter = maxTokensBN
92
+ ? `${convertToNumber(maxTokensBN, spotPrecision)} ${spotMarketName}`
93
+ : 'unchanged';
94
+ console.log(
95
+ ` MaxTokens: ${maxTokensBefore} -> ${maxTokensAfter}`
96
+ );
97
+
98
+ const minDepositAmountBefore = `${convertToNumber(
99
+ vault.minDepositAmount,
100
+ spotPrecision
101
+ )} ${spotMarketName}`;
102
+ const minDepositAmountAfter = minDepositAmountBN
103
+ ? `${convertToNumber(minDepositAmountBN, spotPrecision)} ${spotMarketName}`
104
+ : 'unchanged';
105
+ console.log(
106
+ ` MinDepositAmount: ${minDepositAmountBefore} -> ${minDepositAmountAfter}`
107
+ );
108
+
109
+ const managementFeeBefore =
110
+ convertToNumber(vault.managementFee, PERCENTAGE_PRECISION) * 100.0;
111
+ const managementFeeAfter = managementFeeBN
112
+ ? `${convertToNumber(managementFeeBN, PERCENTAGE_PRECISION) * 100.0}%`
113
+ : 'unchanged';
114
+ console.log(
115
+ ` ManagementFee: ${managementFeeBefore}% -> ${managementFeeAfter}`
116
+ );
117
+
118
+ const profitShareBefore =
119
+ (vault.profitShare / PERCENTAGE_PRECISION.toNumber()) * 100.0;
120
+ const profitShareAfter =
121
+ profitShareNumber !== null
122
+ ? `${(profitShareNumber / PERCENTAGE_PRECISION.toNumber()) * 100.0}%`
123
+ : 'unchanged';
124
+ console.log(
125
+ ` ProfitShare: ${profitShareBefore}% -> ${profitShareAfter}`
126
+ );
127
+
128
+ const hurdleRateBefore =
129
+ (vault.hurdleRate / PERCENTAGE_PRECISION.toNumber()) * 100.0;
130
+ const hurdleRateAfter =
131
+ hurdleRateNumber !== null
132
+ ? `${(hurdleRateNumber / PERCENTAGE_PRECISION.toNumber()) * 100.0}%`
133
+ : 'unchanged';
134
+ console.log(
135
+ ` HurdleRate: ${hurdleRateBefore}% -> ${hurdleRateAfter}`
136
+ );
137
+
138
+ const permissioned: boolean | null =
139
+ cmdOpts.permissioned === null || cmdOpts.permissioned === undefined
140
+ ? null
141
+ : JSON.parse(cmdOpts.permissioned);
142
+ const permissionedBefore = vault.permissioned;
143
+ const permissionedAfter = permissioned !== null ? permissioned : 'unchanged';
144
+ console.log(
145
+ ` Permissioned: ${permissionedBefore} -> ${permissionedAfter}`
146
+ );
147
+
148
+ const readline = require('readline').createInterface({
149
+ input: process.stdin,
150
+ output: process.stdout,
151
+ });
152
+ console.log('');
153
+ const answer = await new Promise((resolve) => {
154
+ readline.question(
155
+ 'Is the above information correct? (yes/no) ',
156
+ (answer: string) => {
157
+ readline.close();
158
+ resolve(answer);
159
+ }
160
+ );
161
+ });
162
+ if ((answer as string).toLowerCase() !== 'yes') {
163
+ console.log('Vault update canceled.');
164
+ readline.close();
165
+ process.exit(0);
166
+ }
167
+ console.log('Updating vault...');
168
+
169
+ // null means unchanged
170
+ const newParams = {
171
+ redeemPeriod: redeemPeriodBN,
172
+ maxTokens: maxTokensBN,
173
+ minDepositAmount: minDepositAmountBN,
174
+ managementFee: managementFeeBN,
175
+ profitShare: profitShareNumber,
176
+ hurdleRate: hurdleRateNumber,
177
+ permissioned,
178
+ };
179
+
180
+ let done = false;
181
+ while (!done) {
182
+ try {
183
+ if (cmdOpts.dumpTransactionMessage) {
184
+ const tx = await driftVault.getManagerUpdateVaultIx(
185
+ vaultAddress,
186
+ newParams
187
+ );
188
+ console.log(dumpTransactionMessage(driftClient.wallet.publicKey, [tx]));
189
+ } else {
190
+ const tx = await driftVault.managerUpdateVault(vaultAddress, newParams);
191
+ console.log(
192
+ `Updated vault params as vault manager: https://solana.fm/tx/${tx}${
193
+ driftClient.env === 'devnet' ? '?cluster=devnet-solana' : ''
194
+ }`
195
+ );
196
+ done = true;
197
+ }
198
+ break;
199
+ } catch (e) {
200
+ const err = e as Error;
201
+ if (err.message.includes('TransactionExpiredTimeoutError')) {
202
+ console.log(err.message);
203
+ console.log('Transaction timeout. Retrying...');
204
+ await new Promise((resolve) => setTimeout(resolve, 5000));
205
+ } else {
206
+ throw err;
207
+ }
208
+ }
209
+ }
210
+ };