@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.
- package/.env.example +3 -0
- package/README.md +152 -0
- package/cli/cli.ts +751 -0
- package/cli/commands/adminDeleteFeeUpdate.ts +73 -0
- package/cli/commands/adminInitFeeUpdate.ts +73 -0
- package/cli/commands/adminUpdateVaultClass.ts +49 -0
- package/cli/commands/applyProfitShare.ts +139 -0
- package/cli/commands/decodeLogs.ts +98 -0
- package/cli/commands/deposit.ts +98 -0
- package/cli/commands/deriveVaultAddress.ts +14 -0
- package/cli/commands/forceWithdraw.ts +56 -0
- package/cli/commands/forceWithdrawAll.ts +142 -0
- package/cli/commands/index.ts +28 -0
- package/cli/commands/initVault.ts +227 -0
- package/cli/commands/initVaultDepositor.ts +42 -0
- package/cli/commands/listDepositorsForVault.ts +32 -0
- package/cli/commands/managerApplyProfitShare.ts +32 -0
- package/cli/commands/managerBorrow.ts +77 -0
- package/cli/commands/managerCancelWithdraw.ts +30 -0
- package/cli/commands/managerDeposit.ts +45 -0
- package/cli/commands/managerRepay.ts +94 -0
- package/cli/commands/managerRequestWithdraw.ts +86 -0
- package/cli/commands/managerUpdateBorrow.ts +56 -0
- package/cli/commands/managerUpdateFees.ts +156 -0
- package/cli/commands/managerUpdateMarginTradingEnabled.ts +32 -0
- package/cli/commands/managerUpdatePoolId.ts +36 -0
- package/cli/commands/managerUpdateVault.ts +210 -0
- package/cli/commands/managerUpdateVaultDelegate.ts +43 -0
- package/cli/commands/managerUpdateVaultManager.ts +77 -0
- package/cli/commands/managerWithdraw.ts +30 -0
- package/cli/commands/requestWithdraw.ts +58 -0
- package/cli/commands/vaultDeposit.ts +42 -0
- package/cli/commands/vaultInvariantChecks.ts +407 -0
- package/cli/commands/vaultWithdraw.ts +42 -0
- package/cli/commands/viewVault.ts +50 -0
- package/cli/commands/viewVaultDepositor.ts +36 -0
- package/cli/commands/withdraw.ts +40 -0
- package/cli/ledgerWallet.test.ts +49 -0
- package/cli/ledgerWallet.ts +111 -0
- package/cli/utils.ts +389 -0
- package/package.json +48 -0
- package/src/accountSubscribers/index.ts +2 -0
- package/src/accountSubscribers/pollingVaultDepositorSubscriber.ts +69 -0
- package/src/accountSubscribers/pollingVaultSubscriber.ts +63 -0
- package/src/accountSubscribers/pollingVaultsProgramAccountSubscriber.ts +114 -0
- package/src/accounts/index.ts +2 -0
- package/src/accounts/vaultAccount.ts +255 -0
- package/src/accounts/vaultDepositorAccount.ts +77 -0
- package/src/accounts/vaultsProgramAccount.ts +38 -0
- package/src/addresses.ts +114 -0
- package/src/constants/index.ts +15 -0
- package/src/idl/drift_vaults.json +5698 -0
- package/src/index.ts +11 -0
- package/src/math/index.ts +2 -0
- package/src/math/vault.ts +71 -0
- package/src/math/vaultDepositor.ts +90 -0
- package/src/name.ts +18 -0
- package/src/parsers/index.ts +1 -0
- package/src/parsers/logParser.ts +28 -0
- package/src/types/drift_vaults.ts +6211 -0
- package/src/types/types.ts +336 -0
- package/src/utils.ts +74 -0
- package/src/vaultClient.ts +3666 -0
- package/tsconfig.json +24 -0
- package/velocity-exchange-vaults-sdk-0.0.1.tgz +0 -0
|
@@ -0,0 +1,3666 @@
|
|
|
1
|
+
import {
|
|
2
|
+
BN,
|
|
3
|
+
BigNum,
|
|
4
|
+
DriftClient,
|
|
5
|
+
getInsuranceFundStakeAccountPublicKey,
|
|
6
|
+
getUserAccountPublicKey,
|
|
7
|
+
getUserAccountPublicKeySync,
|
|
8
|
+
getUserStatsAccountPublicKey,
|
|
9
|
+
TEN,
|
|
10
|
+
UserMap,
|
|
11
|
+
unstakeSharesToAmount as depositSharesToVaultAmount,
|
|
12
|
+
ZERO,
|
|
13
|
+
getInsuranceFundVaultPublicKey,
|
|
14
|
+
OracleSource,
|
|
15
|
+
WRAPPED_SOL_MINT,
|
|
16
|
+
SpotMarketAccount,
|
|
17
|
+
UserAccount,
|
|
18
|
+
UserStatsAccount,
|
|
19
|
+
QUOTE_PRECISION_EXP,
|
|
20
|
+
} from '@velocity-exchange/sdk';
|
|
21
|
+
import { BorshAccountsCoder, Program, ProgramAccount } from '@coral-xyz/anchor';
|
|
22
|
+
import { DriftVaults } from './types/drift_vaults';
|
|
23
|
+
import {
|
|
24
|
+
getTokenizedVaultAddressSync,
|
|
25
|
+
getTokenizedVaultMintAddressSync,
|
|
26
|
+
getInsuranceFundTokenVaultAddressSync,
|
|
27
|
+
getTokenVaultAddressSync,
|
|
28
|
+
getVaultAddressSync,
|
|
29
|
+
getVaultDepositorAddressSync,
|
|
30
|
+
getVaultProtocolAddressSync,
|
|
31
|
+
getFeeUpdateAddressSync,
|
|
32
|
+
} from './addresses';
|
|
33
|
+
import {
|
|
34
|
+
AccountMeta,
|
|
35
|
+
AddressLookupTableAccount,
|
|
36
|
+
ComputeBudgetProgram,
|
|
37
|
+
PublicKey,
|
|
38
|
+
SystemProgram,
|
|
39
|
+
SYSVAR_RENT_PUBKEY,
|
|
40
|
+
TransactionInstruction,
|
|
41
|
+
TransactionSignature,
|
|
42
|
+
VersionedTransaction,
|
|
43
|
+
} from '@solana/web3.js';
|
|
44
|
+
import {
|
|
45
|
+
createAssociatedTokenAccountInstruction,
|
|
46
|
+
createCloseAccountInstruction,
|
|
47
|
+
createSyncNativeInstruction,
|
|
48
|
+
getAssociatedTokenAddressSync,
|
|
49
|
+
TOKEN_PROGRAM_ID,
|
|
50
|
+
} from '@solana/spl-token';
|
|
51
|
+
import {
|
|
52
|
+
FeeUpdate,
|
|
53
|
+
hasPendingFeeUpdate,
|
|
54
|
+
Vault,
|
|
55
|
+
VaultClass,
|
|
56
|
+
VaultDepositor,
|
|
57
|
+
VaultParams,
|
|
58
|
+
VaultProtocol,
|
|
59
|
+
VaultProtocolParams,
|
|
60
|
+
VaultWithProtocolParams,
|
|
61
|
+
WithdrawUnit,
|
|
62
|
+
} from './types/types';
|
|
63
|
+
import { bs58 } from '@coral-xyz/anchor/dist/cjs/utils/bytes';
|
|
64
|
+
import { UserMapConfig } from '@velocity-exchange/sdk';
|
|
65
|
+
import { calculateRealizedVaultDepositorEquity } from './math';
|
|
66
|
+
import { Metaplex } from '@metaplex-foundation/js';
|
|
67
|
+
import { getOrCreateATAInstruction } from './utils';
|
|
68
|
+
import { VAULT_ADMIN_KEY, VAULT_SHARES_PRECISION_EXP } from './constants';
|
|
69
|
+
|
|
70
|
+
type OracleFeedConfig = {
|
|
71
|
+
feed: PublicKey;
|
|
72
|
+
oracleSource: OracleSource;
|
|
73
|
+
pythLazerId?: number;
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
export type TxParams = {
|
|
77
|
+
cuLimit?: number;
|
|
78
|
+
cuPriceMicroLamports?: number;
|
|
79
|
+
simulateTransaction?: boolean;
|
|
80
|
+
lookupTables?: AddressLookupTableAccount[];
|
|
81
|
+
oracleFeedsToCrank?: {
|
|
82
|
+
feedsToCrank: OracleFeedConfig[];
|
|
83
|
+
pythLazerMsgHexGetter?: (feedIds: number[]) => Promise<string>;
|
|
84
|
+
};
|
|
85
|
+
noLut?: boolean;
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
export class VaultClient {
|
|
89
|
+
driftClient: DriftClient;
|
|
90
|
+
metaplex?: Metaplex;
|
|
91
|
+
program: Program<DriftVaults>;
|
|
92
|
+
cliMode: boolean;
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Cache map of drift user accounts of vaults.
|
|
96
|
+
*/
|
|
97
|
+
readonly vaultUsers: UserMap;
|
|
98
|
+
|
|
99
|
+
constructor({
|
|
100
|
+
driftClient,
|
|
101
|
+
program,
|
|
102
|
+
metaplex,
|
|
103
|
+
// @deprecated, no longer used
|
|
104
|
+
cliMode,
|
|
105
|
+
userMapConfig,
|
|
106
|
+
}: {
|
|
107
|
+
driftClient: DriftClient;
|
|
108
|
+
program: Program<DriftVaults>;
|
|
109
|
+
metaplex?: Metaplex;
|
|
110
|
+
// @deprecated, no longer used
|
|
111
|
+
cliMode?: boolean;
|
|
112
|
+
userMapConfig?: UserMapConfig;
|
|
113
|
+
}) {
|
|
114
|
+
this.driftClient = driftClient;
|
|
115
|
+
this.metaplex = metaplex;
|
|
116
|
+
this.program = program;
|
|
117
|
+
this.cliMode = !!cliMode;
|
|
118
|
+
|
|
119
|
+
if (!userMapConfig) {
|
|
120
|
+
this.vaultUsers = new UserMap({
|
|
121
|
+
driftClient: driftClient,
|
|
122
|
+
subscriptionConfig: {
|
|
123
|
+
type: 'polling',
|
|
124
|
+
frequency: 1000,
|
|
125
|
+
commitment: 'processed',
|
|
126
|
+
},
|
|
127
|
+
});
|
|
128
|
+
} else {
|
|
129
|
+
this.vaultUsers = new UserMap(userMapConfig);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
private getRemainingAccountsForUser(
|
|
134
|
+
userAccounts: UserAccount[],
|
|
135
|
+
writableSpotMarketIndexes: number[],
|
|
136
|
+
vaultAccount: Vault,
|
|
137
|
+
_userStats: UserStatsAccount,
|
|
138
|
+
skipVaultProtocol = false,
|
|
139
|
+
_skipFuelOverflow = false,
|
|
140
|
+
skipFeeUpdate = false
|
|
141
|
+
) {
|
|
142
|
+
const remainingAccounts = this.driftClient.getRemainingAccounts({
|
|
143
|
+
userAccounts,
|
|
144
|
+
writableSpotMarketIndexes,
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
const hasVaultProtocol = vaultAccount.vaultProtocol === true;
|
|
148
|
+
|
|
149
|
+
const hasFeeUpdate = hasPendingFeeUpdate(vaultAccount.feeUpdateStatus);
|
|
150
|
+
|
|
151
|
+
if (hasFeeUpdate && !skipFeeUpdate) {
|
|
152
|
+
const feeUpdate = getFeeUpdateAddressSync(
|
|
153
|
+
this.program.programId,
|
|
154
|
+
vaultAccount.pubkey
|
|
155
|
+
);
|
|
156
|
+
remainingAccounts.push({
|
|
157
|
+
pubkey: feeUpdate,
|
|
158
|
+
isSigner: false,
|
|
159
|
+
isWritable: true,
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (hasVaultProtocol && !skipVaultProtocol) {
|
|
164
|
+
const vaultProtocol = this.getVaultProtocolAddress(vaultAccount.pubkey);
|
|
165
|
+
remainingAccounts.push({
|
|
166
|
+
pubkey: vaultProtocol,
|
|
167
|
+
isSigner: false,
|
|
168
|
+
isWritable: true,
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return remainingAccounts;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
private async checkIfAccountExists(account: PublicKey): Promise<boolean> {
|
|
176
|
+
try {
|
|
177
|
+
const accountInfo = await this.driftClient.connection.getAccountInfo(
|
|
178
|
+
account
|
|
179
|
+
);
|
|
180
|
+
return accountInfo != null;
|
|
181
|
+
} catch (e) {
|
|
182
|
+
// Doesn't already exist
|
|
183
|
+
return false;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Unsubscribes from the vault users map. Call this to clean up any dangling promises.
|
|
189
|
+
*/
|
|
190
|
+
public async unsubscribe() {
|
|
191
|
+
if (this.vaultUsers) {
|
|
192
|
+
await this.vaultUsers.unsubscribe();
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
public async getVault(vault: PublicKey): Promise<Vault> {
|
|
197
|
+
return await this.program.account.vault.fetch(vault);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
public async getFeeUpdate(feeUpdate: PublicKey): Promise<FeeUpdate> {
|
|
201
|
+
return await this.program.account.feeUpdate.fetch(feeUpdate);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
public async getVaultAndSlot(
|
|
205
|
+
vault: PublicKey
|
|
206
|
+
): Promise<{ vault: Vault; slot: number }> {
|
|
207
|
+
const vaultAndSlot = await this.program.account.vault.fetchAndContext(
|
|
208
|
+
vault
|
|
209
|
+
);
|
|
210
|
+
return {
|
|
211
|
+
vault: vaultAndSlot.data as Vault,
|
|
212
|
+
slot: vaultAndSlot.context.slot,
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
public async getVaultDepositor(vaultDepositor: PublicKey): Promise<any> {
|
|
217
|
+
return await this.program.account.vaultDepositor.fetch(vaultDepositor);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
public async getVaultDepositorAndSlot(
|
|
221
|
+
vaultDepositor: PublicKey
|
|
222
|
+
): Promise<{ vaultDepositor: any; slot: number }> {
|
|
223
|
+
const vaultDepositorAndSlot =
|
|
224
|
+
await this.program.account.vaultDepositor.fetchAndContext(vaultDepositor);
|
|
225
|
+
return {
|
|
226
|
+
vaultDepositor: vaultDepositorAndSlot.data,
|
|
227
|
+
slot: vaultDepositorAndSlot.context.slot,
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
public getVaultProtocolAddress(vault: PublicKey): PublicKey {
|
|
232
|
+
return getVaultProtocolAddressSync(this.program.programId, vault);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
public async getVaultProtocol(
|
|
236
|
+
vaultProtocol: PublicKey
|
|
237
|
+
): Promise<VaultProtocol> {
|
|
238
|
+
return await this.program.account.vaultProtocol.fetch(vaultProtocol);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
public async getVaultProtocolAndSlot(
|
|
242
|
+
vaultProtocol: PublicKey
|
|
243
|
+
): Promise<{ vaultProtocol: VaultProtocol; slot: number }> {
|
|
244
|
+
const vaultProtocolAndSlot =
|
|
245
|
+
await this.program.account.vaultProtocol.fetchAndContext(vaultProtocol);
|
|
246
|
+
return {
|
|
247
|
+
vaultProtocol: vaultProtocolAndSlot.data as VaultProtocol,
|
|
248
|
+
slot: vaultProtocolAndSlot.context.slot,
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
public async getAllVaultDepositorsWithNoWithdrawRequest(
|
|
253
|
+
vault: PublicKey
|
|
254
|
+
): Promise<ProgramAccount<VaultDepositor>[]> {
|
|
255
|
+
const filters = [
|
|
256
|
+
{
|
|
257
|
+
// discriminator = VaultDepositor
|
|
258
|
+
memcmp: {
|
|
259
|
+
offset: 0,
|
|
260
|
+
bytes: bs58.encode(
|
|
261
|
+
(
|
|
262
|
+
this.program.coder.accounts as BorshAccountsCoder
|
|
263
|
+
).accountDiscriminator('vaultDepositor')
|
|
264
|
+
),
|
|
265
|
+
},
|
|
266
|
+
},
|
|
267
|
+
{
|
|
268
|
+
// vault = vault
|
|
269
|
+
memcmp: {
|
|
270
|
+
offset: 8,
|
|
271
|
+
bytes: vault.toBase58(),
|
|
272
|
+
},
|
|
273
|
+
},
|
|
274
|
+
{
|
|
275
|
+
// last_withdraw_request.shares (u128) = 0
|
|
276
|
+
memcmp: {
|
|
277
|
+
offset: 112,
|
|
278
|
+
bytes: bs58.encode(new Uint8Array(16).fill(0)),
|
|
279
|
+
},
|
|
280
|
+
},
|
|
281
|
+
];
|
|
282
|
+
// @ts-ignore
|
|
283
|
+
return (await this.program.account.vaultDepositor.all(
|
|
284
|
+
filters
|
|
285
|
+
)) as ProgramAccount<VaultDepositor>[];
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
public async getAllVaultDepositors(
|
|
289
|
+
vault?: PublicKey
|
|
290
|
+
): Promise<ProgramAccount<VaultDepositor>[]> {
|
|
291
|
+
const filters = [
|
|
292
|
+
{
|
|
293
|
+
// discriminator = VaultDepositor
|
|
294
|
+
memcmp: {
|
|
295
|
+
offset: 0,
|
|
296
|
+
bytes: bs58.encode(
|
|
297
|
+
(
|
|
298
|
+
this.program.coder.accounts as BorshAccountsCoder
|
|
299
|
+
).accountDiscriminator('vaultDepositor')
|
|
300
|
+
),
|
|
301
|
+
},
|
|
302
|
+
},
|
|
303
|
+
];
|
|
304
|
+
if (vault) {
|
|
305
|
+
filters.push({
|
|
306
|
+
// vault = vault
|
|
307
|
+
memcmp: {
|
|
308
|
+
offset: 8,
|
|
309
|
+
bytes: vault.toBase58(),
|
|
310
|
+
},
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
// @ts-ignore
|
|
314
|
+
return (await this.program.account.vaultDepositor.all(
|
|
315
|
+
filters
|
|
316
|
+
)) as ProgramAccount<VaultDepositor>[];
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
public async getAllVaultDepositorsForAuthority(
|
|
320
|
+
authority: PublicKey
|
|
321
|
+
): Promise<ProgramAccount<VaultDepositor>[]> {
|
|
322
|
+
const filters = [
|
|
323
|
+
{
|
|
324
|
+
// discriminator = VaultDepositor
|
|
325
|
+
memcmp: {
|
|
326
|
+
offset: 0,
|
|
327
|
+
bytes: bs58.encode(
|
|
328
|
+
(
|
|
329
|
+
this.program.coder.accounts as BorshAccountsCoder
|
|
330
|
+
).accountDiscriminator('vaultDepositor')
|
|
331
|
+
),
|
|
332
|
+
},
|
|
333
|
+
},
|
|
334
|
+
];
|
|
335
|
+
filters.push({
|
|
336
|
+
// authority = authority
|
|
337
|
+
memcmp: {
|
|
338
|
+
offset: 8 + 32 + 32,
|
|
339
|
+
bytes: authority.toBase58(),
|
|
340
|
+
},
|
|
341
|
+
});
|
|
342
|
+
// @ts-ignore
|
|
343
|
+
return (await this.program.account.vaultDepositor.all(
|
|
344
|
+
filters
|
|
345
|
+
)) as ProgramAccount<VaultDepositor>[];
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
public async getSubscribedVaultUser(vaultDriftUserAccountPubKey: PublicKey) {
|
|
349
|
+
return this.vaultUsers.mustGet(vaultDriftUserAccountPubKey.toBase58(), {
|
|
350
|
+
type: 'websocket',
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
/// useful for syncing state during tests.
|
|
355
|
+
public async syncVaultUsers() {
|
|
356
|
+
for (const user of this.vaultUsers.values()) {
|
|
357
|
+
await user.fetchAccounts();
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
*
|
|
363
|
+
* @param vault pubkey
|
|
364
|
+
* @param factorUnrealizedPNL add unrealized pnl to net balance
|
|
365
|
+
* @returns vault equity, in USDC
|
|
366
|
+
*/
|
|
367
|
+
public async calculateVaultEquity(params: {
|
|
368
|
+
address?: PublicKey;
|
|
369
|
+
vault?: Vault;
|
|
370
|
+
factorUnrealizedPNL?: boolean;
|
|
371
|
+
includeManagerBorrowedValue?: boolean;
|
|
372
|
+
}): Promise<BN> {
|
|
373
|
+
try {
|
|
374
|
+
// defaults to true if undefined
|
|
375
|
+
let factorUnrealizedPNL = true;
|
|
376
|
+
if (params.factorUnrealizedPNL !== undefined) {
|
|
377
|
+
factorUnrealizedPNL = params.factorUnrealizedPNL;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
let includeManagerBorrowedValue = true;
|
|
381
|
+
if (params.includeManagerBorrowedValue !== undefined) {
|
|
382
|
+
includeManagerBorrowedValue = params.includeManagerBorrowedValue;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
let vaultAccount: Vault;
|
|
386
|
+
if (params.address !== undefined) {
|
|
387
|
+
// @ts-ignore
|
|
388
|
+
vaultAccount = await this.program.account.vault.fetch(params.address);
|
|
389
|
+
} else if (params.vault !== undefined) {
|
|
390
|
+
vaultAccount = params.vault;
|
|
391
|
+
} else {
|
|
392
|
+
throw new Error('Must supply address or vault');
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
const user = await this.getSubscribedVaultUser(vaultAccount.user);
|
|
396
|
+
|
|
397
|
+
let netSpotValue = user.getNetSpotMarketValue();
|
|
398
|
+
|
|
399
|
+
if (factorUnrealizedPNL) {
|
|
400
|
+
const unrealizedPnl = user.getUnrealizedPNL(true, undefined, undefined);
|
|
401
|
+
netSpotValue = netSpotValue.add(unrealizedPnl);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
if (includeManagerBorrowedValue) {
|
|
405
|
+
netSpotValue = netSpotValue.add(vaultAccount.managerBorrowedValue);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
return netSpotValue;
|
|
409
|
+
} catch (err) {
|
|
410
|
+
console.error('VaultClient ~ err:', err);
|
|
411
|
+
return ZERO;
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
public async calculateVaultAllTimeNotionalPnl(params: {
|
|
416
|
+
address?: PublicKey;
|
|
417
|
+
vault?: Vault;
|
|
418
|
+
}): Promise<BN> {
|
|
419
|
+
try {
|
|
420
|
+
let vaultAccount: Vault;
|
|
421
|
+
if (params.address !== undefined) {
|
|
422
|
+
// @ts-ignore
|
|
423
|
+
vaultAccount = await this.program.account.vault.fetch(params.address);
|
|
424
|
+
} else if (params.vault !== undefined) {
|
|
425
|
+
vaultAccount = params.vault;
|
|
426
|
+
} else {
|
|
427
|
+
throw new Error('Must supply address or vault');
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
const user = await this.getSubscribedVaultUser(vaultAccount.user);
|
|
431
|
+
const allTimeTotalPnl = user.getTotalAllTimePnl();
|
|
432
|
+
|
|
433
|
+
return allTimeTotalPnl;
|
|
434
|
+
} catch (err) {
|
|
435
|
+
console.error('VaultClient ~ err:', err);
|
|
436
|
+
return ZERO;
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
/**
|
|
441
|
+
*
|
|
442
|
+
* @param vault pubkey
|
|
443
|
+
* @param factorUnrealizedPNL add unrealized pnl to existing equity
|
|
444
|
+
* @returns total vault equity, in spot deposit asset
|
|
445
|
+
*/
|
|
446
|
+
public async calculateVaultEquityInDepositAsset(params: {
|
|
447
|
+
address?: PublicKey;
|
|
448
|
+
vault?: Vault;
|
|
449
|
+
factorUnrealizedPNL?: boolean;
|
|
450
|
+
}): Promise<BN> {
|
|
451
|
+
let vaultAccount: Vault;
|
|
452
|
+
if (params.address !== undefined) {
|
|
453
|
+
vaultAccount = await this.program.account.vault.fetch(params.address);
|
|
454
|
+
} else if (params.vault !== undefined) {
|
|
455
|
+
vaultAccount = params.vault;
|
|
456
|
+
} else {
|
|
457
|
+
throw new Error('Must supply address or vault');
|
|
458
|
+
}
|
|
459
|
+
const vaultEquity = await this.calculateVaultEquity({
|
|
460
|
+
vault: vaultAccount,
|
|
461
|
+
factorUnrealizedPNL: params.factorUnrealizedPNL,
|
|
462
|
+
});
|
|
463
|
+
const spotMarket = this.driftClient.getSpotMarketAccount(
|
|
464
|
+
vaultAccount.spotMarketIndex
|
|
465
|
+
);
|
|
466
|
+
const spotOracle = this.driftClient.getOracleDataForSpotMarket(
|
|
467
|
+
vaultAccount.spotMarketIndex
|
|
468
|
+
);
|
|
469
|
+
const spotPrecision = TEN.pow(new BN(spotMarket!.decimals));
|
|
470
|
+
|
|
471
|
+
return vaultEquity.mul(spotPrecision).div(spotOracle.price);
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
/**
|
|
475
|
+
* @param params
|
|
476
|
+
* @returns vault depositor equity, in spot market value (which is usually USDC)
|
|
477
|
+
*/
|
|
478
|
+
public async calculateWithdrawableVaultDepositorEquity(params: {
|
|
479
|
+
vaultDepositorAddress?: PublicKey;
|
|
480
|
+
vaultDepositor?: VaultDepositor;
|
|
481
|
+
vaultAddress?: PublicKey;
|
|
482
|
+
vault?: Vault;
|
|
483
|
+
}): Promise<BN> {
|
|
484
|
+
let vaultAccount: Vault;
|
|
485
|
+
if (params.vaultAddress !== undefined) {
|
|
486
|
+
vaultAccount = await this.program.account.vault.fetch(
|
|
487
|
+
params.vaultAddress
|
|
488
|
+
);
|
|
489
|
+
} else if (params.vault !== undefined) {
|
|
490
|
+
vaultAccount = params.vault;
|
|
491
|
+
} else {
|
|
492
|
+
throw new Error('Must supply vaultAddress or vault');
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
let vaultDepositorAccount: VaultDepositor;
|
|
496
|
+
if (params.vaultDepositorAddress !== undefined) {
|
|
497
|
+
vaultDepositorAccount = await this.program.account.vaultDepositor.fetch(
|
|
498
|
+
params.vaultDepositorAddress
|
|
499
|
+
);
|
|
500
|
+
} else if (params.vaultDepositor !== undefined) {
|
|
501
|
+
vaultDepositorAccount = params.vaultDepositor;
|
|
502
|
+
} else {
|
|
503
|
+
throw new Error('Must supply vaultDepositorAddress or vaultDepositor');
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
const vaultEquity = await this.calculateVaultEquity({
|
|
507
|
+
vault: vaultAccount,
|
|
508
|
+
factorUnrealizedPNL: false,
|
|
509
|
+
});
|
|
510
|
+
return calculateRealizedVaultDepositorEquity(
|
|
511
|
+
vaultDepositorAccount,
|
|
512
|
+
vaultEquity,
|
|
513
|
+
vaultAccount
|
|
514
|
+
);
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
public async calculateWithdrawableVaultDepositorEquityInDepositAsset(params: {
|
|
518
|
+
vaultDepositorAddress?: PublicKey;
|
|
519
|
+
vaultDepositor?: VaultDepositor;
|
|
520
|
+
vaultAddress?: PublicKey;
|
|
521
|
+
vault?: Vault;
|
|
522
|
+
}): Promise<BN> {
|
|
523
|
+
let vaultAccount: Vault;
|
|
524
|
+
if (params.vaultAddress !== undefined) {
|
|
525
|
+
vaultAccount = await this.program.account.vault.fetch(
|
|
526
|
+
params.vaultAddress
|
|
527
|
+
);
|
|
528
|
+
} else if (params.vault !== undefined) {
|
|
529
|
+
vaultAccount = params.vault;
|
|
530
|
+
} else {
|
|
531
|
+
throw new Error('Must supply vaultAddress or vault');
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
let vaultDepositorAccount: VaultDepositor;
|
|
535
|
+
if (params.vaultDepositorAddress !== undefined) {
|
|
536
|
+
vaultDepositorAccount = await this.program.account.vaultDepositor.fetch(
|
|
537
|
+
params.vaultDepositorAddress
|
|
538
|
+
);
|
|
539
|
+
} else if (params.vaultDepositor !== undefined) {
|
|
540
|
+
vaultDepositorAccount = params.vaultDepositor;
|
|
541
|
+
} else {
|
|
542
|
+
throw new Error('Must supply vaultDepositorAddress or vaultDepositor');
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
let vaultProtocol: VaultProtocol | undefined = undefined;
|
|
546
|
+
if (vaultAccount.vaultProtocol) {
|
|
547
|
+
vaultProtocol = await this.program.account.vaultProtocol.fetch(
|
|
548
|
+
this.getVaultProtocolAddress(vaultAccount.pubkey)
|
|
549
|
+
);
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
const vaultEquity = await this.calculateVaultEquity({
|
|
553
|
+
vault: vaultAccount,
|
|
554
|
+
factorUnrealizedPNL: false,
|
|
555
|
+
});
|
|
556
|
+
const vdEquity = calculateRealizedVaultDepositorEquity(
|
|
557
|
+
vaultDepositorAccount,
|
|
558
|
+
vaultEquity,
|
|
559
|
+
vaultAccount,
|
|
560
|
+
vaultProtocol
|
|
561
|
+
);
|
|
562
|
+
|
|
563
|
+
const spotMarket = this.driftClient.getSpotMarketAccount(
|
|
564
|
+
vaultAccount.spotMarketIndex
|
|
565
|
+
);
|
|
566
|
+
const spotOracle = this.driftClient.getOracleDataForSpotMarket(
|
|
567
|
+
vaultAccount.spotMarketIndex
|
|
568
|
+
);
|
|
569
|
+
const spotPrecision = TEN.pow(new BN(spotMarket!.decimals));
|
|
570
|
+
|
|
571
|
+
return vdEquity.mul(spotPrecision).div(spotOracle.price);
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
public async calculateVaultProtocolEquity(params: {
|
|
575
|
+
vault: PublicKey;
|
|
576
|
+
}): Promise<BN> {
|
|
577
|
+
const vaultAccount = await this.program.account.vault.fetch(params.vault);
|
|
578
|
+
const vaultTotalEquity = await this.calculateVaultEquity({
|
|
579
|
+
vault: vaultAccount,
|
|
580
|
+
});
|
|
581
|
+
const vaultProtocol = this.getVaultProtocolAddress(params.vault);
|
|
582
|
+
const vpAccount = await this.program.account.vaultProtocol.fetch(
|
|
583
|
+
vaultProtocol
|
|
584
|
+
);
|
|
585
|
+
return depositSharesToVaultAmount(
|
|
586
|
+
vpAccount.protocolProfitAndFeeShares,
|
|
587
|
+
vaultAccount.totalShares,
|
|
588
|
+
vaultTotalEquity
|
|
589
|
+
);
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
public async initializeVault(
|
|
593
|
+
params: {
|
|
594
|
+
name: number[];
|
|
595
|
+
spotMarketIndex: number;
|
|
596
|
+
redeemPeriod: BN;
|
|
597
|
+
maxTokens: BN;
|
|
598
|
+
minDepositAmount: BN;
|
|
599
|
+
managementFee: BN;
|
|
600
|
+
profitShare: number;
|
|
601
|
+
hurdleRate: number;
|
|
602
|
+
permissioned: boolean;
|
|
603
|
+
vaultProtocol?: VaultProtocolParams;
|
|
604
|
+
manager?: PublicKey;
|
|
605
|
+
},
|
|
606
|
+
uiTxParams?: TxParams
|
|
607
|
+
): Promise<TransactionSignature> {
|
|
608
|
+
const ix = await this.getInitializeVaultIx(params);
|
|
609
|
+
return await this.createAndSendTxn([ix], uiTxParams);
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
public async getInitializeVaultIx(params: {
|
|
613
|
+
name: number[];
|
|
614
|
+
spotMarketIndex: number;
|
|
615
|
+
redeemPeriod: BN;
|
|
616
|
+
maxTokens: BN;
|
|
617
|
+
minDepositAmount: BN;
|
|
618
|
+
managementFee: BN;
|
|
619
|
+
profitShare: number;
|
|
620
|
+
hurdleRate: number;
|
|
621
|
+
permissioned: boolean;
|
|
622
|
+
vaultProtocol?: VaultProtocolParams;
|
|
623
|
+
manager?: PublicKey;
|
|
624
|
+
}): Promise<TransactionInstruction> {
|
|
625
|
+
const { vaultProtocol: vaultProtocolParams, ...vaultParams } = params;
|
|
626
|
+
const vault = getVaultAddressSync(this.program.programId, params.name);
|
|
627
|
+
const tokenAccount = getTokenVaultAddressSync(
|
|
628
|
+
this.program.programId,
|
|
629
|
+
vault
|
|
630
|
+
);
|
|
631
|
+
|
|
632
|
+
const driftState = await this.driftClient.getStatePublicKey();
|
|
633
|
+
const spotMarket = this.driftClient.getSpotMarketAccount(
|
|
634
|
+
params.spotMarketIndex
|
|
635
|
+
);
|
|
636
|
+
if (!spotMarket) {
|
|
637
|
+
throw new Error(
|
|
638
|
+
`Spot market ${params.spotMarketIndex} not found on driftClient`
|
|
639
|
+
);
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
const userStatsKey = getUserStatsAccountPublicKey(
|
|
643
|
+
this.driftClient.program.programId,
|
|
644
|
+
vault
|
|
645
|
+
);
|
|
646
|
+
const userKey = getUserAccountPublicKeySync(
|
|
647
|
+
this.driftClient.program.programId,
|
|
648
|
+
vault
|
|
649
|
+
);
|
|
650
|
+
|
|
651
|
+
const accounts = {
|
|
652
|
+
driftSpotMarket: spotMarket.pubkey,
|
|
653
|
+
driftSpotMarketMint: spotMarket.mint,
|
|
654
|
+
driftUserStats: userStatsKey,
|
|
655
|
+
driftUser: userKey,
|
|
656
|
+
driftState,
|
|
657
|
+
vault,
|
|
658
|
+
tokenAccount,
|
|
659
|
+
driftProgram: this.driftClient.program.programId,
|
|
660
|
+
};
|
|
661
|
+
|
|
662
|
+
if (vaultProtocolParams) {
|
|
663
|
+
const _params: VaultWithProtocolParams = {
|
|
664
|
+
...vaultParams,
|
|
665
|
+
vaultProtocol: vaultProtocolParams,
|
|
666
|
+
};
|
|
667
|
+
|
|
668
|
+
const uiAuthority = this.driftClient.wallet.publicKey;
|
|
669
|
+
const initializeVaultWithProtocolIx = await this.program.methods
|
|
670
|
+
.initializeVaultWithProtocol(_params)
|
|
671
|
+
.accounts({
|
|
672
|
+
...accounts,
|
|
673
|
+
payer: params.manager ?? uiAuthority,
|
|
674
|
+
manager: params.manager ?? uiAuthority,
|
|
675
|
+
})
|
|
676
|
+
.instruction();
|
|
677
|
+
return initializeVaultWithProtocolIx;
|
|
678
|
+
} else {
|
|
679
|
+
const _params: VaultParams = vaultParams;
|
|
680
|
+
|
|
681
|
+
const uiAuthority = this.driftClient.wallet.publicKey;
|
|
682
|
+
const initializeVaultIx = await this.program.methods
|
|
683
|
+
.initializeVault(_params)
|
|
684
|
+
.accounts({
|
|
685
|
+
...accounts,
|
|
686
|
+
payer: params.manager ?? uiAuthority,
|
|
687
|
+
manager: params.manager ?? uiAuthority,
|
|
688
|
+
})
|
|
689
|
+
.instruction();
|
|
690
|
+
return initializeVaultIx;
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
/**
|
|
695
|
+
* Updates the delegate address for a vault. The delegate address will be allowed to trade
|
|
696
|
+
* on behalf of the vault.
|
|
697
|
+
* @param vault vault address to update
|
|
698
|
+
* @param delegate delegate address to update to
|
|
699
|
+
* @returns
|
|
700
|
+
*/
|
|
701
|
+
public async updateDelegate(
|
|
702
|
+
vault: PublicKey,
|
|
703
|
+
delegate: PublicKey,
|
|
704
|
+
uiTxParams?: TxParams
|
|
705
|
+
): Promise<TransactionSignature> {
|
|
706
|
+
const vaultAccount = await this.program.account.vault.fetch(vault);
|
|
707
|
+
const updateDelegateIx = await this.getUpdateDelegateIx(
|
|
708
|
+
vault,
|
|
709
|
+
delegate,
|
|
710
|
+
vaultAccount.user,
|
|
711
|
+
vaultAccount.manager
|
|
712
|
+
);
|
|
713
|
+
return await this.createAndSendTxn([updateDelegateIx], uiTxParams);
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
public async getUpdateDelegateIx(
|
|
717
|
+
vault: PublicKey,
|
|
718
|
+
delegate: PublicKey,
|
|
719
|
+
vaultDriftUser: PublicKey,
|
|
720
|
+
vaultManager: PublicKey
|
|
721
|
+
): Promise<TransactionInstruction> {
|
|
722
|
+
const accounts = {
|
|
723
|
+
vault: vault,
|
|
724
|
+
driftUser: vaultDriftUser,
|
|
725
|
+
driftProgram: this.driftClient.program.programId,
|
|
726
|
+
};
|
|
727
|
+
|
|
728
|
+
return await this.program.methods
|
|
729
|
+
.updateDelegate(delegate)
|
|
730
|
+
.accounts({ ...accounts, manager: vaultManager })
|
|
731
|
+
.instruction();
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
/**
|
|
735
|
+
* Updates the vault margin trading status.
|
|
736
|
+
* @param vault vault address to update
|
|
737
|
+
* @param enabled whether to enable margin trading
|
|
738
|
+
* @returns
|
|
739
|
+
*/
|
|
740
|
+
public async updateMarginTradingEnabled(
|
|
741
|
+
vault: PublicKey,
|
|
742
|
+
enabled: boolean,
|
|
743
|
+
uiTxParams?: TxParams
|
|
744
|
+
): Promise<TransactionSignature> {
|
|
745
|
+
const updateMarginTradingEnabledIx =
|
|
746
|
+
await this.getUpdateMarginTradingEnabledIx(vault, enabled);
|
|
747
|
+
return await this.createAndSendTxn(
|
|
748
|
+
[updateMarginTradingEnabledIx],
|
|
749
|
+
uiTxParams
|
|
750
|
+
);
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
public async getUpdateMarginTradingEnabledIx(
|
|
754
|
+
vault: PublicKey,
|
|
755
|
+
enabled: boolean
|
|
756
|
+
): Promise<TransactionInstruction> {
|
|
757
|
+
const vaultAccount = await this.program.account.vault.fetch(vault);
|
|
758
|
+
const accounts = {
|
|
759
|
+
vault: vault,
|
|
760
|
+
driftUser: vaultAccount.user,
|
|
761
|
+
driftProgram: this.driftClient.program.programId,
|
|
762
|
+
};
|
|
763
|
+
|
|
764
|
+
const user = await this.getSubscribedVaultUser(vaultAccount.user);
|
|
765
|
+
|
|
766
|
+
const remainingAccounts: AccountMeta[] = [];
|
|
767
|
+
try {
|
|
768
|
+
const userStatsKey = getUserStatsAccountPublicKey(
|
|
769
|
+
this.driftClient.program.programId,
|
|
770
|
+
vault
|
|
771
|
+
);
|
|
772
|
+
const driftProgram = this.driftClient.program as any;
|
|
773
|
+
const userStats = (await driftProgram.account.userStats.fetch(
|
|
774
|
+
userStatsKey
|
|
775
|
+
)) as UserStatsAccount;
|
|
776
|
+
remainingAccounts.push(
|
|
777
|
+
...this.getRemainingAccountsForUser(
|
|
778
|
+
[user.getUserAccount()],
|
|
779
|
+
[],
|
|
780
|
+
vaultAccount,
|
|
781
|
+
userStats
|
|
782
|
+
)
|
|
783
|
+
);
|
|
784
|
+
} catch (err) {
|
|
785
|
+
console.error('failed to get remaining accounts', err);
|
|
786
|
+
// do nothing
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
return await this.program.methods
|
|
790
|
+
.updateMarginTradingEnabled(enabled)
|
|
791
|
+
.accounts({ ...accounts, manager: vaultAccount.manager })
|
|
792
|
+
.remainingAccounts(remainingAccounts)
|
|
793
|
+
.instruction();
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
/**
|
|
797
|
+
* Updates the vault's pool id (for isolated pools).
|
|
798
|
+
* @param vault vault address to update
|
|
799
|
+
* @param poolId pool id to update to
|
|
800
|
+
* @returns
|
|
801
|
+
*/
|
|
802
|
+
public async updateUserPoolId(
|
|
803
|
+
vault: PublicKey,
|
|
804
|
+
poolId: number,
|
|
805
|
+
uiTxParams?: TxParams
|
|
806
|
+
): Promise<TransactionSignature> {
|
|
807
|
+
const vaultAccount = await this.program.account.vault.fetch(vault);
|
|
808
|
+
const updatePoolIdIx = await this.getUpdatePoolIdIx(
|
|
809
|
+
vault,
|
|
810
|
+
poolId,
|
|
811
|
+
vaultAccount
|
|
812
|
+
);
|
|
813
|
+
return await this.createAndSendTxn([updatePoolIdIx], uiTxParams);
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
/**
|
|
817
|
+
* Gets the instruction to update the pool id for a vault.
|
|
818
|
+
* @param vault vault address to update
|
|
819
|
+
* @param vaultAccount vault account data (optional, will be fetched if not provided)
|
|
820
|
+
* @param poolId pool id to update to
|
|
821
|
+
* @returns instruction to update pool id
|
|
822
|
+
*/
|
|
823
|
+
public async getUpdatePoolIdIx(
|
|
824
|
+
vault: PublicKey,
|
|
825
|
+
poolId: number,
|
|
826
|
+
vaultAccount?: any
|
|
827
|
+
): Promise<TransactionInstruction> {
|
|
828
|
+
if (!vaultAccount) {
|
|
829
|
+
vaultAccount = await this.program.account.vault.fetch(vault);
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
const accounts = {
|
|
833
|
+
vault: vault,
|
|
834
|
+
driftUser: vaultAccount.user,
|
|
835
|
+
driftProgram: this.driftClient.program.programId,
|
|
836
|
+
};
|
|
837
|
+
|
|
838
|
+
const user = await this.getSubscribedVaultUser(vaultAccount.user);
|
|
839
|
+
|
|
840
|
+
const remainingAccounts: AccountMeta[] = [];
|
|
841
|
+
try {
|
|
842
|
+
const userStatsKey = getUserStatsAccountPublicKey(
|
|
843
|
+
this.driftClient.program.programId,
|
|
844
|
+
vault
|
|
845
|
+
);
|
|
846
|
+
const driftProgram = this.driftClient.program as any;
|
|
847
|
+
const userStats = (await driftProgram.account.userStats.fetch(
|
|
848
|
+
userStatsKey
|
|
849
|
+
)) as UserStatsAccount;
|
|
850
|
+
remainingAccounts.push(
|
|
851
|
+
...this.getRemainingAccountsForUser(
|
|
852
|
+
[user.getUserAccount()],
|
|
853
|
+
[],
|
|
854
|
+
vaultAccount,
|
|
855
|
+
userStats
|
|
856
|
+
)
|
|
857
|
+
);
|
|
858
|
+
} catch (err) {
|
|
859
|
+
console.error('failed to get remaining accounts', err);
|
|
860
|
+
// do nothing
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
return await this.program.methods
|
|
864
|
+
.updateUserPoolId(poolId)
|
|
865
|
+
.accounts({ ...accounts, manager: vaultAccount.manager })
|
|
866
|
+
.remainingAccounts(remainingAccounts)
|
|
867
|
+
.instruction();
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
private async handleWSolMovement(
|
|
871
|
+
amount: BN,
|
|
872
|
+
driftSpotMarket: SpotMarketAccount,
|
|
873
|
+
userTokenAccount: PublicKey
|
|
874
|
+
) {
|
|
875
|
+
const isSolDeposit = driftSpotMarket.mint.equals(WRAPPED_SOL_MINT);
|
|
876
|
+
const preIxs: TransactionInstruction[] = [];
|
|
877
|
+
const postIxs: TransactionInstruction[] = [];
|
|
878
|
+
|
|
879
|
+
if (isSolDeposit) {
|
|
880
|
+
const { ixs: createWSolAccountIxs, pubkey } =
|
|
881
|
+
await this.driftClient.getWrappedSolAccountCreationIxs(amount, true);
|
|
882
|
+
|
|
883
|
+
userTokenAccount = pubkey;
|
|
884
|
+
|
|
885
|
+
preIxs.push(...createWSolAccountIxs);
|
|
886
|
+
postIxs.push(
|
|
887
|
+
createCloseAccountInstruction(
|
|
888
|
+
userTokenAccount,
|
|
889
|
+
this.driftClient.wallet.publicKey,
|
|
890
|
+
this.driftClient.wallet.publicKey,
|
|
891
|
+
[]
|
|
892
|
+
)
|
|
893
|
+
);
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
return { userTokenAccount, preIxs, postIxs };
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
/**
|
|
900
|
+
*
|
|
901
|
+
* @param vault vault address to deposit to
|
|
902
|
+
* @param amount amount to deposit
|
|
903
|
+
* @returns
|
|
904
|
+
*/
|
|
905
|
+
public async managerDeposit(
|
|
906
|
+
vault: PublicKey,
|
|
907
|
+
amount: BN,
|
|
908
|
+
uiTxParams?: TxParams,
|
|
909
|
+
managerTokenAccount?: PublicKey
|
|
910
|
+
): Promise<TransactionSignature> {
|
|
911
|
+
const managerDepositIxs = await this.getManagerDepositIx(
|
|
912
|
+
vault,
|
|
913
|
+
amount,
|
|
914
|
+
managerTokenAccount
|
|
915
|
+
);
|
|
916
|
+
return await this.createAndSendTxn(managerDepositIxs, uiTxParams);
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
/**
|
|
920
|
+
*
|
|
921
|
+
* @param vault vault address to deposit to
|
|
922
|
+
* @param amount amount to deposit
|
|
923
|
+
* @returns
|
|
924
|
+
*/
|
|
925
|
+
public async getManagerDepositIx(
|
|
926
|
+
vault: PublicKey,
|
|
927
|
+
amount: BN,
|
|
928
|
+
managerTokenAccount?: PublicKey
|
|
929
|
+
): Promise<Array<TransactionInstruction>> {
|
|
930
|
+
const vaultAccount = await this.program.account.vault.fetch(vault);
|
|
931
|
+
const driftSpotMarket = this.driftClient.getSpotMarketAccount(
|
|
932
|
+
vaultAccount.spotMarketIndex
|
|
933
|
+
);
|
|
934
|
+
if (!driftSpotMarket) {
|
|
935
|
+
throw new Error(
|
|
936
|
+
`Spot market ${vaultAccount.spotMarketIndex} not found on driftClient`
|
|
937
|
+
);
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
const user = await this.getSubscribedVaultUser(vaultAccount.user);
|
|
941
|
+
const userStatsKey = getUserStatsAccountPublicKey(
|
|
942
|
+
this.driftClient.program.programId,
|
|
943
|
+
vault
|
|
944
|
+
);
|
|
945
|
+
const userStats = (await (
|
|
946
|
+
this.driftClient.program as any
|
|
947
|
+
).account.userStats.fetch(userStatsKey)) as UserStatsAccount;
|
|
948
|
+
const remainingAccounts = this.getRemainingAccountsForUser(
|
|
949
|
+
[user.getUserAccount()],
|
|
950
|
+
[vaultAccount.spotMarketIndex],
|
|
951
|
+
vaultAccount,
|
|
952
|
+
userStats,
|
|
953
|
+
false,
|
|
954
|
+
true,
|
|
955
|
+
true
|
|
956
|
+
);
|
|
957
|
+
|
|
958
|
+
const accounts = {
|
|
959
|
+
vault,
|
|
960
|
+
vaultTokenAccount: vaultAccount.tokenAccount,
|
|
961
|
+
driftUser: await getUserAccountPublicKey(
|
|
962
|
+
this.driftClient.program.programId,
|
|
963
|
+
vault
|
|
964
|
+
),
|
|
965
|
+
driftUserStats: getUserStatsAccountPublicKey(
|
|
966
|
+
this.driftClient.program.programId,
|
|
967
|
+
vault
|
|
968
|
+
),
|
|
969
|
+
driftProgram: this.driftClient.program.programId,
|
|
970
|
+
driftState: await this.driftClient.getStatePublicKey(),
|
|
971
|
+
driftSpotMarketVault: driftSpotMarket.vault,
|
|
972
|
+
userTokenAccount:
|
|
973
|
+
managerTokenAccount ??
|
|
974
|
+
getAssociatedTokenAddressSync(
|
|
975
|
+
driftSpotMarket.mint,
|
|
976
|
+
vaultAccount.manager,
|
|
977
|
+
true
|
|
978
|
+
),
|
|
979
|
+
tokenProgram: TOKEN_PROGRAM_ID,
|
|
980
|
+
};
|
|
981
|
+
|
|
982
|
+
const { userTokenAccount, preIxs, postIxs } = await this.handleWSolMovement(
|
|
983
|
+
amount,
|
|
984
|
+
driftSpotMarket,
|
|
985
|
+
accounts.userTokenAccount
|
|
986
|
+
);
|
|
987
|
+
|
|
988
|
+
const managerDepositIx = await this.program.methods
|
|
989
|
+
.managerDeposit(amount)
|
|
990
|
+
.accounts({
|
|
991
|
+
...accounts,
|
|
992
|
+
userTokenAccount,
|
|
993
|
+
manager: vaultAccount.manager,
|
|
994
|
+
})
|
|
995
|
+
.remainingAccounts(remainingAccounts)
|
|
996
|
+
.instruction();
|
|
997
|
+
return [...preIxs, managerDepositIx, ...postIxs];
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
public async managerRequestWithdraw(
|
|
1001
|
+
vault: PublicKey,
|
|
1002
|
+
amount: BN,
|
|
1003
|
+
withdrawUnit: WithdrawUnit,
|
|
1004
|
+
uiTxParams?: TxParams
|
|
1005
|
+
): Promise<TransactionSignature> {
|
|
1006
|
+
const requestWithdrawIx = await this.getManagerRequestWithdrawIx(
|
|
1007
|
+
vault,
|
|
1008
|
+
amount,
|
|
1009
|
+
withdrawUnit
|
|
1010
|
+
);
|
|
1011
|
+
return await this.createAndSendTxn([requestWithdrawIx], uiTxParams);
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
public async getManagerRequestWithdrawIx(
|
|
1015
|
+
vault: PublicKey,
|
|
1016
|
+
amount: BN,
|
|
1017
|
+
withdrawUnit: WithdrawUnit
|
|
1018
|
+
): Promise<TransactionInstruction> {
|
|
1019
|
+
this.program.idl.types;
|
|
1020
|
+
// @ts-ignore
|
|
1021
|
+
const vaultAccount = (await this.program.account.vault.fetch(
|
|
1022
|
+
vault
|
|
1023
|
+
)) as Vault;
|
|
1024
|
+
|
|
1025
|
+
const user = await this.getSubscribedVaultUser(vaultAccount.user);
|
|
1026
|
+
const userStatsKey = getUserStatsAccountPublicKey(
|
|
1027
|
+
this.driftClient.program.programId,
|
|
1028
|
+
vault
|
|
1029
|
+
);
|
|
1030
|
+
const userStats = (await (
|
|
1031
|
+
this.driftClient.program as any
|
|
1032
|
+
).account.userStats.fetch(userStatsKey)) as UserStatsAccount;
|
|
1033
|
+
const remainingAccounts = this.getRemainingAccountsForUser(
|
|
1034
|
+
[user.getUserAccount()],
|
|
1035
|
+
[vaultAccount.spotMarketIndex],
|
|
1036
|
+
vaultAccount,
|
|
1037
|
+
userStats,
|
|
1038
|
+
false,
|
|
1039
|
+
true,
|
|
1040
|
+
true
|
|
1041
|
+
);
|
|
1042
|
+
|
|
1043
|
+
const accounts = {
|
|
1044
|
+
vault,
|
|
1045
|
+
driftUser: vaultAccount.user,
|
|
1046
|
+
driftUserStats: userStatsKey,
|
|
1047
|
+
};
|
|
1048
|
+
|
|
1049
|
+
return this.program.instruction.managerRequestWithdraw(
|
|
1050
|
+
// @ts-ignore
|
|
1051
|
+
amount,
|
|
1052
|
+
withdrawUnit,
|
|
1053
|
+
{
|
|
1054
|
+
accounts: {
|
|
1055
|
+
manager: vaultAccount.manager,
|
|
1056
|
+
...accounts,
|
|
1057
|
+
},
|
|
1058
|
+
remainingAccounts,
|
|
1059
|
+
}
|
|
1060
|
+
);
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
public async managerCancelWithdrawRequest(
|
|
1064
|
+
vault: PublicKey,
|
|
1065
|
+
uiTxParams?: TxParams
|
|
1066
|
+
): Promise<TransactionSignature> {
|
|
1067
|
+
const ix = await this.getManagerCancelWithdrawRequestIx(vault);
|
|
1068
|
+
return await this.createAndSendTxn([ix], uiTxParams);
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
public async getManagerCancelWithdrawRequestIx(
|
|
1072
|
+
vault: PublicKey
|
|
1073
|
+
): Promise<TransactionInstruction> {
|
|
1074
|
+
const vaultAccount = await this.program.account.vault.fetch(vault);
|
|
1075
|
+
|
|
1076
|
+
const userStatsKey = getUserStatsAccountPublicKey(
|
|
1077
|
+
this.driftClient.program.programId,
|
|
1078
|
+
vault
|
|
1079
|
+
);
|
|
1080
|
+
|
|
1081
|
+
const accounts = {
|
|
1082
|
+
manager: vaultAccount.manager,
|
|
1083
|
+
vault,
|
|
1084
|
+
driftUser: vaultAccount.user,
|
|
1085
|
+
driftUserStats: userStatsKey,
|
|
1086
|
+
};
|
|
1087
|
+
|
|
1088
|
+
const user = await this.getSubscribedVaultUser(vaultAccount.user);
|
|
1089
|
+
const userStats = (await (
|
|
1090
|
+
this.driftClient.program as any
|
|
1091
|
+
).account.userStats.fetch(userStatsKey)) as UserStatsAccount;
|
|
1092
|
+
const remainingAccounts = this.getRemainingAccountsForUser(
|
|
1093
|
+
[user.getUserAccount()],
|
|
1094
|
+
[],
|
|
1095
|
+
vaultAccount,
|
|
1096
|
+
userStats,
|
|
1097
|
+
false,
|
|
1098
|
+
true,
|
|
1099
|
+
true
|
|
1100
|
+
);
|
|
1101
|
+
|
|
1102
|
+
return this.program.instruction.mangerCancelWithdrawRequest({
|
|
1103
|
+
accounts,
|
|
1104
|
+
remainingAccounts,
|
|
1105
|
+
});
|
|
1106
|
+
}
|
|
1107
|
+
|
|
1108
|
+
public async managerWithdraw(
|
|
1109
|
+
vault: PublicKey,
|
|
1110
|
+
uiTxParams?: TxParams
|
|
1111
|
+
): Promise<TransactionSignature> {
|
|
1112
|
+
const ixs = await this.getManagerWithdrawIx(vault);
|
|
1113
|
+
return this.createAndSendTxn(ixs, uiTxParams);
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1116
|
+
public async getManagerWithdrawIx(
|
|
1117
|
+
vault: PublicKey
|
|
1118
|
+
): Promise<TransactionInstruction[]> {
|
|
1119
|
+
const vaultAccount = await this.program.account.vault.fetch(vault);
|
|
1120
|
+
|
|
1121
|
+
const user = await this.getSubscribedVaultUser(vaultAccount.user);
|
|
1122
|
+
const userStatsKey = getUserStatsAccountPublicKey(
|
|
1123
|
+
this.driftClient.program.programId,
|
|
1124
|
+
vault
|
|
1125
|
+
);
|
|
1126
|
+
const userStats = (await (
|
|
1127
|
+
this.driftClient.program as any
|
|
1128
|
+
).account.userStats.fetch(userStatsKey)) as UserStatsAccount;
|
|
1129
|
+
const remainingAccounts = this.getRemainingAccountsForUser(
|
|
1130
|
+
[user.getUserAccount()],
|
|
1131
|
+
[vaultAccount.spotMarketIndex],
|
|
1132
|
+
vaultAccount,
|
|
1133
|
+
userStats,
|
|
1134
|
+
false,
|
|
1135
|
+
false,
|
|
1136
|
+
false
|
|
1137
|
+
);
|
|
1138
|
+
|
|
1139
|
+
const spotMarket = this.driftClient.getSpotMarketAccount(
|
|
1140
|
+
vaultAccount.spotMarketIndex
|
|
1141
|
+
);
|
|
1142
|
+
if (!spotMarket) {
|
|
1143
|
+
throw new Error(
|
|
1144
|
+
`Spot market ${vaultAccount.spotMarketIndex} not found on driftClient`
|
|
1145
|
+
);
|
|
1146
|
+
}
|
|
1147
|
+
|
|
1148
|
+
const isSolMarket = spotMarket.mint.equals(WRAPPED_SOL_MINT);
|
|
1149
|
+
let userAta = getAssociatedTokenAddressSync(
|
|
1150
|
+
spotMarket.mint,
|
|
1151
|
+
vaultAccount.manager,
|
|
1152
|
+
true
|
|
1153
|
+
);
|
|
1154
|
+
|
|
1155
|
+
const preIxs: TransactionInstruction[] = [];
|
|
1156
|
+
const postIxs: TransactionInstruction[] = [];
|
|
1157
|
+
|
|
1158
|
+
if (isSolMarket) {
|
|
1159
|
+
const { ixs, pubkey } =
|
|
1160
|
+
await this.driftClient.getWrappedSolAccountCreationIxs(ZERO, false);
|
|
1161
|
+
|
|
1162
|
+
userAta = pubkey;
|
|
1163
|
+
preIxs.push(...ixs);
|
|
1164
|
+
postIxs.push(createSyncNativeInstruction(userAta));
|
|
1165
|
+
postIxs.push(
|
|
1166
|
+
createCloseAccountInstruction(
|
|
1167
|
+
userAta,
|
|
1168
|
+
vaultAccount.manager,
|
|
1169
|
+
vaultAccount.manager,
|
|
1170
|
+
[]
|
|
1171
|
+
)
|
|
1172
|
+
);
|
|
1173
|
+
} else {
|
|
1174
|
+
const userAtaExists = await this.driftClient.connection.getAccountInfo(
|
|
1175
|
+
userAta
|
|
1176
|
+
);
|
|
1177
|
+
if (userAtaExists === null) {
|
|
1178
|
+
preIxs.push(
|
|
1179
|
+
createAssociatedTokenAccountInstruction(
|
|
1180
|
+
vaultAccount.manager,
|
|
1181
|
+
userAta,
|
|
1182
|
+
vaultAccount.manager,
|
|
1183
|
+
spotMarket.mint
|
|
1184
|
+
)
|
|
1185
|
+
);
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
1188
|
+
|
|
1189
|
+
const withdrawIx = await this.program.instruction.managerWithdraw({
|
|
1190
|
+
accounts: {
|
|
1191
|
+
vault,
|
|
1192
|
+
manager: vaultAccount.manager,
|
|
1193
|
+
vaultTokenAccount: vaultAccount.tokenAccount,
|
|
1194
|
+
driftUser: await getUserAccountPublicKey(
|
|
1195
|
+
this.driftClient.program.programId,
|
|
1196
|
+
vault
|
|
1197
|
+
),
|
|
1198
|
+
driftProgram: this.driftClient.program.programId,
|
|
1199
|
+
driftUserStats: getUserStatsAccountPublicKey(
|
|
1200
|
+
this.driftClient.program.programId,
|
|
1201
|
+
vault
|
|
1202
|
+
),
|
|
1203
|
+
driftState: await this.driftClient.getStatePublicKey(),
|
|
1204
|
+
driftSpotMarketVault: spotMarket.vault,
|
|
1205
|
+
userTokenAccount: userAta,
|
|
1206
|
+
driftSigner: this.driftClient.getStateAccount().signer,
|
|
1207
|
+
tokenProgram: TOKEN_PROGRAM_ID,
|
|
1208
|
+
},
|
|
1209
|
+
remainingAccounts,
|
|
1210
|
+
});
|
|
1211
|
+
|
|
1212
|
+
return [...preIxs, withdrawIx, ...postIxs];
|
|
1213
|
+
}
|
|
1214
|
+
|
|
1215
|
+
public async managerBorrow(
|
|
1216
|
+
vault: PublicKey,
|
|
1217
|
+
borrowSpotMarketIndex: number,
|
|
1218
|
+
borrowAmount: BN,
|
|
1219
|
+
managerTokenAccount?: PublicKey,
|
|
1220
|
+
txParams?: TxParams
|
|
1221
|
+
): Promise<TransactionSignature> {
|
|
1222
|
+
const ixs = await this.getManagerBorrowIx(
|
|
1223
|
+
vault,
|
|
1224
|
+
borrowSpotMarketIndex,
|
|
1225
|
+
borrowAmount,
|
|
1226
|
+
managerTokenAccount
|
|
1227
|
+
);
|
|
1228
|
+
return await this.createAndSendTxn(ixs, txParams);
|
|
1229
|
+
}
|
|
1230
|
+
|
|
1231
|
+
public async getManagerBorrowIx(
|
|
1232
|
+
vault: PublicKey,
|
|
1233
|
+
borrowSpotMarketIndex: number,
|
|
1234
|
+
borrowAmount: BN,
|
|
1235
|
+
managerTokenAccount?: PublicKey
|
|
1236
|
+
): Promise<TransactionInstruction[]> {
|
|
1237
|
+
const vaultAccount = await this.program.account.vault.fetch(vault);
|
|
1238
|
+
|
|
1239
|
+
const spotMarket = this.driftClient.getSpotMarketAccount(
|
|
1240
|
+
borrowSpotMarketIndex
|
|
1241
|
+
);
|
|
1242
|
+
if (!spotMarket) {
|
|
1243
|
+
throw new Error(
|
|
1244
|
+
`Spot market ${borrowSpotMarketIndex} not found on driftClient`
|
|
1245
|
+
);
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1248
|
+
if (!managerTokenAccount) {
|
|
1249
|
+
managerTokenAccount = getAssociatedTokenAddressSync(
|
|
1250
|
+
spotMarket.mint,
|
|
1251
|
+
this.driftClient.wallet.publicKey,
|
|
1252
|
+
true
|
|
1253
|
+
);
|
|
1254
|
+
}
|
|
1255
|
+
|
|
1256
|
+
const user = await this.getSubscribedVaultUser(vaultAccount.user);
|
|
1257
|
+
const userStatsKey = getUserStatsAccountPublicKey(
|
|
1258
|
+
this.driftClient.program.programId,
|
|
1259
|
+
vault
|
|
1260
|
+
);
|
|
1261
|
+
const userStats = (await (
|
|
1262
|
+
this.driftClient.program as any
|
|
1263
|
+
).account.userStats.fetch(userStatsKey)) as UserStatsAccount;
|
|
1264
|
+
const remainingAccounts = this.getRemainingAccountsForUser(
|
|
1265
|
+
[user.getUserAccount()],
|
|
1266
|
+
[borrowSpotMarketIndex, vaultAccount.spotMarketIndex],
|
|
1267
|
+
vaultAccount,
|
|
1268
|
+
userStats,
|
|
1269
|
+
false,
|
|
1270
|
+
false,
|
|
1271
|
+
false
|
|
1272
|
+
);
|
|
1273
|
+
|
|
1274
|
+
const preIxs = [];
|
|
1275
|
+
const postIxs = [];
|
|
1276
|
+
|
|
1277
|
+
const managerTokenAccountExists =
|
|
1278
|
+
await this.driftClient.connection.getAccountInfo(managerTokenAccount);
|
|
1279
|
+
if (managerTokenAccountExists === null) {
|
|
1280
|
+
preIxs.push(
|
|
1281
|
+
createAssociatedTokenAccountInstruction(
|
|
1282
|
+
vaultAccount.manager,
|
|
1283
|
+
managerTokenAccount,
|
|
1284
|
+
vaultAccount.manager,
|
|
1285
|
+
spotMarket.mint
|
|
1286
|
+
)
|
|
1287
|
+
);
|
|
1288
|
+
}
|
|
1289
|
+
|
|
1290
|
+
const vaultBorrowTokenAccount = getAssociatedTokenAddressSync(
|
|
1291
|
+
spotMarket.mint,
|
|
1292
|
+
vault,
|
|
1293
|
+
true
|
|
1294
|
+
);
|
|
1295
|
+
const vaultBorrowTokenAccountExists =
|
|
1296
|
+
await this.driftClient.connection.getAccountInfo(vaultBorrowTokenAccount);
|
|
1297
|
+
if (vaultBorrowTokenAccountExists === null) {
|
|
1298
|
+
preIxs.push(
|
|
1299
|
+
createAssociatedTokenAccountInstruction(
|
|
1300
|
+
this.driftClient.wallet.publicKey,
|
|
1301
|
+
vaultBorrowTokenAccount,
|
|
1302
|
+
vault,
|
|
1303
|
+
spotMarket.mint
|
|
1304
|
+
)
|
|
1305
|
+
);
|
|
1306
|
+
}
|
|
1307
|
+
|
|
1308
|
+
if (spotMarket.mint.equals(WRAPPED_SOL_MINT)) {
|
|
1309
|
+
postIxs.push(
|
|
1310
|
+
createCloseAccountInstruction(
|
|
1311
|
+
managerTokenAccount,
|
|
1312
|
+
vaultAccount.manager,
|
|
1313
|
+
vaultAccount.manager,
|
|
1314
|
+
[]
|
|
1315
|
+
)
|
|
1316
|
+
);
|
|
1317
|
+
}
|
|
1318
|
+
|
|
1319
|
+
return [
|
|
1320
|
+
...preIxs,
|
|
1321
|
+
await this.program.methods
|
|
1322
|
+
.managerBorrow(borrowSpotMarketIndex, borrowAmount)
|
|
1323
|
+
.accounts({
|
|
1324
|
+
vault,
|
|
1325
|
+
vaultTokenAccount: vaultBorrowTokenAccount,
|
|
1326
|
+
manager: vaultAccount.manager,
|
|
1327
|
+
driftUserStats: userStatsKey,
|
|
1328
|
+
driftUser: vaultAccount.user,
|
|
1329
|
+
driftState: await this.driftClient.getStatePublicKey(),
|
|
1330
|
+
driftSpotMarketVault: spotMarket.vault,
|
|
1331
|
+
driftSigner: this.driftClient.getStateAccount().signer,
|
|
1332
|
+
userTokenAccount: managerTokenAccount,
|
|
1333
|
+
})
|
|
1334
|
+
.remainingAccounts(remainingAccounts)
|
|
1335
|
+
.instruction(),
|
|
1336
|
+
...postIxs,
|
|
1337
|
+
];
|
|
1338
|
+
}
|
|
1339
|
+
|
|
1340
|
+
public async managerRepay(
|
|
1341
|
+
vault: PublicKey,
|
|
1342
|
+
repaySpotMarketIndex: number,
|
|
1343
|
+
repayAmount: BN,
|
|
1344
|
+
repayValue: BN | null,
|
|
1345
|
+
managerTokenAccount?: PublicKey,
|
|
1346
|
+
uiTxParams?: TxParams
|
|
1347
|
+
): Promise<TransactionSignature> {
|
|
1348
|
+
const ixs = await this.getManagerRepayIxs(
|
|
1349
|
+
vault,
|
|
1350
|
+
repaySpotMarketIndex,
|
|
1351
|
+
repayAmount,
|
|
1352
|
+
repayValue,
|
|
1353
|
+
managerTokenAccount
|
|
1354
|
+
);
|
|
1355
|
+
return this.createAndSendTxn(ixs, uiTxParams);
|
|
1356
|
+
}
|
|
1357
|
+
|
|
1358
|
+
/**
|
|
1359
|
+
* Get the instructions for the manager repay transaction
|
|
1360
|
+
* @param vault - The vault to repay
|
|
1361
|
+
* @param repaySpotMarketIndex - The spot market index to repay
|
|
1362
|
+
* @param repayAmount - The amount to repay
|
|
1363
|
+
* @param repayValue - The value of the repay
|
|
1364
|
+
* @param managerTokenAccount - The manager token account to use, if depositing SOL, leave undefined to automatically wrap the SOL
|
|
1365
|
+
* @returns The instructions for the manager repay transaction
|
|
1366
|
+
*/
|
|
1367
|
+
public async getManagerRepayIxs(
|
|
1368
|
+
vault: PublicKey,
|
|
1369
|
+
repaySpotMarketIndex: number,
|
|
1370
|
+
repayAmount: BN,
|
|
1371
|
+
repayValue: BN | null,
|
|
1372
|
+
managerTokenAccount?: PublicKey
|
|
1373
|
+
): Promise<TransactionInstruction[]> {
|
|
1374
|
+
const vaultAccount = await this.program.account.vault.fetch(vault);
|
|
1375
|
+
const spotMarket =
|
|
1376
|
+
this.driftClient.getSpotMarketAccount(repaySpotMarketIndex);
|
|
1377
|
+
if (!spotMarket) {
|
|
1378
|
+
throw new Error(
|
|
1379
|
+
`Spot market ${repaySpotMarketIndex} not found on driftClient`
|
|
1380
|
+
);
|
|
1381
|
+
}
|
|
1382
|
+
const isSolMarket = spotMarket.mint.equals(WRAPPED_SOL_MINT);
|
|
1383
|
+
|
|
1384
|
+
const preIxs: TransactionInstruction[] = [];
|
|
1385
|
+
const postIxs: TransactionInstruction[] = [];
|
|
1386
|
+
let createdWsolAccount = false;
|
|
1387
|
+
|
|
1388
|
+
if (!managerTokenAccount) {
|
|
1389
|
+
if (isSolMarket) {
|
|
1390
|
+
// create wSOL
|
|
1391
|
+
const { ixs, pubkey } =
|
|
1392
|
+
await this.driftClient.getWrappedSolAccountCreationIxs(
|
|
1393
|
+
repayAmount,
|
|
1394
|
+
true
|
|
1395
|
+
);
|
|
1396
|
+
preIxs.push(...ixs);
|
|
1397
|
+
managerTokenAccount = pubkey;
|
|
1398
|
+
createdWsolAccount = true;
|
|
1399
|
+
} else {
|
|
1400
|
+
managerTokenAccount = getAssociatedTokenAddressSync(
|
|
1401
|
+
spotMarket.mint,
|
|
1402
|
+
vaultAccount.manager,
|
|
1403
|
+
true
|
|
1404
|
+
);
|
|
1405
|
+
}
|
|
1406
|
+
}
|
|
1407
|
+
|
|
1408
|
+
const vaultRepayTokenAccount = getAssociatedTokenAddressSync(
|
|
1409
|
+
spotMarket.mint,
|
|
1410
|
+
vault,
|
|
1411
|
+
true
|
|
1412
|
+
);
|
|
1413
|
+
const vaultRepayTokenAccountExists =
|
|
1414
|
+
await this.driftClient.connection.getAccountInfo(vaultRepayTokenAccount);
|
|
1415
|
+
if (vaultRepayTokenAccountExists === null) {
|
|
1416
|
+
preIxs.push(
|
|
1417
|
+
createAssociatedTokenAccountInstruction(
|
|
1418
|
+
this.driftClient.wallet.publicKey,
|
|
1419
|
+
vaultRepayTokenAccount,
|
|
1420
|
+
vault,
|
|
1421
|
+
spotMarket.mint
|
|
1422
|
+
)
|
|
1423
|
+
);
|
|
1424
|
+
}
|
|
1425
|
+
|
|
1426
|
+
if (createdWsolAccount) {
|
|
1427
|
+
postIxs.push(
|
|
1428
|
+
createCloseAccountInstruction(
|
|
1429
|
+
managerTokenAccount,
|
|
1430
|
+
vaultAccount.manager,
|
|
1431
|
+
vaultAccount.manager,
|
|
1432
|
+
[]
|
|
1433
|
+
)
|
|
1434
|
+
);
|
|
1435
|
+
}
|
|
1436
|
+
|
|
1437
|
+
const userStatsKey = getUserStatsAccountPublicKey(
|
|
1438
|
+
this.driftClient.program.programId,
|
|
1439
|
+
vault
|
|
1440
|
+
);
|
|
1441
|
+
const user = await this.getSubscribedVaultUser(vaultAccount.user);
|
|
1442
|
+
const userStats = (await (
|
|
1443
|
+
this.driftClient.program as any
|
|
1444
|
+
).account.userStats.fetch(userStatsKey)) as UserStatsAccount;
|
|
1445
|
+
const remainingAccounts = this.getRemainingAccountsForUser(
|
|
1446
|
+
[user.getUserAccount()],
|
|
1447
|
+
[repaySpotMarketIndex, vaultAccount.spotMarketIndex],
|
|
1448
|
+
vaultAccount,
|
|
1449
|
+
userStats,
|
|
1450
|
+
false,
|
|
1451
|
+
false,
|
|
1452
|
+
false
|
|
1453
|
+
);
|
|
1454
|
+
|
|
1455
|
+
return [
|
|
1456
|
+
...preIxs,
|
|
1457
|
+
await this.program.methods
|
|
1458
|
+
.managerRepay(repaySpotMarketIndex, repayAmount, repayValue)
|
|
1459
|
+
.accounts({
|
|
1460
|
+
vault,
|
|
1461
|
+
vaultTokenAccount: vaultRepayTokenAccount,
|
|
1462
|
+
manager: vaultAccount.manager,
|
|
1463
|
+
driftUserStats: userStatsKey,
|
|
1464
|
+
driftUser: vaultAccount.user,
|
|
1465
|
+
driftState: await this.driftClient.getStatePublicKey(),
|
|
1466
|
+
driftSpotMarketVault: spotMarket.vault,
|
|
1467
|
+
driftSigner: this.driftClient.getStateAccount().signer,
|
|
1468
|
+
userTokenAccount: managerTokenAccount,
|
|
1469
|
+
})
|
|
1470
|
+
.remainingAccounts(remainingAccounts)
|
|
1471
|
+
.instruction(),
|
|
1472
|
+
...postIxs,
|
|
1473
|
+
];
|
|
1474
|
+
}
|
|
1475
|
+
|
|
1476
|
+
public async managerUpdateBorrow(
|
|
1477
|
+
vault: PublicKey,
|
|
1478
|
+
newBorrowValue: BN,
|
|
1479
|
+
txParams?: TxParams
|
|
1480
|
+
): Promise<TransactionSignature> {
|
|
1481
|
+
const ix = await this.getManagerUpdateBorrowIx(vault, newBorrowValue);
|
|
1482
|
+
return await this.createAndSendTxn([ix], txParams);
|
|
1483
|
+
}
|
|
1484
|
+
|
|
1485
|
+
public async getManagerUpdateBorrowIx(
|
|
1486
|
+
vault: PublicKey,
|
|
1487
|
+
newBorrowValue: BN
|
|
1488
|
+
): Promise<TransactionInstruction> {
|
|
1489
|
+
const vaultAccount = await this.program.account.vault.fetch(vault);
|
|
1490
|
+
|
|
1491
|
+
const user = await this.getSubscribedVaultUser(vaultAccount.user);
|
|
1492
|
+
const userStatsKey = getUserStatsAccountPublicKey(
|
|
1493
|
+
this.driftClient.program.programId,
|
|
1494
|
+
vault
|
|
1495
|
+
);
|
|
1496
|
+
const userStats = (await (
|
|
1497
|
+
this.driftClient.program as any
|
|
1498
|
+
).account.userStats.fetch(userStatsKey)) as UserStatsAccount;
|
|
1499
|
+
const remainingAccounts = this.getRemainingAccountsForUser(
|
|
1500
|
+
[user.getUserAccount()],
|
|
1501
|
+
[],
|
|
1502
|
+
vaultAccount,
|
|
1503
|
+
userStats,
|
|
1504
|
+
false,
|
|
1505
|
+
false,
|
|
1506
|
+
false
|
|
1507
|
+
);
|
|
1508
|
+
|
|
1509
|
+
return this.program.instruction.managerUpdateBorrow(newBorrowValue, {
|
|
1510
|
+
accounts: {
|
|
1511
|
+
vault,
|
|
1512
|
+
manager: vaultAccount.manager,
|
|
1513
|
+
driftUserStats: userStatsKey,
|
|
1514
|
+
driftUser: vaultAccount.user,
|
|
1515
|
+
},
|
|
1516
|
+
remainingAccounts,
|
|
1517
|
+
});
|
|
1518
|
+
}
|
|
1519
|
+
|
|
1520
|
+
public async managerUpdateVault(
|
|
1521
|
+
vault: PublicKey,
|
|
1522
|
+
params: {
|
|
1523
|
+
redeemPeriod: BN | null;
|
|
1524
|
+
maxTokens: BN | null;
|
|
1525
|
+
managementFee: BN | null;
|
|
1526
|
+
minDepositAmount: BN | null;
|
|
1527
|
+
profitShare: number | null;
|
|
1528
|
+
hurdleRate: number | null;
|
|
1529
|
+
permissioned: boolean | null;
|
|
1530
|
+
},
|
|
1531
|
+
uiTxParams?: TxParams
|
|
1532
|
+
): Promise<TransactionSignature> {
|
|
1533
|
+
const ix = await this.getManagerUpdateVaultIx(vault, params);
|
|
1534
|
+
return this.createAndSendTxn([ix], uiTxParams);
|
|
1535
|
+
}
|
|
1536
|
+
|
|
1537
|
+
public async getManagerUpdateVaultIx(
|
|
1538
|
+
vault: PublicKey,
|
|
1539
|
+
params: {
|
|
1540
|
+
redeemPeriod: BN | null;
|
|
1541
|
+
maxTokens: BN | null;
|
|
1542
|
+
managementFee: BN | null;
|
|
1543
|
+
minDepositAmount: BN | null;
|
|
1544
|
+
profitShare: number | null;
|
|
1545
|
+
hurdleRate: number | null;
|
|
1546
|
+
permissioned: boolean | null;
|
|
1547
|
+
}
|
|
1548
|
+
): Promise<TransactionInstruction> {
|
|
1549
|
+
const vaultAccount = await this.program.account.vault.fetch(vault);
|
|
1550
|
+
return this.program.instruction.updateVault(params, {
|
|
1551
|
+
accounts: {
|
|
1552
|
+
vault,
|
|
1553
|
+
manager: vaultAccount.manager,
|
|
1554
|
+
},
|
|
1555
|
+
});
|
|
1556
|
+
}
|
|
1557
|
+
|
|
1558
|
+
public async managerUpdateVaultManager(
|
|
1559
|
+
vault: PublicKey,
|
|
1560
|
+
manager: PublicKey,
|
|
1561
|
+
uiTxParams?: TxParams
|
|
1562
|
+
): Promise<TransactionSignature> {
|
|
1563
|
+
const ix = await this.getManagerUpdateVaultManagerIx(vault, manager);
|
|
1564
|
+
return this.createAndSendTxn([ix], uiTxParams);
|
|
1565
|
+
}
|
|
1566
|
+
|
|
1567
|
+
public async getManagerUpdateVaultManagerIx(
|
|
1568
|
+
vault: PublicKey,
|
|
1569
|
+
manager: PublicKey
|
|
1570
|
+
): Promise<TransactionInstruction> {
|
|
1571
|
+
const vaultAccount = await this.program.account.vault.fetch(vault);
|
|
1572
|
+
return this.program.instruction.updateVaultManager(manager, {
|
|
1573
|
+
accounts: {
|
|
1574
|
+
vault,
|
|
1575
|
+
manager: vaultAccount.manager,
|
|
1576
|
+
},
|
|
1577
|
+
});
|
|
1578
|
+
}
|
|
1579
|
+
|
|
1580
|
+
public async applyProfitShare(
|
|
1581
|
+
vault: PublicKey,
|
|
1582
|
+
vaultDepositor: PublicKey,
|
|
1583
|
+
uiTxParams?: TxParams
|
|
1584
|
+
): Promise<TransactionSignature> {
|
|
1585
|
+
const ix = await this.getApplyProfitShareIx(vault, vaultDepositor);
|
|
1586
|
+
return this.createAndSendTxn([ix], uiTxParams);
|
|
1587
|
+
}
|
|
1588
|
+
|
|
1589
|
+
public async getApplyProfitShareIx(
|
|
1590
|
+
vault: PublicKey,
|
|
1591
|
+
vaultDepositor: PublicKey
|
|
1592
|
+
): Promise<TransactionInstruction> {
|
|
1593
|
+
const vaultAccount = await this.program.account.vault.fetch(vault);
|
|
1594
|
+
|
|
1595
|
+
const user = await this.getSubscribedVaultUser(vaultAccount.user);
|
|
1596
|
+
|
|
1597
|
+
const spotMarket = this.driftClient.getSpotMarketAccount(
|
|
1598
|
+
vaultAccount.spotMarketIndex
|
|
1599
|
+
);
|
|
1600
|
+
if (!spotMarket) {
|
|
1601
|
+
throw new Error(
|
|
1602
|
+
`Spot market ${vaultAccount.spotMarketIndex} not found on driftClient`
|
|
1603
|
+
);
|
|
1604
|
+
}
|
|
1605
|
+
|
|
1606
|
+
const userStatsKey = getUserStatsAccountPublicKey(
|
|
1607
|
+
this.driftClient.program.programId,
|
|
1608
|
+
vault
|
|
1609
|
+
);
|
|
1610
|
+
const userStats = (await (
|
|
1611
|
+
this.driftClient.program as any
|
|
1612
|
+
).account.userStats.fetch(userStatsKey)) as UserStatsAccount;
|
|
1613
|
+
const remainingAccounts = this.getRemainingAccountsForUser(
|
|
1614
|
+
[user.getUserAccount()],
|
|
1615
|
+
[vaultAccount.spotMarketIndex],
|
|
1616
|
+
vaultAccount,
|
|
1617
|
+
userStats,
|
|
1618
|
+
false,
|
|
1619
|
+
false,
|
|
1620
|
+
false
|
|
1621
|
+
);
|
|
1622
|
+
|
|
1623
|
+
const accounts = {
|
|
1624
|
+
vault,
|
|
1625
|
+
vaultDepositor,
|
|
1626
|
+
manager: vaultAccount.manager,
|
|
1627
|
+
driftUserStats: getUserStatsAccountPublicKey(
|
|
1628
|
+
this.driftClient.program.programId,
|
|
1629
|
+
vault
|
|
1630
|
+
),
|
|
1631
|
+
driftUser: await getUserAccountPublicKey(
|
|
1632
|
+
this.driftClient.program.programId,
|
|
1633
|
+
vault
|
|
1634
|
+
),
|
|
1635
|
+
driftState: await this.driftClient.getStatePublicKey(),
|
|
1636
|
+
driftSigner: this.driftClient.getStateAccount().signer,
|
|
1637
|
+
driftProgram: this.driftClient.program.programId,
|
|
1638
|
+
};
|
|
1639
|
+
|
|
1640
|
+
return this.program.instruction.applyProfitShare({
|
|
1641
|
+
accounts: {
|
|
1642
|
+
...accounts,
|
|
1643
|
+
},
|
|
1644
|
+
remainingAccounts,
|
|
1645
|
+
});
|
|
1646
|
+
}
|
|
1647
|
+
|
|
1648
|
+
public async getApplyRebaseTokenizedDepositorIx(
|
|
1649
|
+
vault: PublicKey,
|
|
1650
|
+
tokenizedVaultDepositor: PublicKey
|
|
1651
|
+
): Promise<TransactionInstruction> {
|
|
1652
|
+
const vaultAccount = await this.program.account.vault.fetch(vault);
|
|
1653
|
+
|
|
1654
|
+
const user = await this.getSubscribedVaultUser(vaultAccount.user);
|
|
1655
|
+
|
|
1656
|
+
const spotMarket = this.driftClient.getSpotMarketAccount(
|
|
1657
|
+
vaultAccount.spotMarketIndex
|
|
1658
|
+
);
|
|
1659
|
+
if (!spotMarket) {
|
|
1660
|
+
throw new Error(
|
|
1661
|
+
`Spot market ${vaultAccount.spotMarketIndex} not found on driftClient`
|
|
1662
|
+
);
|
|
1663
|
+
}
|
|
1664
|
+
|
|
1665
|
+
const userStatsKey = getUserStatsAccountPublicKey(
|
|
1666
|
+
this.driftClient.program.programId,
|
|
1667
|
+
vault
|
|
1668
|
+
);
|
|
1669
|
+
const userStats = (await (
|
|
1670
|
+
this.driftClient.program as any
|
|
1671
|
+
).account.userStats.fetch(userStatsKey)) as UserStatsAccount;
|
|
1672
|
+
const remainingAccounts = this.getRemainingAccountsForUser(
|
|
1673
|
+
[user.getUserAccount()],
|
|
1674
|
+
[vaultAccount.spotMarketIndex],
|
|
1675
|
+
vaultAccount,
|
|
1676
|
+
userStats,
|
|
1677
|
+
false,
|
|
1678
|
+
true,
|
|
1679
|
+
true
|
|
1680
|
+
);
|
|
1681
|
+
|
|
1682
|
+
const accounts = {
|
|
1683
|
+
vault,
|
|
1684
|
+
tokenizedVaultDepositor,
|
|
1685
|
+
driftUser: await getUserAccountPublicKey(
|
|
1686
|
+
this.driftClient.program.programId,
|
|
1687
|
+
vault
|
|
1688
|
+
),
|
|
1689
|
+
driftState: await this.driftClient.getStatePublicKey(),
|
|
1690
|
+
driftSigner: this.driftClient.getStateAccount().signer,
|
|
1691
|
+
driftProgram: this.driftClient.program.programId,
|
|
1692
|
+
};
|
|
1693
|
+
|
|
1694
|
+
return this.program.instruction.applyRebaseTokenizedDepositor({
|
|
1695
|
+
accounts: {
|
|
1696
|
+
...accounts,
|
|
1697
|
+
},
|
|
1698
|
+
remainingAccounts,
|
|
1699
|
+
});
|
|
1700
|
+
}
|
|
1701
|
+
|
|
1702
|
+
public async applyRebase(
|
|
1703
|
+
vault: PublicKey,
|
|
1704
|
+
vaultDepositor: PublicKey
|
|
1705
|
+
): Promise<TransactionSignature> {
|
|
1706
|
+
return await this.createAndSendTxn([
|
|
1707
|
+
await this.getApplyRebaseIx(vault, vaultDepositor),
|
|
1708
|
+
]);
|
|
1709
|
+
}
|
|
1710
|
+
|
|
1711
|
+
public async getApplyRebaseIx(
|
|
1712
|
+
vault: PublicKey,
|
|
1713
|
+
vaultDepositor: PublicKey
|
|
1714
|
+
): Promise<TransactionInstruction> {
|
|
1715
|
+
const vaultAccount = await this.program.account.vault.fetch(vault);
|
|
1716
|
+
|
|
1717
|
+
const user = await this.getSubscribedVaultUser(vaultAccount.user);
|
|
1718
|
+
|
|
1719
|
+
const spotMarket = this.driftClient.getSpotMarketAccount(
|
|
1720
|
+
vaultAccount.spotMarketIndex
|
|
1721
|
+
);
|
|
1722
|
+
if (!spotMarket) {
|
|
1723
|
+
throw new Error(
|
|
1724
|
+
`Spot market ${vaultAccount.spotMarketIndex} not found on driftClient`
|
|
1725
|
+
);
|
|
1726
|
+
}
|
|
1727
|
+
|
|
1728
|
+
const userStatsKey = getUserStatsAccountPublicKey(
|
|
1729
|
+
this.driftClient.program.programId,
|
|
1730
|
+
vault
|
|
1731
|
+
);
|
|
1732
|
+
const userStats = (await (
|
|
1733
|
+
this.driftClient.program as any
|
|
1734
|
+
).account.userStats.fetch(userStatsKey)) as UserStatsAccount;
|
|
1735
|
+
const remainingAccounts = this.getRemainingAccountsForUser(
|
|
1736
|
+
[user.getUserAccount()],
|
|
1737
|
+
[vaultAccount.spotMarketIndex],
|
|
1738
|
+
vaultAccount,
|
|
1739
|
+
userStats,
|
|
1740
|
+
false,
|
|
1741
|
+
true,
|
|
1742
|
+
true
|
|
1743
|
+
);
|
|
1744
|
+
|
|
1745
|
+
const accounts = {
|
|
1746
|
+
vault,
|
|
1747
|
+
vaultDepositor,
|
|
1748
|
+
driftUser: await getUserAccountPublicKey(
|
|
1749
|
+
this.driftClient.program.programId,
|
|
1750
|
+
vault
|
|
1751
|
+
),
|
|
1752
|
+
driftState: await this.driftClient.getStatePublicKey(),
|
|
1753
|
+
driftSigner: this.driftClient.getStateAccount().signer,
|
|
1754
|
+
driftProgram: this.driftClient.program.programId,
|
|
1755
|
+
};
|
|
1756
|
+
|
|
1757
|
+
return this.program.instruction.applyRebase({
|
|
1758
|
+
accounts: {
|
|
1759
|
+
...accounts,
|
|
1760
|
+
},
|
|
1761
|
+
remainingAccounts,
|
|
1762
|
+
});
|
|
1763
|
+
}
|
|
1764
|
+
|
|
1765
|
+
public async applyRebaseTokenizedDepositor(
|
|
1766
|
+
vault: PublicKey,
|
|
1767
|
+
tokenizedVaultDepositor: PublicKey
|
|
1768
|
+
): Promise<TransactionSignature> {
|
|
1769
|
+
return await this.createAndSendTxn([
|
|
1770
|
+
await this.getApplyRebaseTokenizedDepositorIx(
|
|
1771
|
+
vault,
|
|
1772
|
+
tokenizedVaultDepositor
|
|
1773
|
+
),
|
|
1774
|
+
]);
|
|
1775
|
+
}
|
|
1776
|
+
|
|
1777
|
+
private createInitVaultDepositorIx(
|
|
1778
|
+
vault: PublicKey,
|
|
1779
|
+
authority?: PublicKey,
|
|
1780
|
+
payer?: PublicKey
|
|
1781
|
+
) {
|
|
1782
|
+
const vaultDepositor = getVaultDepositorAddressSync(
|
|
1783
|
+
this.program.programId,
|
|
1784
|
+
vault,
|
|
1785
|
+
authority || this.driftClient.wallet.publicKey
|
|
1786
|
+
);
|
|
1787
|
+
|
|
1788
|
+
const accounts = {
|
|
1789
|
+
vaultDepositor,
|
|
1790
|
+
vault,
|
|
1791
|
+
authority: authority || this.driftClient.wallet.publicKey,
|
|
1792
|
+
};
|
|
1793
|
+
|
|
1794
|
+
const initIx = this.program.instruction.initializeVaultDepositor({
|
|
1795
|
+
accounts: {
|
|
1796
|
+
...accounts,
|
|
1797
|
+
payer: payer || authority || this.driftClient.wallet.publicKey,
|
|
1798
|
+
rent: SYSVAR_RENT_PUBKEY,
|
|
1799
|
+
systemProgram: SystemProgram.programId,
|
|
1800
|
+
},
|
|
1801
|
+
});
|
|
1802
|
+
|
|
1803
|
+
return initIx;
|
|
1804
|
+
}
|
|
1805
|
+
|
|
1806
|
+
/**
|
|
1807
|
+
* Initializes the vault depositor account. This account is used to deposit funds into a vault.
|
|
1808
|
+
* @param vault the vault address to deposit into
|
|
1809
|
+
* @param authority the authority allowed to make deposits into the vault
|
|
1810
|
+
* @returns
|
|
1811
|
+
*/
|
|
1812
|
+
public async initializeVaultDepositor(
|
|
1813
|
+
vault: PublicKey,
|
|
1814
|
+
authority?: PublicKey,
|
|
1815
|
+
payer?: PublicKey,
|
|
1816
|
+
uiTxParams?: TxParams
|
|
1817
|
+
): Promise<TransactionSignature> {
|
|
1818
|
+
const initIx = this.createInitVaultDepositorIx(vault, authority, payer);
|
|
1819
|
+
return await this.createAndSendTxn([initIx], uiTxParams);
|
|
1820
|
+
}
|
|
1821
|
+
|
|
1822
|
+
public async initializeTokenizedVaultDepositor(
|
|
1823
|
+
params: {
|
|
1824
|
+
vault: PublicKey;
|
|
1825
|
+
tokenName: string;
|
|
1826
|
+
tokenSymbol: string;
|
|
1827
|
+
tokenUri: string;
|
|
1828
|
+
decimals?: number;
|
|
1829
|
+
sharesBase?: number;
|
|
1830
|
+
},
|
|
1831
|
+
uiTxParams?: TxParams
|
|
1832
|
+
): Promise<TransactionSignature> {
|
|
1833
|
+
if (!this.metaplex) {
|
|
1834
|
+
throw new Error(
|
|
1835
|
+
'Metaplex instance is required when constructing VaultClient to initialize a tokenized vault depositor'
|
|
1836
|
+
);
|
|
1837
|
+
}
|
|
1838
|
+
|
|
1839
|
+
let spotMarketDecimals = 6;
|
|
1840
|
+
let sharesBase = 0;
|
|
1841
|
+
if (params.decimals === undefined || params.sharesBase === undefined) {
|
|
1842
|
+
const vault = await this.program.account.vault.fetch(params.vault);
|
|
1843
|
+
const spotMarketAccount = this.driftClient.getSpotMarketAccount(
|
|
1844
|
+
vault.spotMarketIndex
|
|
1845
|
+
);
|
|
1846
|
+
if (!spotMarketAccount) {
|
|
1847
|
+
throw new Error(
|
|
1848
|
+
`DriftClient failed to load vault's spot market (marketIndex: ${vault.spotMarketIndex})`
|
|
1849
|
+
);
|
|
1850
|
+
}
|
|
1851
|
+
spotMarketDecimals = spotMarketAccount.decimals;
|
|
1852
|
+
sharesBase = vault.sharesBase;
|
|
1853
|
+
}
|
|
1854
|
+
|
|
1855
|
+
const mintAddress = getTokenizedVaultMintAddressSync(
|
|
1856
|
+
this.program.programId,
|
|
1857
|
+
params.vault,
|
|
1858
|
+
sharesBase
|
|
1859
|
+
);
|
|
1860
|
+
|
|
1861
|
+
const vaultAccount = await this.program.account.vault.fetch(params.vault);
|
|
1862
|
+
|
|
1863
|
+
const accounts = {
|
|
1864
|
+
vault: params.vault,
|
|
1865
|
+
vaultDepositor: getTokenizedVaultAddressSync(
|
|
1866
|
+
this.program.programId,
|
|
1867
|
+
params.vault,
|
|
1868
|
+
sharesBase
|
|
1869
|
+
),
|
|
1870
|
+
mintAccount: mintAddress,
|
|
1871
|
+
metadataAccount: this.metaplex.nfts().pdas().metadata({
|
|
1872
|
+
mint: mintAddress,
|
|
1873
|
+
}),
|
|
1874
|
+
tokenMetadataProgram: this.metaplex.programs().getTokenMetadata().address,
|
|
1875
|
+
payer: vaultAccount.manager,
|
|
1876
|
+
};
|
|
1877
|
+
|
|
1878
|
+
const vaultTokenAta = getAssociatedTokenAddressSync(
|
|
1879
|
+
mintAddress,
|
|
1880
|
+
params.vault,
|
|
1881
|
+
true
|
|
1882
|
+
);
|
|
1883
|
+
const createAtaIx = createAssociatedTokenAccountInstruction(
|
|
1884
|
+
vaultAccount.manager,
|
|
1885
|
+
vaultTokenAta,
|
|
1886
|
+
params.vault,
|
|
1887
|
+
mintAddress
|
|
1888
|
+
);
|
|
1889
|
+
|
|
1890
|
+
return await this.createAndSendTxn(
|
|
1891
|
+
[
|
|
1892
|
+
await this.program.methods
|
|
1893
|
+
.initializeTokenizedVaultDepositor({
|
|
1894
|
+
...params,
|
|
1895
|
+
decimals: params.decimals ?? spotMarketDecimals,
|
|
1896
|
+
})
|
|
1897
|
+
.accounts(accounts)
|
|
1898
|
+
.instruction(),
|
|
1899
|
+
createAtaIx,
|
|
1900
|
+
],
|
|
1901
|
+
uiTxParams
|
|
1902
|
+
);
|
|
1903
|
+
}
|
|
1904
|
+
|
|
1905
|
+
public async createTokenizeSharesIx(
|
|
1906
|
+
vaultDepositor: PublicKey,
|
|
1907
|
+
amount: BN,
|
|
1908
|
+
unit: WithdrawUnit,
|
|
1909
|
+
mint?: PublicKey
|
|
1910
|
+
): Promise<TransactionInstruction[]> {
|
|
1911
|
+
const vaultDepositorAccount =
|
|
1912
|
+
await this.program.account.vaultDepositor.fetch(vaultDepositor);
|
|
1913
|
+
const vaultAccount = await this.program.account.vault.fetch(
|
|
1914
|
+
vaultDepositorAccount.vault
|
|
1915
|
+
);
|
|
1916
|
+
|
|
1917
|
+
mint =
|
|
1918
|
+
mint ??
|
|
1919
|
+
getTokenizedVaultMintAddressSync(
|
|
1920
|
+
this.program.programId,
|
|
1921
|
+
vaultDepositorAccount.vault,
|
|
1922
|
+
vaultAccount.sharesBase
|
|
1923
|
+
);
|
|
1924
|
+
|
|
1925
|
+
const userAta = getAssociatedTokenAddressSync(
|
|
1926
|
+
mint,
|
|
1927
|
+
this.driftClient.wallet.publicKey,
|
|
1928
|
+
true
|
|
1929
|
+
);
|
|
1930
|
+
|
|
1931
|
+
const ixs: TransactionInstruction[] = [];
|
|
1932
|
+
|
|
1933
|
+
const userAtaExists = await this.driftClient.connection.getAccountInfo(
|
|
1934
|
+
userAta
|
|
1935
|
+
);
|
|
1936
|
+
if (userAtaExists === null) {
|
|
1937
|
+
ixs.push(
|
|
1938
|
+
createAssociatedTokenAccountInstruction(
|
|
1939
|
+
this.driftClient.wallet.publicKey,
|
|
1940
|
+
userAta,
|
|
1941
|
+
this.driftClient.wallet.publicKey,
|
|
1942
|
+
mint
|
|
1943
|
+
)
|
|
1944
|
+
);
|
|
1945
|
+
}
|
|
1946
|
+
|
|
1947
|
+
const user = await this.getSubscribedVaultUser(vaultAccount.user);
|
|
1948
|
+
const userStatsKey = getUserStatsAccountPublicKey(
|
|
1949
|
+
this.driftClient.program.programId,
|
|
1950
|
+
vaultDepositorAccount.vault
|
|
1951
|
+
);
|
|
1952
|
+
const userStats = (await (
|
|
1953
|
+
this.driftClient.program as any
|
|
1954
|
+
).account.userStats.fetch(userStatsKey)) as UserStatsAccount;
|
|
1955
|
+
const remainingAccounts = this.getRemainingAccountsForUser(
|
|
1956
|
+
[user.getUserAccount()],
|
|
1957
|
+
[vaultAccount.spotMarketIndex],
|
|
1958
|
+
vaultAccount,
|
|
1959
|
+
userStats,
|
|
1960
|
+
false,
|
|
1961
|
+
true,
|
|
1962
|
+
true
|
|
1963
|
+
);
|
|
1964
|
+
|
|
1965
|
+
ixs.push(
|
|
1966
|
+
await this.program.methods
|
|
1967
|
+
// anchor idl bug: https://github.com/coral-xyz/anchor/issues/2914
|
|
1968
|
+
// @ts-ignore args tuple vs anchor 0.32 IDL recursion limit
|
|
1969
|
+
.tokenizeShares(amount, unit)
|
|
1970
|
+
// `mint` is auto-resolved from IDL seeds, but anchor 1.0's IDL
|
|
1971
|
+
// generator can't encode `vault.shares_base.to_string().as_bytes()`
|
|
1972
|
+
// and emits broken seeds. Pass it explicitly to override.
|
|
1973
|
+
.accountsPartial({
|
|
1974
|
+
authority: this.driftClient.wallet.publicKey,
|
|
1975
|
+
vault: vaultDepositorAccount.vault,
|
|
1976
|
+
tokenizedVaultDepositor: getTokenizedVaultAddressSync(
|
|
1977
|
+
this.program.programId,
|
|
1978
|
+
vaultDepositorAccount.vault,
|
|
1979
|
+
vaultAccount.sharesBase
|
|
1980
|
+
),
|
|
1981
|
+
mint,
|
|
1982
|
+
userTokenAccount: userAta,
|
|
1983
|
+
driftUser: vaultAccount.user,
|
|
1984
|
+
})
|
|
1985
|
+
.remainingAccounts(remainingAccounts)
|
|
1986
|
+
.instruction()
|
|
1987
|
+
);
|
|
1988
|
+
|
|
1989
|
+
return ixs;
|
|
1990
|
+
}
|
|
1991
|
+
|
|
1992
|
+
public async tokenizeShares(
|
|
1993
|
+
vaultDepositor: PublicKey,
|
|
1994
|
+
amount: BN,
|
|
1995
|
+
unit: WithdrawUnit,
|
|
1996
|
+
mint?: PublicKey,
|
|
1997
|
+
txParams?: TxParams
|
|
1998
|
+
): Promise<TransactionSignature> {
|
|
1999
|
+
const ixs = await this.createTokenizeSharesIx(
|
|
2000
|
+
vaultDepositor,
|
|
2001
|
+
amount,
|
|
2002
|
+
unit,
|
|
2003
|
+
mint
|
|
2004
|
+
);
|
|
2005
|
+
return await this.createAndSendTxn(ixs, txParams);
|
|
2006
|
+
}
|
|
2007
|
+
|
|
2008
|
+
public async createTransferVaultDepositorSharesIx(
|
|
2009
|
+
fromVaultDepositor: PublicKey,
|
|
2010
|
+
toVaultDepositor: PublicKey,
|
|
2011
|
+
amount: BN,
|
|
2012
|
+
withdrawUnit: WithdrawUnit
|
|
2013
|
+
): Promise<TransactionInstruction[]> {
|
|
2014
|
+
const vaultDepositorAccount =
|
|
2015
|
+
await this.program.account.vaultDepositor.fetch(fromVaultDepositor);
|
|
2016
|
+
const vaultAccount = await this.program.account.vault.fetch(
|
|
2017
|
+
vaultDepositorAccount.vault
|
|
2018
|
+
);
|
|
2019
|
+
|
|
2020
|
+
const user = await this.getSubscribedVaultUser(vaultAccount.user);
|
|
2021
|
+
const userStatsKey = getUserStatsAccountPublicKey(
|
|
2022
|
+
this.driftClient.program.programId,
|
|
2023
|
+
vaultDepositorAccount.vault
|
|
2024
|
+
);
|
|
2025
|
+
const userStats = (await (
|
|
2026
|
+
this.driftClient.program as any
|
|
2027
|
+
).account.userStats.fetch(userStatsKey)) as UserStatsAccount;
|
|
2028
|
+
const remainingAccounts = this.getRemainingAccountsForUser(
|
|
2029
|
+
[user.getUserAccount()],
|
|
2030
|
+
[vaultAccount.spotMarketIndex],
|
|
2031
|
+
vaultAccount,
|
|
2032
|
+
userStats,
|
|
2033
|
+
false,
|
|
2034
|
+
true,
|
|
2035
|
+
true
|
|
2036
|
+
);
|
|
2037
|
+
|
|
2038
|
+
const ixs: TransactionInstruction[] = [];
|
|
2039
|
+
|
|
2040
|
+
ixs.push(
|
|
2041
|
+
await this.program.methods
|
|
2042
|
+
// @ts-ignore args tuple vs anchor 0.32 IDL recursion limit
|
|
2043
|
+
.transferVaultDepositorShares(amount, withdrawUnit)
|
|
2044
|
+
.accounts({
|
|
2045
|
+
vault: vaultDepositorAccount.vault,
|
|
2046
|
+
authority: this.driftClient.wallet.publicKey,
|
|
2047
|
+
toVaultDepositor,
|
|
2048
|
+
driftUser: vaultAccount.user,
|
|
2049
|
+
})
|
|
2050
|
+
.remainingAccounts(remainingAccounts)
|
|
2051
|
+
.instruction()
|
|
2052
|
+
);
|
|
2053
|
+
|
|
2054
|
+
return ixs;
|
|
2055
|
+
}
|
|
2056
|
+
|
|
2057
|
+
public async transferVaultDepositorShares(
|
|
2058
|
+
fromVaultDepositor: PublicKey,
|
|
2059
|
+
toVaultDepositor: PublicKey,
|
|
2060
|
+
amount: BN,
|
|
2061
|
+
withdrawUnit: WithdrawUnit,
|
|
2062
|
+
txParams?: TxParams
|
|
2063
|
+
): Promise<TransactionSignature> {
|
|
2064
|
+
const ixs = await this.createTransferVaultDepositorSharesIx(
|
|
2065
|
+
fromVaultDepositor,
|
|
2066
|
+
toVaultDepositor,
|
|
2067
|
+
amount,
|
|
2068
|
+
withdrawUnit
|
|
2069
|
+
);
|
|
2070
|
+
return await this.createAndSendTxn(ixs, txParams);
|
|
2071
|
+
}
|
|
2072
|
+
|
|
2073
|
+
public async createRedeemTokensIx(
|
|
2074
|
+
vaultDepositor: PublicKey,
|
|
2075
|
+
tokensToBurn: BN,
|
|
2076
|
+
sharesBase?: number
|
|
2077
|
+
): Promise<TransactionInstruction> {
|
|
2078
|
+
const vaultDepositorAccount =
|
|
2079
|
+
await this.program.account.vaultDepositor.fetch(vaultDepositor);
|
|
2080
|
+
const vaultAccount = await this.program.account.vault.fetch(
|
|
2081
|
+
vaultDepositorAccount.vault
|
|
2082
|
+
);
|
|
2083
|
+
|
|
2084
|
+
const mint = getTokenizedVaultMintAddressSync(
|
|
2085
|
+
this.program.programId,
|
|
2086
|
+
vaultDepositorAccount.vault,
|
|
2087
|
+
sharesBase ?? vaultAccount.sharesBase
|
|
2088
|
+
);
|
|
2089
|
+
|
|
2090
|
+
const userAta = getAssociatedTokenAddressSync(
|
|
2091
|
+
mint,
|
|
2092
|
+
this.driftClient.wallet.publicKey,
|
|
2093
|
+
true
|
|
2094
|
+
);
|
|
2095
|
+
|
|
2096
|
+
const vaultTokenAta = getAssociatedTokenAddressSync(
|
|
2097
|
+
mint,
|
|
2098
|
+
vaultDepositorAccount.vault,
|
|
2099
|
+
true
|
|
2100
|
+
);
|
|
2101
|
+
|
|
2102
|
+
const user = await this.getSubscribedVaultUser(vaultAccount.user);
|
|
2103
|
+
const userStatsKey = getUserStatsAccountPublicKey(
|
|
2104
|
+
this.driftClient.program.programId,
|
|
2105
|
+
vaultDepositorAccount.vault
|
|
2106
|
+
);
|
|
2107
|
+
const userStats = (await (
|
|
2108
|
+
this.driftClient.program as any
|
|
2109
|
+
).account.userStats.fetch(userStatsKey)) as UserStatsAccount;
|
|
2110
|
+
const remainingAccounts = this.getRemainingAccountsForUser(
|
|
2111
|
+
[user.getUserAccount()],
|
|
2112
|
+
[vaultAccount.spotMarketIndex],
|
|
2113
|
+
vaultAccount,
|
|
2114
|
+
userStats,
|
|
2115
|
+
false,
|
|
2116
|
+
true,
|
|
2117
|
+
true
|
|
2118
|
+
);
|
|
2119
|
+
|
|
2120
|
+
return await this.program.methods
|
|
2121
|
+
.redeemTokens(tokensToBurn)
|
|
2122
|
+
.accounts({
|
|
2123
|
+
authority: this.driftClient.wallet.publicKey,
|
|
2124
|
+
vault: vaultDepositorAccount.vault,
|
|
2125
|
+
tokenizedVaultDepositor: getTokenizedVaultAddressSync(
|
|
2126
|
+
this.program.programId,
|
|
2127
|
+
vaultDepositorAccount.vault,
|
|
2128
|
+
sharesBase ?? vaultAccount.sharesBase
|
|
2129
|
+
),
|
|
2130
|
+
mint,
|
|
2131
|
+
userTokenAccount: userAta,
|
|
2132
|
+
vaultTokenAccount: vaultTokenAta,
|
|
2133
|
+
driftUser: vaultAccount.user,
|
|
2134
|
+
})
|
|
2135
|
+
.remainingAccounts(remainingAccounts)
|
|
2136
|
+
.instruction();
|
|
2137
|
+
}
|
|
2138
|
+
|
|
2139
|
+
/**
|
|
2140
|
+
* Redeems tokens from the vault.
|
|
2141
|
+
* @param vaultDepositor
|
|
2142
|
+
* @param tokensToBurn
|
|
2143
|
+
* @param mint optionally provide a mint, or infer the mint from the current vault share base
|
|
2144
|
+
* @param txParams
|
|
2145
|
+
* @returns
|
|
2146
|
+
*/
|
|
2147
|
+
public async redeemTokens(
|
|
2148
|
+
vaultDepositor: PublicKey,
|
|
2149
|
+
tokensToBurn: BN,
|
|
2150
|
+
sharesBase?: number,
|
|
2151
|
+
txParams?: TxParams
|
|
2152
|
+
): Promise<TransactionSignature> {
|
|
2153
|
+
const ix = await this.createRedeemTokensIx(
|
|
2154
|
+
vaultDepositor,
|
|
2155
|
+
tokensToBurn,
|
|
2156
|
+
sharesBase
|
|
2157
|
+
);
|
|
2158
|
+
return await this.createAndSendTxn([ix], txParams);
|
|
2159
|
+
}
|
|
2160
|
+
|
|
2161
|
+
public async prepDepositTx(
|
|
2162
|
+
vaultDepositor: PublicKey,
|
|
2163
|
+
amount: BN,
|
|
2164
|
+
initVaultDepositor?: {
|
|
2165
|
+
authority: PublicKey;
|
|
2166
|
+
vault: PublicKey;
|
|
2167
|
+
},
|
|
2168
|
+
depositTokenAccount?: PublicKey
|
|
2169
|
+
) {
|
|
2170
|
+
let vaultPubKey: PublicKey;
|
|
2171
|
+
if (initVaultDepositor) {
|
|
2172
|
+
vaultPubKey = initVaultDepositor.vault;
|
|
2173
|
+
} else {
|
|
2174
|
+
const vaultDepositorAccount =
|
|
2175
|
+
await this.program.account.vaultDepositor.fetch(vaultDepositor);
|
|
2176
|
+
vaultPubKey = vaultDepositorAccount.vault;
|
|
2177
|
+
}
|
|
2178
|
+
|
|
2179
|
+
const vaultAccount = await this.program.account.vault.fetch(vaultPubKey);
|
|
2180
|
+
|
|
2181
|
+
const user = await this.getSubscribedVaultUser(vaultAccount.user);
|
|
2182
|
+
const userStatsKey = getUserStatsAccountPublicKey(
|
|
2183
|
+
this.driftClient.program.programId,
|
|
2184
|
+
vaultPubKey
|
|
2185
|
+
);
|
|
2186
|
+
const userStats = (await (
|
|
2187
|
+
this.driftClient.program as any
|
|
2188
|
+
).account.userStats.fetch(userStatsKey)) as UserStatsAccount;
|
|
2189
|
+
const remainingAccounts = this.getRemainingAccountsForUser(
|
|
2190
|
+
[user.getUserAccount()],
|
|
2191
|
+
[vaultAccount.spotMarketIndex],
|
|
2192
|
+
vaultAccount,
|
|
2193
|
+
userStats,
|
|
2194
|
+
false,
|
|
2195
|
+
false,
|
|
2196
|
+
false
|
|
2197
|
+
);
|
|
2198
|
+
|
|
2199
|
+
const driftStateKey = await this.driftClient.getStatePublicKey();
|
|
2200
|
+
|
|
2201
|
+
const spotMarket = this.driftClient.getSpotMarketAccount(
|
|
2202
|
+
vaultAccount.spotMarketIndex
|
|
2203
|
+
);
|
|
2204
|
+
if (!spotMarket) {
|
|
2205
|
+
throw new Error(
|
|
2206
|
+
`Spot market ${vaultAccount.spotMarketIndex} not found on driftClient`
|
|
2207
|
+
);
|
|
2208
|
+
}
|
|
2209
|
+
|
|
2210
|
+
const nonWSolUserTokenAccount =
|
|
2211
|
+
depositTokenAccount ??
|
|
2212
|
+
getAssociatedTokenAddressSync(
|
|
2213
|
+
spotMarket.mint,
|
|
2214
|
+
this.driftClient.wallet.publicKey,
|
|
2215
|
+
true
|
|
2216
|
+
);
|
|
2217
|
+
|
|
2218
|
+
const { userTokenAccount, preIxs, postIxs } = await this.handleWSolMovement(
|
|
2219
|
+
amount,
|
|
2220
|
+
spotMarket,
|
|
2221
|
+
nonWSolUserTokenAccount
|
|
2222
|
+
);
|
|
2223
|
+
|
|
2224
|
+
const accounts = {
|
|
2225
|
+
vault: vaultPubKey,
|
|
2226
|
+
vaultDepositor,
|
|
2227
|
+
vaultTokenAccount: vaultAccount.tokenAccount,
|
|
2228
|
+
driftUserStats: userStatsKey,
|
|
2229
|
+
driftUser: vaultAccount.user,
|
|
2230
|
+
driftState: driftStateKey,
|
|
2231
|
+
driftSpotMarketVault: spotMarket.vault,
|
|
2232
|
+
userTokenAccount: userTokenAccount,
|
|
2233
|
+
driftProgram: this.driftClient.program.programId,
|
|
2234
|
+
tokenProgram: TOKEN_PROGRAM_ID,
|
|
2235
|
+
};
|
|
2236
|
+
|
|
2237
|
+
return {
|
|
2238
|
+
vaultAccount,
|
|
2239
|
+
accounts,
|
|
2240
|
+
remainingAccounts,
|
|
2241
|
+
preIxs,
|
|
2242
|
+
postIxs,
|
|
2243
|
+
};
|
|
2244
|
+
}
|
|
2245
|
+
|
|
2246
|
+
/**
|
|
2247
|
+
* Creates a transaction to deposit funds into the specified vault.
|
|
2248
|
+
* Uses the associated token account of the vault depositor authority and spot market mint,
|
|
2249
|
+
* and assumes it exists before calling this function.
|
|
2250
|
+
* @param vaultDepositor
|
|
2251
|
+
* @param amount
|
|
2252
|
+
* @param initVaultDepositor If true, will initialize the vault depositor account
|
|
2253
|
+
* @returns transaction
|
|
2254
|
+
*/
|
|
2255
|
+
public async createDepositTx(
|
|
2256
|
+
vaultDepositor: PublicKey,
|
|
2257
|
+
amount: BN,
|
|
2258
|
+
initVaultDepositor?: {
|
|
2259
|
+
authority: PublicKey;
|
|
2260
|
+
vault: PublicKey;
|
|
2261
|
+
},
|
|
2262
|
+
txParams?: TxParams,
|
|
2263
|
+
userTokenAccount?: PublicKey
|
|
2264
|
+
): Promise<VersionedTransaction> {
|
|
2265
|
+
const { vaultAccount, accounts, remainingAccounts, preIxs, postIxs } =
|
|
2266
|
+
await this.prepDepositTx(
|
|
2267
|
+
vaultDepositor,
|
|
2268
|
+
amount,
|
|
2269
|
+
initVaultDepositor,
|
|
2270
|
+
userTokenAccount
|
|
2271
|
+
);
|
|
2272
|
+
|
|
2273
|
+
const ixs: TransactionInstruction[] = [];
|
|
2274
|
+
|
|
2275
|
+
if (initVaultDepositor) {
|
|
2276
|
+
ixs.push(
|
|
2277
|
+
this.createInitVaultDepositorIx(
|
|
2278
|
+
vaultAccount.pubkey,
|
|
2279
|
+
initVaultDepositor.authority
|
|
2280
|
+
)
|
|
2281
|
+
);
|
|
2282
|
+
}
|
|
2283
|
+
|
|
2284
|
+
const depositIx = await this.program.methods
|
|
2285
|
+
.deposit(amount)
|
|
2286
|
+
.accounts({
|
|
2287
|
+
authority: this.driftClient.wallet.publicKey,
|
|
2288
|
+
...accounts,
|
|
2289
|
+
})
|
|
2290
|
+
.remainingAccounts(remainingAccounts)
|
|
2291
|
+
.instruction();
|
|
2292
|
+
ixs.push(...preIxs);
|
|
2293
|
+
ixs.push(depositIx);
|
|
2294
|
+
ixs.push(...postIxs);
|
|
2295
|
+
|
|
2296
|
+
if (txParams?.noLut ? txParams.noLut : false) {
|
|
2297
|
+
return await this.createTxnNoLut(ixs, txParams);
|
|
2298
|
+
} else {
|
|
2299
|
+
return await this.createTxn(ixs, txParams);
|
|
2300
|
+
}
|
|
2301
|
+
}
|
|
2302
|
+
|
|
2303
|
+
/**
|
|
2304
|
+
* Depositor funds into the specified vault.
|
|
2305
|
+
* @param vaultDepositor
|
|
2306
|
+
* @param amount
|
|
2307
|
+
* @param initVaultDepositor If true, will initialize the vault depositor account
|
|
2308
|
+
* @param txParams
|
|
2309
|
+
* @returns
|
|
2310
|
+
*/
|
|
2311
|
+
public async deposit(
|
|
2312
|
+
vaultDepositor: PublicKey,
|
|
2313
|
+
amount: BN,
|
|
2314
|
+
initVaultDepositor?: {
|
|
2315
|
+
authority: PublicKey;
|
|
2316
|
+
vault: PublicKey;
|
|
2317
|
+
},
|
|
2318
|
+
txParams?: TxParams,
|
|
2319
|
+
userTokenAccount?: PublicKey
|
|
2320
|
+
): Promise<TransactionSignature> {
|
|
2321
|
+
const depositTxn = await this.createDepositTx(
|
|
2322
|
+
vaultDepositor,
|
|
2323
|
+
amount,
|
|
2324
|
+
initVaultDepositor,
|
|
2325
|
+
txParams,
|
|
2326
|
+
userTokenAccount
|
|
2327
|
+
);
|
|
2328
|
+
|
|
2329
|
+
return this.sendTxn(depositTxn, txParams?.simulateTransaction);
|
|
2330
|
+
}
|
|
2331
|
+
|
|
2332
|
+
public async requestWithdraw(
|
|
2333
|
+
vaultDepositor: PublicKey,
|
|
2334
|
+
amount: BN,
|
|
2335
|
+
withdrawUnit: WithdrawUnit,
|
|
2336
|
+
txParams?: TxParams
|
|
2337
|
+
): Promise<TransactionSignature> {
|
|
2338
|
+
const ixs = await this.getRequestWithdrawIx(
|
|
2339
|
+
vaultDepositor,
|
|
2340
|
+
amount,
|
|
2341
|
+
withdrawUnit,
|
|
2342
|
+
txParams?.oracleFeedsToCrank
|
|
2343
|
+
);
|
|
2344
|
+
return await this.createAndSendTxn(ixs, txParams);
|
|
2345
|
+
}
|
|
2346
|
+
|
|
2347
|
+
public async getRequestWithdrawIx(
|
|
2348
|
+
vaultDepositor: PublicKey,
|
|
2349
|
+
amount: BN,
|
|
2350
|
+
withdrawUnit: WithdrawUnit,
|
|
2351
|
+
oracleFeedsToCrank?: TxParams['oracleFeedsToCrank']
|
|
2352
|
+
): Promise<TransactionInstruction[]> {
|
|
2353
|
+
const vaultDepositorAccount =
|
|
2354
|
+
await this.program.account.vaultDepositor.fetch(vaultDepositor);
|
|
2355
|
+
const vaultAccount = await this.program.account.vault.fetch(
|
|
2356
|
+
vaultDepositorAccount.vault
|
|
2357
|
+
);
|
|
2358
|
+
|
|
2359
|
+
const user = await this.getSubscribedVaultUser(vaultAccount.user);
|
|
2360
|
+
const userStatsKey = getUserStatsAccountPublicKey(
|
|
2361
|
+
this.driftClient.program.programId,
|
|
2362
|
+
vaultDepositorAccount.vault
|
|
2363
|
+
);
|
|
2364
|
+
const userStats = (await (
|
|
2365
|
+
this.driftClient.program as any
|
|
2366
|
+
).account.userStats.fetch(userStatsKey)) as UserStatsAccount;
|
|
2367
|
+
const remainingAccounts = this.getRemainingAccountsForUser(
|
|
2368
|
+
[user.getUserAccount()],
|
|
2369
|
+
[vaultAccount.spotMarketIndex],
|
|
2370
|
+
vaultAccount,
|
|
2371
|
+
userStats,
|
|
2372
|
+
false,
|
|
2373
|
+
false,
|
|
2374
|
+
false
|
|
2375
|
+
);
|
|
2376
|
+
|
|
2377
|
+
const accounts = {
|
|
2378
|
+
vault: vaultDepositorAccount.vault,
|
|
2379
|
+
vaultDepositor,
|
|
2380
|
+
driftUser: vaultAccount.user,
|
|
2381
|
+
driftUserStats: userStatsKey,
|
|
2382
|
+
};
|
|
2383
|
+
|
|
2384
|
+
const oracleFeedsToCrankIxs = await this.getOracleFeedsToCrankIxs(
|
|
2385
|
+
oracleFeedsToCrank
|
|
2386
|
+
);
|
|
2387
|
+
|
|
2388
|
+
const requestWithdrawIx = this.program.instruction.requestWithdraw(
|
|
2389
|
+
// @ts-ignore
|
|
2390
|
+
amount,
|
|
2391
|
+
withdrawUnit,
|
|
2392
|
+
{
|
|
2393
|
+
accounts: {
|
|
2394
|
+
authority: this.driftClient.wallet.publicKey,
|
|
2395
|
+
...accounts,
|
|
2396
|
+
},
|
|
2397
|
+
remainingAccounts,
|
|
2398
|
+
}
|
|
2399
|
+
);
|
|
2400
|
+
|
|
2401
|
+
return [...oracleFeedsToCrankIxs, requestWithdrawIx];
|
|
2402
|
+
}
|
|
2403
|
+
|
|
2404
|
+
public async withdraw(
|
|
2405
|
+
vaultDepositor: PublicKey,
|
|
2406
|
+
txParams?: TxParams
|
|
2407
|
+
): Promise<TransactionSignature> {
|
|
2408
|
+
const ixs = await this.getWithdrawIx(
|
|
2409
|
+
vaultDepositor,
|
|
2410
|
+
txParams?.oracleFeedsToCrank
|
|
2411
|
+
);
|
|
2412
|
+
return await this.createAndSendTxn(ixs, {
|
|
2413
|
+
cuLimit: 850_000, // overestimating to be safe
|
|
2414
|
+
...txParams,
|
|
2415
|
+
});
|
|
2416
|
+
}
|
|
2417
|
+
|
|
2418
|
+
public async getWithdrawIx(
|
|
2419
|
+
vaultDepositor: PublicKey,
|
|
2420
|
+
oracleFeedsToCrank?: TxParams['oracleFeedsToCrank']
|
|
2421
|
+
): Promise<TransactionInstruction[]> {
|
|
2422
|
+
const vaultDepositorAccount =
|
|
2423
|
+
await this.program.account.vaultDepositor.fetch(vaultDepositor);
|
|
2424
|
+
const vaultAccount = await this.program.account.vault.fetch(
|
|
2425
|
+
vaultDepositorAccount.vault
|
|
2426
|
+
);
|
|
2427
|
+
|
|
2428
|
+
const user = await this.getSubscribedVaultUser(vaultAccount.user);
|
|
2429
|
+
const userStatsKey = getUserStatsAccountPublicKey(
|
|
2430
|
+
this.driftClient.program.programId,
|
|
2431
|
+
vaultDepositorAccount.vault
|
|
2432
|
+
);
|
|
2433
|
+
const userStats = (await (
|
|
2434
|
+
this.driftClient.program as any
|
|
2435
|
+
).account.userStats.fetch(userStatsKey)) as UserStatsAccount;
|
|
2436
|
+
const remainingAccounts = this.getRemainingAccountsForUser(
|
|
2437
|
+
[user.getUserAccount()],
|
|
2438
|
+
[vaultAccount.spotMarketIndex],
|
|
2439
|
+
vaultAccount,
|
|
2440
|
+
userStats,
|
|
2441
|
+
false,
|
|
2442
|
+
false,
|
|
2443
|
+
false
|
|
2444
|
+
);
|
|
2445
|
+
|
|
2446
|
+
const driftStateKey = await this.driftClient.getStatePublicKey();
|
|
2447
|
+
|
|
2448
|
+
const spotMarket = this.driftClient.getSpotMarketAccount(
|
|
2449
|
+
vaultAccount.spotMarketIndex
|
|
2450
|
+
);
|
|
2451
|
+
if (!spotMarket) {
|
|
2452
|
+
throw new Error(
|
|
2453
|
+
`Spot market ${vaultAccount.spotMarketIndex} not found on driftClient`
|
|
2454
|
+
);
|
|
2455
|
+
}
|
|
2456
|
+
|
|
2457
|
+
const isSolMarket = spotMarket.mint.equals(WRAPPED_SOL_MINT);
|
|
2458
|
+
|
|
2459
|
+
// let createAtaIx: TransactionInstruction | undefined = undefined;
|
|
2460
|
+
let userAta = getAssociatedTokenAddressSync(
|
|
2461
|
+
spotMarket.mint,
|
|
2462
|
+
this.driftClient.wallet.publicKey,
|
|
2463
|
+
true
|
|
2464
|
+
);
|
|
2465
|
+
|
|
2466
|
+
const preIxs: TransactionInstruction[] = [];
|
|
2467
|
+
const postIxs: TransactionInstruction[] = [];
|
|
2468
|
+
|
|
2469
|
+
if (isSolMarket) {
|
|
2470
|
+
const { ixs, pubkey } =
|
|
2471
|
+
await this.driftClient.getWrappedSolAccountCreationIxs(ZERO, false);
|
|
2472
|
+
|
|
2473
|
+
userAta = pubkey;
|
|
2474
|
+
preIxs.push(...ixs);
|
|
2475
|
+
postIxs.push(createSyncNativeInstruction(userAta));
|
|
2476
|
+
postIxs.push(
|
|
2477
|
+
createCloseAccountInstruction(
|
|
2478
|
+
userAta,
|
|
2479
|
+
this.driftClient.wallet.publicKey,
|
|
2480
|
+
this.driftClient.wallet.publicKey,
|
|
2481
|
+
[]
|
|
2482
|
+
)
|
|
2483
|
+
);
|
|
2484
|
+
} else {
|
|
2485
|
+
const userAtaExists = await this.driftClient.connection.getAccountInfo(
|
|
2486
|
+
userAta
|
|
2487
|
+
);
|
|
2488
|
+
if (userAtaExists === null) {
|
|
2489
|
+
preIxs.push(
|
|
2490
|
+
createAssociatedTokenAccountInstruction(
|
|
2491
|
+
this.driftClient.wallet.publicKey,
|
|
2492
|
+
userAta,
|
|
2493
|
+
this.driftClient.wallet.publicKey,
|
|
2494
|
+
spotMarket.mint
|
|
2495
|
+
)
|
|
2496
|
+
);
|
|
2497
|
+
}
|
|
2498
|
+
}
|
|
2499
|
+
|
|
2500
|
+
const accounts = {
|
|
2501
|
+
vault: vaultDepositorAccount.vault,
|
|
2502
|
+
vaultDepositor,
|
|
2503
|
+
vaultTokenAccount: vaultAccount.tokenAccount,
|
|
2504
|
+
driftUserStats: userStatsKey,
|
|
2505
|
+
driftUser: vaultAccount.user,
|
|
2506
|
+
driftState: driftStateKey,
|
|
2507
|
+
driftSpotMarketVault: spotMarket.vault,
|
|
2508
|
+
driftSigner: this.driftClient.getStateAccount().signer,
|
|
2509
|
+
userTokenAccount: userAta,
|
|
2510
|
+
driftProgram: this.driftClient.program.programId,
|
|
2511
|
+
tokenProgram: TOKEN_PROGRAM_ID,
|
|
2512
|
+
};
|
|
2513
|
+
|
|
2514
|
+
const oracleFeedsToCrankIxs = await this.getOracleFeedsToCrankIxs(
|
|
2515
|
+
oracleFeedsToCrank
|
|
2516
|
+
);
|
|
2517
|
+
|
|
2518
|
+
const ixs = [
|
|
2519
|
+
...oracleFeedsToCrankIxs,
|
|
2520
|
+
...preIxs,
|
|
2521
|
+
await this.program.methods
|
|
2522
|
+
.withdraw()
|
|
2523
|
+
.accounts({
|
|
2524
|
+
authority: this.driftClient.wallet.publicKey,
|
|
2525
|
+
...accounts,
|
|
2526
|
+
})
|
|
2527
|
+
.remainingAccounts(remainingAccounts)
|
|
2528
|
+
.instruction(),
|
|
2529
|
+
...postIxs,
|
|
2530
|
+
];
|
|
2531
|
+
|
|
2532
|
+
return ixs;
|
|
2533
|
+
}
|
|
2534
|
+
|
|
2535
|
+
public async forceWithdraw(
|
|
2536
|
+
vaultDepositor: PublicKey,
|
|
2537
|
+
txParams?: TxParams
|
|
2538
|
+
): Promise<TransactionSignature> {
|
|
2539
|
+
const ix = await this.getForceWithdrawIx(vaultDepositor);
|
|
2540
|
+
return await this.createAndSendTxn(ix, txParams);
|
|
2541
|
+
}
|
|
2542
|
+
|
|
2543
|
+
public async getForceWithdrawIx(
|
|
2544
|
+
vaultDepositor: PublicKey
|
|
2545
|
+
): Promise<TransactionInstruction[]> {
|
|
2546
|
+
const vaultDepositorAccount =
|
|
2547
|
+
await this.program.account.vaultDepositor.fetch(vaultDepositor);
|
|
2548
|
+
const vaultAccount = await this.program.account.vault.fetch(
|
|
2549
|
+
vaultDepositorAccount.vault
|
|
2550
|
+
);
|
|
2551
|
+
|
|
2552
|
+
const user = await this.getSubscribedVaultUser(vaultAccount.user);
|
|
2553
|
+
const userStatsKey = getUserStatsAccountPublicKey(
|
|
2554
|
+
this.driftClient.program.programId,
|
|
2555
|
+
vaultDepositorAccount.vault
|
|
2556
|
+
);
|
|
2557
|
+
const userStats = (await (
|
|
2558
|
+
this.driftClient.program as any
|
|
2559
|
+
).account.userStats.fetch(userStatsKey)) as UserStatsAccount;
|
|
2560
|
+
const remainingAccounts = this.getRemainingAccountsForUser(
|
|
2561
|
+
[user.getUserAccount()],
|
|
2562
|
+
[vaultAccount.spotMarketIndex],
|
|
2563
|
+
vaultAccount,
|
|
2564
|
+
userStats,
|
|
2565
|
+
false,
|
|
2566
|
+
false,
|
|
2567
|
+
false
|
|
2568
|
+
);
|
|
2569
|
+
if (vaultAccount.vaultProtocol) {
|
|
2570
|
+
const vaultProtocol = this.getVaultProtocolAddress(
|
|
2571
|
+
vaultDepositorAccount.vault
|
|
2572
|
+
);
|
|
2573
|
+
remainingAccounts.push({
|
|
2574
|
+
pubkey: vaultProtocol,
|
|
2575
|
+
isSigner: false,
|
|
2576
|
+
isWritable: true,
|
|
2577
|
+
});
|
|
2578
|
+
}
|
|
2579
|
+
|
|
2580
|
+
const driftStateKey = await this.driftClient.getStatePublicKey();
|
|
2581
|
+
|
|
2582
|
+
const spotMarket = this.driftClient.getSpotMarketAccount(
|
|
2583
|
+
vaultAccount.spotMarketIndex
|
|
2584
|
+
);
|
|
2585
|
+
if (!spotMarket) {
|
|
2586
|
+
throw new Error(
|
|
2587
|
+
`Spot market ${vaultAccount.spotMarketIndex} not found on driftClient`
|
|
2588
|
+
);
|
|
2589
|
+
}
|
|
2590
|
+
|
|
2591
|
+
const [userTokenAccount, createAtaIx] = await getOrCreateATAInstruction(
|
|
2592
|
+
spotMarket.mint,
|
|
2593
|
+
vaultDepositorAccount.authority,
|
|
2594
|
+
this.driftClient.connection,
|
|
2595
|
+
true,
|
|
2596
|
+
this.driftClient.wallet.publicKey
|
|
2597
|
+
);
|
|
2598
|
+
|
|
2599
|
+
if (createAtaIx) {
|
|
2600
|
+
console.log(
|
|
2601
|
+
`Creating ATA for ${vaultDepositorAccount.authority.toBase58()} to ${userTokenAccount.toBase58()}`
|
|
2602
|
+
);
|
|
2603
|
+
}
|
|
2604
|
+
|
|
2605
|
+
const accounts = {
|
|
2606
|
+
manager: vaultAccount.manager,
|
|
2607
|
+
vault: vaultDepositorAccount.vault,
|
|
2608
|
+
vaultDepositor,
|
|
2609
|
+
vaultTokenAccount: vaultAccount.tokenAccount,
|
|
2610
|
+
driftUserStats: userStatsKey,
|
|
2611
|
+
driftUser: vaultAccount.user,
|
|
2612
|
+
driftState: driftStateKey,
|
|
2613
|
+
driftSpotMarketVault: spotMarket.vault,
|
|
2614
|
+
driftSigner: this.driftClient.getStateAccount().signer,
|
|
2615
|
+
userTokenAccount,
|
|
2616
|
+
driftProgram: this.driftClient.program.programId,
|
|
2617
|
+
tokenProgram: TOKEN_PROGRAM_ID,
|
|
2618
|
+
};
|
|
2619
|
+
|
|
2620
|
+
const ixs: TransactionInstruction[] = [];
|
|
2621
|
+
|
|
2622
|
+
if (createAtaIx) {
|
|
2623
|
+
ixs.push(createAtaIx);
|
|
2624
|
+
}
|
|
2625
|
+
|
|
2626
|
+
ixs.push(
|
|
2627
|
+
await this.program.methods
|
|
2628
|
+
.forceWithdraw()
|
|
2629
|
+
.accounts(accounts)
|
|
2630
|
+
.remainingAccounts(remainingAccounts)
|
|
2631
|
+
.instruction()
|
|
2632
|
+
);
|
|
2633
|
+
|
|
2634
|
+
return ixs;
|
|
2635
|
+
}
|
|
2636
|
+
|
|
2637
|
+
public async cancelRequestWithdraw(
|
|
2638
|
+
vaultDepositor: PublicKey,
|
|
2639
|
+
txParams?: TxParams
|
|
2640
|
+
): Promise<TransactionSignature> {
|
|
2641
|
+
const ixs = await this.getCancelRequestWithdrawIx(
|
|
2642
|
+
vaultDepositor,
|
|
2643
|
+
txParams?.oracleFeedsToCrank
|
|
2644
|
+
);
|
|
2645
|
+
return await this.createAndSendTxn(ixs, txParams);
|
|
2646
|
+
}
|
|
2647
|
+
|
|
2648
|
+
public async getCancelRequestWithdrawIx(
|
|
2649
|
+
vaultDepositor: PublicKey,
|
|
2650
|
+
oracleFeedsToCrank: TxParams['oracleFeedsToCrank']
|
|
2651
|
+
): Promise<TransactionInstruction[]> {
|
|
2652
|
+
const vaultDepositorAccount =
|
|
2653
|
+
await this.program.account.vaultDepositor.fetch(vaultDepositor);
|
|
2654
|
+
const vaultAccount = await this.program.account.vault.fetch(
|
|
2655
|
+
vaultDepositorAccount.vault
|
|
2656
|
+
);
|
|
2657
|
+
|
|
2658
|
+
const userStatsKey = getUserStatsAccountPublicKey(
|
|
2659
|
+
this.driftClient.program.programId,
|
|
2660
|
+
vaultDepositorAccount.vault
|
|
2661
|
+
);
|
|
2662
|
+
|
|
2663
|
+
const accounts = {
|
|
2664
|
+
vault: vaultDepositorAccount.vault,
|
|
2665
|
+
vaultDepositor,
|
|
2666
|
+
driftUserStats: userStatsKey,
|
|
2667
|
+
driftUser: vaultAccount.user,
|
|
2668
|
+
};
|
|
2669
|
+
|
|
2670
|
+
const user = await this.getSubscribedVaultUser(vaultAccount.user);
|
|
2671
|
+
const userStats = (await (
|
|
2672
|
+
this.driftClient.program as any
|
|
2673
|
+
).account.userStats.fetch(userStatsKey)) as UserStatsAccount;
|
|
2674
|
+
const remainingAccounts = this.getRemainingAccountsForUser(
|
|
2675
|
+
[user.getUserAccount()],
|
|
2676
|
+
[vaultAccount.spotMarketIndex],
|
|
2677
|
+
vaultAccount,
|
|
2678
|
+
userStats,
|
|
2679
|
+
false,
|
|
2680
|
+
false,
|
|
2681
|
+
false
|
|
2682
|
+
);
|
|
2683
|
+
|
|
2684
|
+
if (this.cliMode) {
|
|
2685
|
+
return [
|
|
2686
|
+
await this.program.methods
|
|
2687
|
+
.cancelRequestWithdraw()
|
|
2688
|
+
.accounts(accounts)
|
|
2689
|
+
.remainingAccounts(remainingAccounts)
|
|
2690
|
+
.instruction(),
|
|
2691
|
+
];
|
|
2692
|
+
} else {
|
|
2693
|
+
const oracleFeedsToCrankIxs = await this.getOracleFeedsToCrankIxs(
|
|
2694
|
+
oracleFeedsToCrank
|
|
2695
|
+
);
|
|
2696
|
+
|
|
2697
|
+
const cancelRequestWithdrawIx =
|
|
2698
|
+
this.program.instruction.cancelRequestWithdraw({
|
|
2699
|
+
accounts: {
|
|
2700
|
+
authority: this.driftClient.wallet.publicKey,
|
|
2701
|
+
...accounts,
|
|
2702
|
+
},
|
|
2703
|
+
remainingAccounts,
|
|
2704
|
+
});
|
|
2705
|
+
|
|
2706
|
+
return [...oracleFeedsToCrankIxs, cancelRequestWithdrawIx];
|
|
2707
|
+
}
|
|
2708
|
+
}
|
|
2709
|
+
|
|
2710
|
+
/**
|
|
2711
|
+
* Liquidates (become delegate for) a vault.
|
|
2712
|
+
* @param
|
|
2713
|
+
* @param
|
|
2714
|
+
* @returns
|
|
2715
|
+
*/
|
|
2716
|
+
public async liquidate(
|
|
2717
|
+
vaultDepositor: PublicKey,
|
|
2718
|
+
txParams?: TxParams
|
|
2719
|
+
): Promise<TransactionSignature> {
|
|
2720
|
+
const ix = await this.getLiquidateIx(vaultDepositor);
|
|
2721
|
+
return await this.createAndSendTxn([ix], txParams);
|
|
2722
|
+
}
|
|
2723
|
+
|
|
2724
|
+
public async getLiquidateIx(
|
|
2725
|
+
vaultDepositor: PublicKey
|
|
2726
|
+
): Promise<TransactionInstruction> {
|
|
2727
|
+
if (!this.driftClient.wallet.publicKey.equals(VAULT_ADMIN_KEY)) {
|
|
2728
|
+
throw new Error('Only vault admin can liquidate');
|
|
2729
|
+
}
|
|
2730
|
+
const vaultDepositorAccount =
|
|
2731
|
+
await this.program.account.vaultDepositor.fetch(vaultDepositor);
|
|
2732
|
+
const vault = vaultDepositorAccount.vault;
|
|
2733
|
+
|
|
2734
|
+
const vaultAccount = await this.program.account.vault.fetch(vault);
|
|
2735
|
+
|
|
2736
|
+
const user = await this.getSubscribedVaultUser(vaultAccount.user);
|
|
2737
|
+
const userStatsKey = getUserStatsAccountPublicKey(
|
|
2738
|
+
this.driftClient.program.programId,
|
|
2739
|
+
vault
|
|
2740
|
+
);
|
|
2741
|
+
const userStats = (await (
|
|
2742
|
+
this.driftClient.program as any
|
|
2743
|
+
).account.userStats.fetch(userStatsKey)) as UserStatsAccount;
|
|
2744
|
+
const remainingAccounts = this.getRemainingAccountsForUser(
|
|
2745
|
+
[user.getUserAccount()],
|
|
2746
|
+
[vaultAccount.spotMarketIndex],
|
|
2747
|
+
vaultAccount,
|
|
2748
|
+
userStats,
|
|
2749
|
+
false,
|
|
2750
|
+
true,
|
|
2751
|
+
true
|
|
2752
|
+
);
|
|
2753
|
+
|
|
2754
|
+
const driftStateKey = await this.driftClient.getStatePublicKey();
|
|
2755
|
+
|
|
2756
|
+
const accounts = {
|
|
2757
|
+
vault,
|
|
2758
|
+
vaultDepositor,
|
|
2759
|
+
vaultTokenAccount: vaultAccount.tokenAccount,
|
|
2760
|
+
driftUserStats: userStatsKey,
|
|
2761
|
+
driftUser: vaultAccount.user,
|
|
2762
|
+
driftState: driftStateKey,
|
|
2763
|
+
driftProgram: this.driftClient.program.programId,
|
|
2764
|
+
authority: vaultDepositorAccount.authority,
|
|
2765
|
+
};
|
|
2766
|
+
|
|
2767
|
+
if (this.cliMode) {
|
|
2768
|
+
return await this.program.methods
|
|
2769
|
+
.liquidate()
|
|
2770
|
+
.accounts(accounts)
|
|
2771
|
+
.remainingAccounts(remainingAccounts)
|
|
2772
|
+
.instruction();
|
|
2773
|
+
} else {
|
|
2774
|
+
return this.program.instruction.liquidate({
|
|
2775
|
+
accounts: {
|
|
2776
|
+
...accounts,
|
|
2777
|
+
admin: this.driftClient.wallet.publicKey,
|
|
2778
|
+
},
|
|
2779
|
+
remainingAccounts,
|
|
2780
|
+
});
|
|
2781
|
+
}
|
|
2782
|
+
}
|
|
2783
|
+
|
|
2784
|
+
public async createTxn(
|
|
2785
|
+
vaultIxs: TransactionInstruction[],
|
|
2786
|
+
txParams?: TxParams
|
|
2787
|
+
): Promise<VersionedTransaction> {
|
|
2788
|
+
const ixs = [
|
|
2789
|
+
ComputeBudgetProgram.setComputeUnitLimit({
|
|
2790
|
+
units: txParams?.cuLimit ?? 400_000,
|
|
2791
|
+
}),
|
|
2792
|
+
ComputeBudgetProgram.setComputeUnitPrice({
|
|
2793
|
+
microLamports:
|
|
2794
|
+
txParams?.cuPriceMicroLamports === undefined
|
|
2795
|
+
? 50_000
|
|
2796
|
+
: txParams.cuPriceMicroLamports,
|
|
2797
|
+
}),
|
|
2798
|
+
...vaultIxs,
|
|
2799
|
+
];
|
|
2800
|
+
|
|
2801
|
+
return (await this.driftClient.txHandler.buildTransaction({
|
|
2802
|
+
connection: this.driftClient.connection,
|
|
2803
|
+
instructions: ixs,
|
|
2804
|
+
lookupTables: txParams?.lookupTables ?? [],
|
|
2805
|
+
preFlightCommitment: 'confirmed',
|
|
2806
|
+
forceVersionedTransaction: true,
|
|
2807
|
+
txVersion: 0,
|
|
2808
|
+
fetchAllMarketLookupTableAccounts:
|
|
2809
|
+
this.driftClient.fetchAllLookupTableAccounts.bind(this.driftClient),
|
|
2810
|
+
})) as VersionedTransaction;
|
|
2811
|
+
}
|
|
2812
|
+
|
|
2813
|
+
public async createTxnNoLut(
|
|
2814
|
+
vaultIxs: TransactionInstruction[],
|
|
2815
|
+
txParams?: TxParams
|
|
2816
|
+
): Promise<VersionedTransaction> {
|
|
2817
|
+
const ixs = [
|
|
2818
|
+
ComputeBudgetProgram.setComputeUnitLimit({
|
|
2819
|
+
units: txParams?.cuLimit ?? 400_000,
|
|
2820
|
+
}),
|
|
2821
|
+
ComputeBudgetProgram.setComputeUnitPrice({
|
|
2822
|
+
microLamports:
|
|
2823
|
+
txParams?.cuPriceMicroLamports === undefined
|
|
2824
|
+
? 50_000
|
|
2825
|
+
: txParams.cuPriceMicroLamports,
|
|
2826
|
+
}),
|
|
2827
|
+
...vaultIxs,
|
|
2828
|
+
];
|
|
2829
|
+
|
|
2830
|
+
const recentBlockhash =
|
|
2831
|
+
await this.driftClient.connection.getLatestBlockhash();
|
|
2832
|
+
|
|
2833
|
+
return this.driftClient.txHandler.generateVersionedTransaction(
|
|
2834
|
+
recentBlockhash,
|
|
2835
|
+
ixs,
|
|
2836
|
+
[],
|
|
2837
|
+
this.driftClient.wallet
|
|
2838
|
+
);
|
|
2839
|
+
}
|
|
2840
|
+
|
|
2841
|
+
public async sendTxn(
|
|
2842
|
+
transaction: VersionedTransaction,
|
|
2843
|
+
simulateTransaction?: boolean
|
|
2844
|
+
): Promise<TransactionSignature> {
|
|
2845
|
+
let txSig = bs58.encode(transaction.signatures[0]);
|
|
2846
|
+
if (simulateTransaction) {
|
|
2847
|
+
try {
|
|
2848
|
+
const resp = await this.driftClient.connection.simulateTransaction(
|
|
2849
|
+
transaction,
|
|
2850
|
+
{
|
|
2851
|
+
sigVerify: false,
|
|
2852
|
+
commitment: this.driftClient.connection.commitment,
|
|
2853
|
+
}
|
|
2854
|
+
);
|
|
2855
|
+
console.log(`Simulated transaction:\n${JSON.stringify(resp, null, 2)}`);
|
|
2856
|
+
} catch (e) {
|
|
2857
|
+
const err = e as Error;
|
|
2858
|
+
console.error(
|
|
2859
|
+
`Error simulating transaction: ${err.message}\n:${err.stack ?? ''}`
|
|
2860
|
+
);
|
|
2861
|
+
}
|
|
2862
|
+
} else {
|
|
2863
|
+
const resp = await this.driftClient.sendTransaction(
|
|
2864
|
+
transaction,
|
|
2865
|
+
[],
|
|
2866
|
+
this.driftClient.opts
|
|
2867
|
+
);
|
|
2868
|
+
if (resp.txSig !== txSig) {
|
|
2869
|
+
txSig = resp.txSig;
|
|
2870
|
+
}
|
|
2871
|
+
}
|
|
2872
|
+
|
|
2873
|
+
return txSig!;
|
|
2874
|
+
}
|
|
2875
|
+
|
|
2876
|
+
/**
|
|
2877
|
+
* Used for UI wallet adapters compatibility
|
|
2878
|
+
*/
|
|
2879
|
+
public async createAndSendTxn(
|
|
2880
|
+
vaultIxs: TransactionInstruction[],
|
|
2881
|
+
txParams?: TxParams
|
|
2882
|
+
): Promise<TransactionSignature> {
|
|
2883
|
+
let tx: VersionedTransaction;
|
|
2884
|
+
if (txParams?.noLut ? txParams.noLut : false) {
|
|
2885
|
+
tx = await this.createTxnNoLut(vaultIxs, txParams);
|
|
2886
|
+
// @ts-ignore
|
|
2887
|
+
tx.sign([this.driftClient.wallet.payer]);
|
|
2888
|
+
} else {
|
|
2889
|
+
tx = await this.createTxn(vaultIxs, txParams);
|
|
2890
|
+
}
|
|
2891
|
+
const txSig = await this.sendTxn(tx, txParams?.simulateTransaction);
|
|
2892
|
+
|
|
2893
|
+
return txSig;
|
|
2894
|
+
}
|
|
2895
|
+
|
|
2896
|
+
/**
|
|
2897
|
+
* Initializes an insurance fund stake for the vault.
|
|
2898
|
+
* @param vault vault address to update
|
|
2899
|
+
* @param spotMarketIndex spot market index of the insurance fund stake
|
|
2900
|
+
* @returns
|
|
2901
|
+
*/
|
|
2902
|
+
public async initializeInsuranceFundStake(
|
|
2903
|
+
vault: PublicKey,
|
|
2904
|
+
spotMarketIndex: number,
|
|
2905
|
+
txParams?: TxParams
|
|
2906
|
+
): Promise<TransactionSignature> {
|
|
2907
|
+
const ixs = await this.getInitializeInsuranceFundStakeIx(
|
|
2908
|
+
vault,
|
|
2909
|
+
spotMarketIndex
|
|
2910
|
+
);
|
|
2911
|
+
return await this.createAndSendTxn([ixs], txParams);
|
|
2912
|
+
}
|
|
2913
|
+
|
|
2914
|
+
public async getInitializeInsuranceFundStakeIx(
|
|
2915
|
+
vault: PublicKey,
|
|
2916
|
+
spotMarketIndex: number
|
|
2917
|
+
): Promise<TransactionInstruction> {
|
|
2918
|
+
const vaultAccount = await this.program.account.vault.fetch(vault);
|
|
2919
|
+
|
|
2920
|
+
const _ifStakeAccountPublicKey = getInsuranceFundStakeAccountPublicKey(
|
|
2921
|
+
this.driftClient.program.programId,
|
|
2922
|
+
vault,
|
|
2923
|
+
spotMarketIndex
|
|
2924
|
+
);
|
|
2925
|
+
|
|
2926
|
+
const spotMarket = this.driftClient.getSpotMarketAccount(spotMarketIndex);
|
|
2927
|
+
if (!spotMarket) {
|
|
2928
|
+
throw new Error(
|
|
2929
|
+
`Spot market ${spotMarketIndex} not found on driftClient`
|
|
2930
|
+
);
|
|
2931
|
+
}
|
|
2932
|
+
|
|
2933
|
+
const _ifVaultTokenAccount = getInsuranceFundTokenVaultAddressSync(
|
|
2934
|
+
this.program.programId,
|
|
2935
|
+
vault,
|
|
2936
|
+
spotMarketIndex
|
|
2937
|
+
);
|
|
2938
|
+
|
|
2939
|
+
return await this.program.methods
|
|
2940
|
+
.initializeInsuranceFundStake(spotMarketIndex)
|
|
2941
|
+
.accounts({
|
|
2942
|
+
vault: vault,
|
|
2943
|
+
driftSpotMarketMint: spotMarket.mint,
|
|
2944
|
+
driftUserStats: vaultAccount.userStats,
|
|
2945
|
+
driftState: await this.driftClient.getStatePublicKey(),
|
|
2946
|
+
})
|
|
2947
|
+
.instruction();
|
|
2948
|
+
}
|
|
2949
|
+
|
|
2950
|
+
/**
|
|
2951
|
+
* Adds an amount to an insurance fund stake for the vault.
|
|
2952
|
+
* @param vault vault address to update
|
|
2953
|
+
* @param spotMarketIndex spot market index of the insurance fund stake
|
|
2954
|
+
* @param amount amount to add to the insurance fund stake, in spotMarketIndex precision
|
|
2955
|
+
* @returns
|
|
2956
|
+
*/
|
|
2957
|
+
public async addToInsuranceFundStake(
|
|
2958
|
+
vault: PublicKey,
|
|
2959
|
+
spotMarketIndex: number,
|
|
2960
|
+
amount: BN,
|
|
2961
|
+
managerTokenAccount?: PublicKey,
|
|
2962
|
+
txParams?: TxParams
|
|
2963
|
+
): Promise<TransactionSignature> {
|
|
2964
|
+
const ixs = await this.getAddToInsuranceFundStakeIx(
|
|
2965
|
+
vault,
|
|
2966
|
+
spotMarketIndex,
|
|
2967
|
+
amount,
|
|
2968
|
+
managerTokenAccount
|
|
2969
|
+
);
|
|
2970
|
+
return await this.createAndSendTxn([ixs], txParams);
|
|
2971
|
+
}
|
|
2972
|
+
|
|
2973
|
+
public async getAddToInsuranceFundStakeIx(
|
|
2974
|
+
vault: PublicKey,
|
|
2975
|
+
spotMarketIndex: number,
|
|
2976
|
+
amount: BN,
|
|
2977
|
+
managerTokenAccount?: PublicKey
|
|
2978
|
+
): Promise<TransactionInstruction> {
|
|
2979
|
+
const vaultAccount = await this.program.account.vault.fetch(vault);
|
|
2980
|
+
|
|
2981
|
+
if (!vaultAccount.manager.equals(this.driftClient.wallet.publicKey)) {
|
|
2982
|
+
throw new Error(
|
|
2983
|
+
`Only the manager of the vault can add to the insurance fund stake.`
|
|
2984
|
+
);
|
|
2985
|
+
}
|
|
2986
|
+
|
|
2987
|
+
const _ifStakeAccountPublicKey = getInsuranceFundStakeAccountPublicKey(
|
|
2988
|
+
this.driftClient.program.programId,
|
|
2989
|
+
vault,
|
|
2990
|
+
spotMarketIndex
|
|
2991
|
+
);
|
|
2992
|
+
const _ifVaultPublicKey = await getInsuranceFundVaultPublicKey(
|
|
2993
|
+
this.driftClient.program.programId,
|
|
2994
|
+
spotMarketIndex
|
|
2995
|
+
);
|
|
2996
|
+
|
|
2997
|
+
const spotMarket = this.driftClient.getSpotMarketAccount(spotMarketIndex);
|
|
2998
|
+
if (!spotMarket) {
|
|
2999
|
+
throw new Error(
|
|
3000
|
+
`Spot market ${spotMarketIndex} not found on driftClient`
|
|
3001
|
+
);
|
|
3002
|
+
}
|
|
3003
|
+
|
|
3004
|
+
if (!managerTokenAccount) {
|
|
3005
|
+
managerTokenAccount = getAssociatedTokenAddressSync(
|
|
3006
|
+
spotMarket.mint,
|
|
3007
|
+
vaultAccount.manager,
|
|
3008
|
+
true
|
|
3009
|
+
);
|
|
3010
|
+
}
|
|
3011
|
+
|
|
3012
|
+
const _ifVaultTokenAccount = getInsuranceFundTokenVaultAddressSync(
|
|
3013
|
+
this.program.programId,
|
|
3014
|
+
vault,
|
|
3015
|
+
spotMarketIndex
|
|
3016
|
+
);
|
|
3017
|
+
|
|
3018
|
+
return await this.program.methods
|
|
3019
|
+
.addInsuranceFundStake(spotMarketIndex, amount)
|
|
3020
|
+
.accounts({
|
|
3021
|
+
vault: vault,
|
|
3022
|
+
managerTokenAccount,
|
|
3023
|
+
driftUserStats: vaultAccount.userStats,
|
|
3024
|
+
driftState: await this.driftClient.getStatePublicKey(),
|
|
3025
|
+
driftSigner: this.driftClient.getStateAccount().signer,
|
|
3026
|
+
})
|
|
3027
|
+
.instruction();
|
|
3028
|
+
}
|
|
3029
|
+
|
|
3030
|
+
public async requestRemoveInsuranceFundStake(
|
|
3031
|
+
vault: PublicKey,
|
|
3032
|
+
spotMarketIndex: number,
|
|
3033
|
+
amount: BN,
|
|
3034
|
+
txParams?: TxParams
|
|
3035
|
+
): Promise<TransactionSignature> {
|
|
3036
|
+
const ix = await this.getRequestRemoveInsuranceFundStakeIx(
|
|
3037
|
+
vault,
|
|
3038
|
+
spotMarketIndex,
|
|
3039
|
+
amount
|
|
3040
|
+
);
|
|
3041
|
+
return await this.createAndSendTxn([ix], txParams);
|
|
3042
|
+
}
|
|
3043
|
+
|
|
3044
|
+
public async getRequestRemoveInsuranceFundStakeIx(
|
|
3045
|
+
vault: PublicKey,
|
|
3046
|
+
spotMarketIndex: number,
|
|
3047
|
+
amount: BN
|
|
3048
|
+
): Promise<TransactionInstruction> {
|
|
3049
|
+
const vaultAccount = await this.program.account.vault.fetch(vault);
|
|
3050
|
+
const _ifStakeAccountPublicKey = getInsuranceFundStakeAccountPublicKey(
|
|
3051
|
+
this.driftClient.program.programId,
|
|
3052
|
+
vault,
|
|
3053
|
+
spotMarketIndex
|
|
3054
|
+
);
|
|
3055
|
+
const _ifVaultPublicKey = await getInsuranceFundVaultPublicKey(
|
|
3056
|
+
this.driftClient.program.programId,
|
|
3057
|
+
spotMarketIndex
|
|
3058
|
+
);
|
|
3059
|
+
|
|
3060
|
+
const spotMarket = this.driftClient.getSpotMarketAccount(spotMarketIndex);
|
|
3061
|
+
if (!spotMarket) {
|
|
3062
|
+
throw new Error(
|
|
3063
|
+
`Spot market ${spotMarketIndex} not found on driftClient`
|
|
3064
|
+
);
|
|
3065
|
+
}
|
|
3066
|
+
|
|
3067
|
+
return await this.program.methods
|
|
3068
|
+
.requestRemoveInsuranceFundStake(spotMarketIndex, amount)
|
|
3069
|
+
.accounts({
|
|
3070
|
+
vault,
|
|
3071
|
+
manager: vaultAccount.manager,
|
|
3072
|
+
driftUserStats: vaultAccount.userStats,
|
|
3073
|
+
})
|
|
3074
|
+
.instruction();
|
|
3075
|
+
}
|
|
3076
|
+
|
|
3077
|
+
public async cancelRequestRemoveInsuranceFundStake(
|
|
3078
|
+
vault: PublicKey,
|
|
3079
|
+
spotMarketIndex: number,
|
|
3080
|
+
txParams?: TxParams
|
|
3081
|
+
): Promise<TransactionSignature> {
|
|
3082
|
+
const ix = await this.getCancelRequestRemoveInsuranceFundStakeIx(
|
|
3083
|
+
vault,
|
|
3084
|
+
spotMarketIndex
|
|
3085
|
+
);
|
|
3086
|
+
return await this.createAndSendTxn([ix], txParams);
|
|
3087
|
+
}
|
|
3088
|
+
|
|
3089
|
+
public async getCancelRequestRemoveInsuranceFundStakeIx(
|
|
3090
|
+
vault: PublicKey,
|
|
3091
|
+
spotMarketIndex: number
|
|
3092
|
+
): Promise<TransactionInstruction> {
|
|
3093
|
+
const vaultAccount = await this.program.account.vault.fetch(vault);
|
|
3094
|
+
const _ifStakeAccountPublicKey = getInsuranceFundStakeAccountPublicKey(
|
|
3095
|
+
this.driftClient.program.programId,
|
|
3096
|
+
vault,
|
|
3097
|
+
spotMarketIndex
|
|
3098
|
+
);
|
|
3099
|
+
const _ifVaultPublicKey = await getInsuranceFundVaultPublicKey(
|
|
3100
|
+
this.driftClient.program.programId,
|
|
3101
|
+
spotMarketIndex
|
|
3102
|
+
);
|
|
3103
|
+
const spotMarket = this.driftClient.getSpotMarketAccount(spotMarketIndex);
|
|
3104
|
+
if (!spotMarket) {
|
|
3105
|
+
throw new Error(
|
|
3106
|
+
`Spot market ${spotMarketIndex} not found on driftClient`
|
|
3107
|
+
);
|
|
3108
|
+
}
|
|
3109
|
+
|
|
3110
|
+
return await this.program.methods
|
|
3111
|
+
.cancelRequestRemoveInsuranceFundStake(spotMarketIndex)
|
|
3112
|
+
.accounts({
|
|
3113
|
+
vault: vault,
|
|
3114
|
+
manager: vaultAccount.manager,
|
|
3115
|
+
driftUserStats: vaultAccount.userStats,
|
|
3116
|
+
})
|
|
3117
|
+
.instruction();
|
|
3118
|
+
}
|
|
3119
|
+
|
|
3120
|
+
public async removeInsuranceFundStake(
|
|
3121
|
+
vault: PublicKey,
|
|
3122
|
+
spotMarketIndex: number,
|
|
3123
|
+
managerTokenAccount?: PublicKey,
|
|
3124
|
+
txParams?: TxParams
|
|
3125
|
+
): Promise<TransactionSignature> {
|
|
3126
|
+
const ixs = await this.getRemoveInsuranceFundStakeIx(
|
|
3127
|
+
vault,
|
|
3128
|
+
spotMarketIndex,
|
|
3129
|
+
managerTokenAccount
|
|
3130
|
+
);
|
|
3131
|
+
return await this.createAndSendTxn([ixs], txParams);
|
|
3132
|
+
}
|
|
3133
|
+
|
|
3134
|
+
public async getRemoveInsuranceFundStakeIx(
|
|
3135
|
+
vault: PublicKey,
|
|
3136
|
+
spotMarketIndex: number,
|
|
3137
|
+
managerTokenAccount?: PublicKey
|
|
3138
|
+
): Promise<TransactionInstruction> {
|
|
3139
|
+
const vaultAccount = await this.program.account.vault.fetch(vault);
|
|
3140
|
+
const _ifStakeAccountPublicKey = getInsuranceFundStakeAccountPublicKey(
|
|
3141
|
+
this.driftClient.program.programId,
|
|
3142
|
+
vault,
|
|
3143
|
+
spotMarketIndex
|
|
3144
|
+
);
|
|
3145
|
+
const _ifVaultPublicKey = await getInsuranceFundVaultPublicKey(
|
|
3146
|
+
this.driftClient.program.programId,
|
|
3147
|
+
spotMarketIndex
|
|
3148
|
+
);
|
|
3149
|
+
const spotMarket = this.driftClient.getSpotMarketAccount(spotMarketIndex);
|
|
3150
|
+
if (!spotMarket) {
|
|
3151
|
+
throw new Error(
|
|
3152
|
+
`Spot market ${spotMarketIndex} not found on driftClient`
|
|
3153
|
+
);
|
|
3154
|
+
}
|
|
3155
|
+
|
|
3156
|
+
if (!managerTokenAccount) {
|
|
3157
|
+
managerTokenAccount = getAssociatedTokenAddressSync(
|
|
3158
|
+
spotMarket.mint,
|
|
3159
|
+
vaultAccount.manager,
|
|
3160
|
+
true
|
|
3161
|
+
);
|
|
3162
|
+
}
|
|
3163
|
+
|
|
3164
|
+
const _ifVaultTokenAccount = getInsuranceFundTokenVaultAddressSync(
|
|
3165
|
+
this.program.programId,
|
|
3166
|
+
vault,
|
|
3167
|
+
spotMarketIndex
|
|
3168
|
+
);
|
|
3169
|
+
|
|
3170
|
+
return await this.program.methods
|
|
3171
|
+
.removeInsuranceFundStake(spotMarketIndex)
|
|
3172
|
+
.accounts({
|
|
3173
|
+
vault: vault,
|
|
3174
|
+
managerTokenAccount,
|
|
3175
|
+
driftState: await this.driftClient.getStatePublicKey(),
|
|
3176
|
+
driftUserStats: vaultAccount.userStats,
|
|
3177
|
+
driftSigner: this.driftClient.getStateAccount().signer,
|
|
3178
|
+
})
|
|
3179
|
+
.instruction();
|
|
3180
|
+
}
|
|
3181
|
+
|
|
3182
|
+
public async protocolRequestWithdraw(
|
|
3183
|
+
vault: PublicKey,
|
|
3184
|
+
amount: BN,
|
|
3185
|
+
withdrawUnit: WithdrawUnit,
|
|
3186
|
+
txParams?: TxParams
|
|
3187
|
+
): Promise<TransactionSignature> {
|
|
3188
|
+
const ix = await this.getProtocolRequestWithdrawIx(
|
|
3189
|
+
vault,
|
|
3190
|
+
amount,
|
|
3191
|
+
withdrawUnit
|
|
3192
|
+
);
|
|
3193
|
+
return await this.createAndSendTxn([ix], txParams);
|
|
3194
|
+
}
|
|
3195
|
+
|
|
3196
|
+
public async getProtocolRequestWithdrawIx(
|
|
3197
|
+
vault: PublicKey,
|
|
3198
|
+
amount: BN,
|
|
3199
|
+
withdrawUnit: WithdrawUnit
|
|
3200
|
+
): Promise<TransactionInstruction> {
|
|
3201
|
+
// @ts-ignore
|
|
3202
|
+
const vaultAccount = (await this.program.account.vault.fetch(
|
|
3203
|
+
vault
|
|
3204
|
+
)) as Vault;
|
|
3205
|
+
const vp = this.getVaultProtocolAddress(vault);
|
|
3206
|
+
const vpAccount = (await this.program.account.vaultProtocol.fetch(
|
|
3207
|
+
vp
|
|
3208
|
+
)) as VaultProtocol;
|
|
3209
|
+
|
|
3210
|
+
if (!this.driftClient.wallet.publicKey.equals(vpAccount.protocol)) {
|
|
3211
|
+
throw new Error(`Only the protocol of the vault can request a withdraw.`);
|
|
3212
|
+
}
|
|
3213
|
+
|
|
3214
|
+
const user = await this.getSubscribedVaultUser(vaultAccount.user);
|
|
3215
|
+
const userStatsKey = getUserStatsAccountPublicKey(
|
|
3216
|
+
this.driftClient.program.programId,
|
|
3217
|
+
vault
|
|
3218
|
+
);
|
|
3219
|
+
const userStats = (await (
|
|
3220
|
+
this.driftClient.program as any
|
|
3221
|
+
).account.userStats.fetch(userStatsKey)) as UserStatsAccount;
|
|
3222
|
+
const remainingAccounts = this.getRemainingAccountsForUser(
|
|
3223
|
+
[user.getUserAccount()],
|
|
3224
|
+
[],
|
|
3225
|
+
vaultAccount,
|
|
3226
|
+
userStats,
|
|
3227
|
+
false,
|
|
3228
|
+
true,
|
|
3229
|
+
true
|
|
3230
|
+
);
|
|
3231
|
+
|
|
3232
|
+
const accounts = {
|
|
3233
|
+
vault,
|
|
3234
|
+
driftUserStats: userStatsKey,
|
|
3235
|
+
driftUser: vaultAccount.user,
|
|
3236
|
+
};
|
|
3237
|
+
|
|
3238
|
+
if (this.cliMode) {
|
|
3239
|
+
return await this.program.methods
|
|
3240
|
+
// @ts-ignore, 0.29.0 anchor issues..
|
|
3241
|
+
.managerRequestWithdraw(amount, withdrawUnit)
|
|
3242
|
+
.accounts(accounts)
|
|
3243
|
+
.remainingAccounts(remainingAccounts)
|
|
3244
|
+
.instruction();
|
|
3245
|
+
} else {
|
|
3246
|
+
const requestWithdrawIx = this.program.instruction.managerRequestWithdraw(
|
|
3247
|
+
// @ts-ignore
|
|
3248
|
+
amount,
|
|
3249
|
+
withdrawUnit,
|
|
3250
|
+
{
|
|
3251
|
+
accounts: {
|
|
3252
|
+
manager: vaultAccount.manager,
|
|
3253
|
+
...accounts,
|
|
3254
|
+
},
|
|
3255
|
+
remainingAccounts,
|
|
3256
|
+
}
|
|
3257
|
+
);
|
|
3258
|
+
|
|
3259
|
+
return requestWithdrawIx;
|
|
3260
|
+
}
|
|
3261
|
+
}
|
|
3262
|
+
|
|
3263
|
+
public async protocolCancelWithdrawRequest(
|
|
3264
|
+
vault: PublicKey,
|
|
3265
|
+
txParams?: TxParams
|
|
3266
|
+
): Promise<TransactionSignature> {
|
|
3267
|
+
const ixs = await this.getProtocolCancelWithdrawRequestIx(vault);
|
|
3268
|
+
return await this.createAndSendTxn(ixs, txParams);
|
|
3269
|
+
}
|
|
3270
|
+
|
|
3271
|
+
public async getProtocolCancelWithdrawRequestIx(
|
|
3272
|
+
vault: PublicKey
|
|
3273
|
+
): Promise<TransactionInstruction[]> {
|
|
3274
|
+
const vaultAccount = await this.program.account.vault.fetch(vault);
|
|
3275
|
+
|
|
3276
|
+
const userStatsKey = getUserStatsAccountPublicKey(
|
|
3277
|
+
this.driftClient.program.programId,
|
|
3278
|
+
vault
|
|
3279
|
+
);
|
|
3280
|
+
|
|
3281
|
+
const accounts = {
|
|
3282
|
+
manager: vaultAccount.manager,
|
|
3283
|
+
vault,
|
|
3284
|
+
driftUserStats: userStatsKey,
|
|
3285
|
+
driftUser: vaultAccount.user,
|
|
3286
|
+
};
|
|
3287
|
+
|
|
3288
|
+
const user = await this.getSubscribedVaultUser(vaultAccount.user);
|
|
3289
|
+
const userStats = (await (
|
|
3290
|
+
this.driftClient.program as any
|
|
3291
|
+
).account.userStats.fetch(userStatsKey)) as UserStatsAccount;
|
|
3292
|
+
const remainingAccounts = this.getRemainingAccountsForUser(
|
|
3293
|
+
[user.getUserAccount()],
|
|
3294
|
+
[],
|
|
3295
|
+
vaultAccount,
|
|
3296
|
+
userStats,
|
|
3297
|
+
false,
|
|
3298
|
+
true,
|
|
3299
|
+
true
|
|
3300
|
+
);
|
|
3301
|
+
|
|
3302
|
+
if (this.cliMode) {
|
|
3303
|
+
return [
|
|
3304
|
+
await this.program.methods
|
|
3305
|
+
.mangerCancelWithdrawRequest()
|
|
3306
|
+
.accounts(accounts)
|
|
3307
|
+
.remainingAccounts(remainingAccounts)
|
|
3308
|
+
.instruction(),
|
|
3309
|
+
];
|
|
3310
|
+
} else {
|
|
3311
|
+
const cancelRequestWithdrawIx =
|
|
3312
|
+
this.program.instruction.mangerCancelWithdrawRequest({
|
|
3313
|
+
accounts: {
|
|
3314
|
+
...accounts,
|
|
3315
|
+
manager: vaultAccount.manager,
|
|
3316
|
+
},
|
|
3317
|
+
remainingAccounts,
|
|
3318
|
+
});
|
|
3319
|
+
|
|
3320
|
+
return [cancelRequestWithdrawIx];
|
|
3321
|
+
}
|
|
3322
|
+
}
|
|
3323
|
+
|
|
3324
|
+
public async protocolWithdraw(
|
|
3325
|
+
vault: PublicKey,
|
|
3326
|
+
txParams?: TxParams
|
|
3327
|
+
): Promise<TransactionSignature> {
|
|
3328
|
+
const ixs = await this.getProtocolWithdrawIx(vault);
|
|
3329
|
+
return await this.createAndSendTxn(ixs, txParams);
|
|
3330
|
+
}
|
|
3331
|
+
|
|
3332
|
+
public async getProtocolWithdrawIx(
|
|
3333
|
+
vault: PublicKey
|
|
3334
|
+
): Promise<TransactionInstruction[]> {
|
|
3335
|
+
const vaultAccount = await this.program.account.vault.fetch(vault);
|
|
3336
|
+
|
|
3337
|
+
if (!this.driftClient.wallet.publicKey.equals(vaultAccount.manager)) {
|
|
3338
|
+
throw new Error(`Only the manager of the vault can request a withdraw.`);
|
|
3339
|
+
}
|
|
3340
|
+
|
|
3341
|
+
const user = await this.getSubscribedVaultUser(vaultAccount.user);
|
|
3342
|
+
const userStatsKey = getUserStatsAccountPublicKey(
|
|
3343
|
+
this.driftClient.program.programId,
|
|
3344
|
+
vault
|
|
3345
|
+
);
|
|
3346
|
+
const userStats = (await (
|
|
3347
|
+
this.driftClient.program as any
|
|
3348
|
+
).account.userStats.fetch(userStatsKey)) as UserStatsAccount;
|
|
3349
|
+
const remainingAccounts = this.getRemainingAccountsForUser(
|
|
3350
|
+
[user.getUserAccount()],
|
|
3351
|
+
[],
|
|
3352
|
+
vaultAccount,
|
|
3353
|
+
userStats,
|
|
3354
|
+
false,
|
|
3355
|
+
true,
|
|
3356
|
+
true
|
|
3357
|
+
);
|
|
3358
|
+
|
|
3359
|
+
const spotMarket = this.driftClient.getSpotMarketAccount(
|
|
3360
|
+
vaultAccount.spotMarketIndex
|
|
3361
|
+
);
|
|
3362
|
+
if (!spotMarket) {
|
|
3363
|
+
throw new Error(
|
|
3364
|
+
`Spot market ${vaultAccount.spotMarketIndex} not found on driftClient`
|
|
3365
|
+
);
|
|
3366
|
+
}
|
|
3367
|
+
|
|
3368
|
+
const ix = this.program.instruction.managerWithdraw({
|
|
3369
|
+
accounts: {
|
|
3370
|
+
vault,
|
|
3371
|
+
manager: vaultAccount.manager,
|
|
3372
|
+
vaultTokenAccount: vaultAccount.tokenAccount,
|
|
3373
|
+
driftUser: await getUserAccountPublicKey(
|
|
3374
|
+
this.driftClient.program.programId,
|
|
3375
|
+
vault
|
|
3376
|
+
),
|
|
3377
|
+
driftProgram: this.driftClient.program.programId,
|
|
3378
|
+
driftUserStats: userStatsKey,
|
|
3379
|
+
driftState: await this.driftClient.getStatePublicKey(),
|
|
3380
|
+
driftSpotMarketVault: spotMarket.vault,
|
|
3381
|
+
userTokenAccount: getAssociatedTokenAddressSync(
|
|
3382
|
+
spotMarket.mint,
|
|
3383
|
+
this.driftClient.wallet.publicKey
|
|
3384
|
+
),
|
|
3385
|
+
driftSigner: this.driftClient.getStateAccount().signer,
|
|
3386
|
+
tokenProgram: TOKEN_PROGRAM_ID,
|
|
3387
|
+
},
|
|
3388
|
+
remainingAccounts,
|
|
3389
|
+
});
|
|
3390
|
+
return [ix];
|
|
3391
|
+
}
|
|
3392
|
+
|
|
3393
|
+
private async getPythLazerOracleCrankIxs(
|
|
3394
|
+
oracleFeedsToCrank: OracleFeedConfig[] = [],
|
|
3395
|
+
pythLazerMsgHexGetter?: (feedIds: number[]) => Promise<string>
|
|
3396
|
+
) {
|
|
3397
|
+
try {
|
|
3398
|
+
const isPythLazerOracle = (oracleSource: OracleSource) => {
|
|
3399
|
+
const pythLazerStr = JSON.stringify(OracleSource.PYTH_LAZER);
|
|
3400
|
+
const pythLazer1kStr = JSON.stringify(OracleSource.PYTH_LAZER_1K);
|
|
3401
|
+
const pythLazer1mStr = JSON.stringify(OracleSource.PYTH_LAZER_1M);
|
|
3402
|
+
const pythLazerStableCoinStr = JSON.stringify(
|
|
3403
|
+
OracleSource.PYTH_LAZER_STABLE_COIN
|
|
3404
|
+
);
|
|
3405
|
+
const targetOracleSourceStr = JSON.stringify(oracleSource);
|
|
3406
|
+
return (
|
|
3407
|
+
targetOracleSourceStr === pythLazerStr ||
|
|
3408
|
+
targetOracleSourceStr === pythLazer1kStr ||
|
|
3409
|
+
targetOracleSourceStr === pythLazer1mStr ||
|
|
3410
|
+
targetOracleSourceStr === pythLazerStableCoinStr
|
|
3411
|
+
);
|
|
3412
|
+
};
|
|
3413
|
+
|
|
3414
|
+
const pythLazerOracles = oracleFeedsToCrank.filter((config) =>
|
|
3415
|
+
isPythLazerOracle(config.oracleSource)
|
|
3416
|
+
);
|
|
3417
|
+
|
|
3418
|
+
if (pythLazerOracles.length === 0) {
|
|
3419
|
+
return [];
|
|
3420
|
+
}
|
|
3421
|
+
|
|
3422
|
+
if (!pythLazerMsgHexGetter) {
|
|
3423
|
+
console.error(
|
|
3424
|
+
'pythLazerMsgHexGetter is required to crank pyth lazer oracles'
|
|
3425
|
+
);
|
|
3426
|
+
return [];
|
|
3427
|
+
}
|
|
3428
|
+
|
|
3429
|
+
const pythLazerFeedIds = pythLazerOracles
|
|
3430
|
+
.map((config) => config.pythLazerId)
|
|
3431
|
+
.filter(Boolean) as number[];
|
|
3432
|
+
const pythLazerMsgHex = await pythLazerMsgHexGetter(pythLazerFeedIds);
|
|
3433
|
+
|
|
3434
|
+
const oracleUpdateIxs =
|
|
3435
|
+
await this.driftClient.getPostPythLazerOracleUpdateIxs(
|
|
3436
|
+
pythLazerFeedIds,
|
|
3437
|
+
pythLazerMsgHex,
|
|
3438
|
+
undefined,
|
|
3439
|
+
3
|
|
3440
|
+
);
|
|
3441
|
+
|
|
3442
|
+
return oracleUpdateIxs;
|
|
3443
|
+
} catch (err) {
|
|
3444
|
+
console.error('Error cranking pyth lazer oracles', err);
|
|
3445
|
+
return [];
|
|
3446
|
+
}
|
|
3447
|
+
}
|
|
3448
|
+
|
|
3449
|
+
public async getOracleFeedsToCrankIxs(
|
|
3450
|
+
oracleFeedsToCrank: TxParams['oracleFeedsToCrank']
|
|
3451
|
+
) {
|
|
3452
|
+
if (!oracleFeedsToCrank?.feedsToCrank) {
|
|
3453
|
+
return [];
|
|
3454
|
+
}
|
|
3455
|
+
|
|
3456
|
+
return await this.getPythLazerOracleCrankIxs(
|
|
3457
|
+
oracleFeedsToCrank.feedsToCrank,
|
|
3458
|
+
oracleFeedsToCrank.pythLazerMsgHexGetter
|
|
3459
|
+
);
|
|
3460
|
+
}
|
|
3461
|
+
|
|
3462
|
+
public async updateVaultProtocol(
|
|
3463
|
+
vault: PublicKey,
|
|
3464
|
+
params: {
|
|
3465
|
+
protocolFee: BN | null;
|
|
3466
|
+
protocolProfitShare: number | null;
|
|
3467
|
+
},
|
|
3468
|
+
txParams?: TxParams
|
|
3469
|
+
): Promise<TransactionSignature> {
|
|
3470
|
+
const ix = await this.getUpdateVaultProtocolIx(vault, params);
|
|
3471
|
+
return await this.createAndSendTxn([ix], txParams);
|
|
3472
|
+
}
|
|
3473
|
+
|
|
3474
|
+
public async getUpdateVaultProtocolIx(
|
|
3475
|
+
vault: PublicKey,
|
|
3476
|
+
params: {
|
|
3477
|
+
protocolFee: BN | null;
|
|
3478
|
+
protocolProfitShare: number | null;
|
|
3479
|
+
}
|
|
3480
|
+
): Promise<TransactionInstruction> {
|
|
3481
|
+
return this.program.methods
|
|
3482
|
+
.updateVaultProtocol(params)
|
|
3483
|
+
.accounts({
|
|
3484
|
+
vault,
|
|
3485
|
+
vaultProtocol: this.getVaultProtocolAddress(vault),
|
|
3486
|
+
})
|
|
3487
|
+
.instruction();
|
|
3488
|
+
}
|
|
3489
|
+
|
|
3490
|
+
public async adminInitFeeUpdate(
|
|
3491
|
+
vault: PublicKey,
|
|
3492
|
+
uiTxParams?: TxParams
|
|
3493
|
+
): Promise<TransactionSignature> {
|
|
3494
|
+
const ix = await this.getAdminInitFeeUpdateIx(vault);
|
|
3495
|
+
return await this.createAndSendTxn([ix], uiTxParams);
|
|
3496
|
+
}
|
|
3497
|
+
|
|
3498
|
+
public async getAdminInitFeeUpdateIx(
|
|
3499
|
+
vault: PublicKey
|
|
3500
|
+
): Promise<TransactionInstruction> {
|
|
3501
|
+
const feeUpdate = getFeeUpdateAddressSync(this.program.programId, vault);
|
|
3502
|
+
|
|
3503
|
+
return this.program.instruction.adminInitFeeUpdate({
|
|
3504
|
+
accounts: {
|
|
3505
|
+
vault,
|
|
3506
|
+
admin: this.driftClient.wallet.publicKey,
|
|
3507
|
+
feeUpdate,
|
|
3508
|
+
systemProgram: SystemProgram.programId,
|
|
3509
|
+
},
|
|
3510
|
+
});
|
|
3511
|
+
}
|
|
3512
|
+
|
|
3513
|
+
public async adminDeleteFeeUpdate(
|
|
3514
|
+
vault: PublicKey,
|
|
3515
|
+
uiTxParams?: TxParams
|
|
3516
|
+
): Promise<TransactionSignature> {
|
|
3517
|
+
const ix = await this.getAdminDeleteFeeUpdateIx(vault);
|
|
3518
|
+
return await this.createAndSendTxn([ix], uiTxParams);
|
|
3519
|
+
}
|
|
3520
|
+
|
|
3521
|
+
public async getAdminDeleteFeeUpdateIx(
|
|
3522
|
+
vault: PublicKey
|
|
3523
|
+
): Promise<TransactionInstruction> {
|
|
3524
|
+
const feeUpdate = getFeeUpdateAddressSync(this.program.programId, vault);
|
|
3525
|
+
|
|
3526
|
+
return this.program.instruction.adminDeleteFeeUpdate({
|
|
3527
|
+
accounts: {
|
|
3528
|
+
vault,
|
|
3529
|
+
admin: this.driftClient.wallet.publicKey,
|
|
3530
|
+
feeUpdate,
|
|
3531
|
+
},
|
|
3532
|
+
});
|
|
3533
|
+
}
|
|
3534
|
+
|
|
3535
|
+
public async adminUpdateVaultClass(
|
|
3536
|
+
vault: PublicKey,
|
|
3537
|
+
newVaultClass: VaultClass,
|
|
3538
|
+
uiTxParams?: TxParams
|
|
3539
|
+
): Promise<TransactionSignature> {
|
|
3540
|
+
const ix = await this.getAdminUpdateVaultClassIx(vault, newVaultClass);
|
|
3541
|
+
return await this.createAndSendTxn([ix], uiTxParams);
|
|
3542
|
+
}
|
|
3543
|
+
|
|
3544
|
+
public async getAdminUpdateVaultClassIx(
|
|
3545
|
+
vault: PublicKey,
|
|
3546
|
+
newVaultClass: VaultClass
|
|
3547
|
+
): Promise<TransactionInstruction> {
|
|
3548
|
+
return this.program.methods
|
|
3549
|
+
.adminUpdateVaultClass(newVaultClass as any)
|
|
3550
|
+
.accounts({
|
|
3551
|
+
vault,
|
|
3552
|
+
admin: this.driftClient.wallet.publicKey,
|
|
3553
|
+
})
|
|
3554
|
+
.instruction();
|
|
3555
|
+
}
|
|
3556
|
+
|
|
3557
|
+
public async managerUpdateFees(
|
|
3558
|
+
vault: PublicKey,
|
|
3559
|
+
params: {
|
|
3560
|
+
timelockDuration: BN;
|
|
3561
|
+
newManagementFee: BN | null;
|
|
3562
|
+
newProfitShare: number | null;
|
|
3563
|
+
newHurdleRate: number | null;
|
|
3564
|
+
},
|
|
3565
|
+
uiTxParams?: TxParams
|
|
3566
|
+
): Promise<TransactionSignature> {
|
|
3567
|
+
const feeUpdate = getFeeUpdateAddressSync(this.program.programId, vault);
|
|
3568
|
+
const ixs: TransactionInstruction[] = [];
|
|
3569
|
+
if (!(await this.checkIfAccountExists(feeUpdate))) {
|
|
3570
|
+
throw new Error(
|
|
3571
|
+
'Fee update account does not exist, it must be created by an admin first'
|
|
3572
|
+
);
|
|
3573
|
+
}
|
|
3574
|
+
ixs.push(await this.getManagerUpdateFeesIx(vault, params));
|
|
3575
|
+
return await this.createAndSendTxn(ixs, uiTxParams);
|
|
3576
|
+
}
|
|
3577
|
+
|
|
3578
|
+
public async getManagerUpdateFeesIx(
|
|
3579
|
+
vault: PublicKey,
|
|
3580
|
+
params: {
|
|
3581
|
+
timelockDuration: BN;
|
|
3582
|
+
newManagementFee: BN | null;
|
|
3583
|
+
newProfitShare: number | null;
|
|
3584
|
+
newHurdleRate: number | null;
|
|
3585
|
+
}
|
|
3586
|
+
): Promise<TransactionInstruction> {
|
|
3587
|
+
const vaultAccount = await this.program.account.vault.fetch(vault);
|
|
3588
|
+
const feeUpdate = getFeeUpdateAddressSync(this.program.programId, vault);
|
|
3589
|
+
|
|
3590
|
+
return this.program.instruction.managerUpdateFees(params, {
|
|
3591
|
+
accounts: {
|
|
3592
|
+
vault,
|
|
3593
|
+
manager: vaultAccount.manager,
|
|
3594
|
+
feeUpdate,
|
|
3595
|
+
},
|
|
3596
|
+
});
|
|
3597
|
+
}
|
|
3598
|
+
|
|
3599
|
+
public async managerCancelFeeUpdate(
|
|
3600
|
+
vault: PublicKey,
|
|
3601
|
+
uiTxParams?: TxParams
|
|
3602
|
+
): Promise<TransactionSignature> {
|
|
3603
|
+
const ix = await this.getManagerCancelFeeUpdateIx(vault);
|
|
3604
|
+
return await this.createAndSendTxn([ix], uiTxParams);
|
|
3605
|
+
}
|
|
3606
|
+
|
|
3607
|
+
public async getManagerCancelFeeUpdateIx(
|
|
3608
|
+
vault: PublicKey
|
|
3609
|
+
): Promise<TransactionInstruction> {
|
|
3610
|
+
const vaultAccount = await this.program.account.vault.fetch(vault);
|
|
3611
|
+
const feeUpdate = getFeeUpdateAddressSync(this.program.programId, vault);
|
|
3612
|
+
|
|
3613
|
+
return this.program.instruction.managerCancelFeeUpdate({
|
|
3614
|
+
accounts: {
|
|
3615
|
+
vault,
|
|
3616
|
+
manager: vaultAccount.manager,
|
|
3617
|
+
feeUpdate,
|
|
3618
|
+
},
|
|
3619
|
+
});
|
|
3620
|
+
}
|
|
3621
|
+
|
|
3622
|
+
/**
|
|
3623
|
+
* Calculate the vault share price (base value per share)
|
|
3624
|
+
* @param params vault address or vault account, and precision exponent
|
|
3625
|
+
* @returns BigNum representing the base value per share
|
|
3626
|
+
*/
|
|
3627
|
+
public async calcVaultSharePrice(params: {
|
|
3628
|
+
address?: PublicKey;
|
|
3629
|
+
vault?: Vault;
|
|
3630
|
+
}): Promise<BigNum> {
|
|
3631
|
+
let spotPrecisionExp = QUOTE_PRECISION_EXP;
|
|
3632
|
+
|
|
3633
|
+
try {
|
|
3634
|
+
let vaultAccount: Vault;
|
|
3635
|
+
if (params.address !== undefined) {
|
|
3636
|
+
vaultAccount = await this.program.account.vault.fetch(params.address);
|
|
3637
|
+
} else if (params.vault !== undefined) {
|
|
3638
|
+
vaultAccount = params.vault;
|
|
3639
|
+
} else {
|
|
3640
|
+
throw new Error('Must supply address or vault');
|
|
3641
|
+
}
|
|
3642
|
+
|
|
3643
|
+
const spotMarket = this.driftClient.getSpotMarketAccount(
|
|
3644
|
+
vaultAccount.spotMarketIndex
|
|
3645
|
+
);
|
|
3646
|
+
spotPrecisionExp = new BN(spotMarket!.decimals);
|
|
3647
|
+
|
|
3648
|
+
const totalAccountValue = await this.calculateVaultEquityInDepositAsset({
|
|
3649
|
+
vault: vaultAccount,
|
|
3650
|
+
factorUnrealizedPNL: true,
|
|
3651
|
+
});
|
|
3652
|
+
|
|
3653
|
+
const vaultTotalShares = vaultAccount.totalShares.toString();
|
|
3654
|
+
const totalAccountValueStr = totalAccountValue.toString();
|
|
3655
|
+
|
|
3656
|
+
return vaultTotalShares === '0'
|
|
3657
|
+
? BigNum.from(0, spotPrecisionExp)
|
|
3658
|
+
: new BigNum(totalAccountValueStr, spotPrecisionExp)
|
|
3659
|
+
.shift(VAULT_SHARES_PRECISION_EXP)
|
|
3660
|
+
.div(new BigNum(vaultTotalShares, VAULT_SHARES_PRECISION_EXP));
|
|
3661
|
+
} catch (err) {
|
|
3662
|
+
console.error('VaultClient calcVaultSharePrice error:', err);
|
|
3663
|
+
return BigNum.from(0, spotPrecisionExp);
|
|
3664
|
+
}
|
|
3665
|
+
}
|
|
3666
|
+
}
|