@drift-labs/sdk 2.85.0-beta.0 → 2.85.0-beta.10
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/VERSION +1 -1
- package/bun.lockb +0 -0
- package/lib/accounts/bulkAccountLoader.d.ts +3 -3
- package/lib/accounts/pollingDriftClientAccountSubscriber.js +10 -2
- package/lib/accounts/pollingUserAccountSubscriber.d.ts +4 -3
- package/lib/accounts/pollingUserAccountSubscriber.js +7 -6
- package/lib/accounts/testBulkAccountLoader.d.ts +4 -0
- package/lib/accounts/testBulkAccountLoader.js +45 -0
- package/lib/bankrun/bankrunConnection.d.ts +71 -0
- package/lib/bankrun/bankrunConnection.js +285 -0
- package/lib/blockhashSubscriber/BlockhashSubscriber.js +22 -15
- package/lib/constants/perpMarkets.js +10 -0
- package/lib/constants/spotMarkets.js +10 -0
- package/lib/driftClient.d.ts +2 -2
- package/lib/driftClient.js +14 -13
- package/lib/events/eventSubscriber.js +12 -4
- package/lib/idl/drift.json +28 -8
- package/lib/testClient.js +1 -2
- package/lib/tokenFaucet.d.ts +3 -1
- package/lib/tokenFaucet.js +41 -8
- package/lib/tx/txParamProcessor.d.ts +2 -1
- package/lib/tx/txParamProcessor.js +7 -3
- package/lib/types.d.ts +1 -0
- package/lib/user.js +1 -1
- package/package.json +3 -1
- package/src/accounts/bulkAccountLoader.ts +3 -2
- package/src/accounts/pollingDriftClientAccountSubscriber.ts +16 -3
- package/src/accounts/pollingUserAccountSubscriber.ts +13 -12
- package/src/accounts/testBulkAccountLoader.ts +53 -0
- package/src/bankrun/bankrunConnection.ts +466 -0
- package/src/blockhashSubscriber/BlockhashSubscriber.ts +24 -19
- package/src/constants/perpMarkets.ts +10 -0
- package/src/constants/spotMarkets.ts +10 -0
- package/src/driftClient.ts +21 -15
- package/src/events/eventSubscriber.ts +5 -0
- package/src/idl/drift.json +28 -8
- package/src/testClient.ts +1 -2
- package/src/tokenFaucet.ts +49 -12
- package/src/tx/txParamProcessor.ts +11 -2
- package/src/types.ts +1 -0
- package/src/user.ts +5 -2
|
@@ -0,0 +1,466 @@
|
|
|
1
|
+
import {
|
|
2
|
+
TransactionConfirmationStatus,
|
|
3
|
+
AccountInfo,
|
|
4
|
+
Keypair,
|
|
5
|
+
PublicKey,
|
|
6
|
+
Transaction,
|
|
7
|
+
RpcResponseAndContext,
|
|
8
|
+
Commitment,
|
|
9
|
+
TransactionSignature,
|
|
10
|
+
SignatureStatusConfig,
|
|
11
|
+
SignatureStatus,
|
|
12
|
+
GetVersionedTransactionConfig,
|
|
13
|
+
GetTransactionConfig,
|
|
14
|
+
VersionedTransaction,
|
|
15
|
+
SimulateTransactionConfig,
|
|
16
|
+
SimulatedTransactionResponse,
|
|
17
|
+
TransactionReturnData,
|
|
18
|
+
TransactionError,
|
|
19
|
+
SignatureResultCallback,
|
|
20
|
+
ClientSubscriptionId,
|
|
21
|
+
Connection as SolanaConnection,
|
|
22
|
+
SystemProgram,
|
|
23
|
+
Blockhash,
|
|
24
|
+
LogsFilter,
|
|
25
|
+
LogsCallback,
|
|
26
|
+
AccountChangeCallback,
|
|
27
|
+
LAMPORTS_PER_SOL,
|
|
28
|
+
} from '@solana/web3.js';
|
|
29
|
+
import {
|
|
30
|
+
ProgramTestContext,
|
|
31
|
+
BanksClient,
|
|
32
|
+
BanksTransactionResultWithMeta,
|
|
33
|
+
Clock,
|
|
34
|
+
} from 'solana-bankrun';
|
|
35
|
+
import { BankrunProvider } from 'anchor-bankrun';
|
|
36
|
+
import bs58 from 'bs58';
|
|
37
|
+
import { BN, Wallet } from '@coral-xyz/anchor';
|
|
38
|
+
import { Account, TOKEN_PROGRAM_ID, unpackAccount } from '@solana/spl-token';
|
|
39
|
+
|
|
40
|
+
export type Connection = SolanaConnection | BankrunConnection;
|
|
41
|
+
|
|
42
|
+
type BankrunTransactionMetaNormalized = {
|
|
43
|
+
logMessages: string[];
|
|
44
|
+
err: TransactionError;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
type BankrunTransactionRespose = {
|
|
48
|
+
slot: number;
|
|
49
|
+
meta: BankrunTransactionMetaNormalized;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export class BankrunContextWrapper {
|
|
53
|
+
public readonly connection: BankrunConnection;
|
|
54
|
+
public readonly context: ProgramTestContext;
|
|
55
|
+
public readonly provider: BankrunProvider;
|
|
56
|
+
public readonly commitment: Commitment = 'confirmed';
|
|
57
|
+
|
|
58
|
+
constructor(context: ProgramTestContext) {
|
|
59
|
+
this.context = context;
|
|
60
|
+
this.provider = new BankrunProvider(context);
|
|
61
|
+
this.connection = new BankrunConnection(
|
|
62
|
+
this.context.banksClient,
|
|
63
|
+
this.context
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async sendTransaction(
|
|
68
|
+
tx: Transaction,
|
|
69
|
+
additionalSigners?: Keypair[]
|
|
70
|
+
): Promise<TransactionSignature> {
|
|
71
|
+
tx.recentBlockhash = (await this.getLatestBlockhash()).toString();
|
|
72
|
+
tx.feePayer = this.context.payer.publicKey;
|
|
73
|
+
if (!additionalSigners) {
|
|
74
|
+
additionalSigners = [];
|
|
75
|
+
}
|
|
76
|
+
tx.sign(this.context.payer, ...additionalSigners);
|
|
77
|
+
return await this.connection.sendTransaction(tx);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async getMinimumBalanceForRentExemption(_: number): Promise<number> {
|
|
81
|
+
return 10 * LAMPORTS_PER_SOL;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async fundKeypair(
|
|
85
|
+
keypair: Keypair | Wallet,
|
|
86
|
+
lamports: number | bigint
|
|
87
|
+
): Promise<TransactionSignature> {
|
|
88
|
+
const ixs = [
|
|
89
|
+
SystemProgram.transfer({
|
|
90
|
+
fromPubkey: this.context.payer.publicKey,
|
|
91
|
+
toPubkey: keypair.publicKey,
|
|
92
|
+
lamports,
|
|
93
|
+
}),
|
|
94
|
+
];
|
|
95
|
+
const tx = new Transaction().add(...ixs);
|
|
96
|
+
return await this.sendTransaction(tx);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
async getLatestBlockhash(): Promise<Blockhash> {
|
|
100
|
+
const blockhash = await this.connection.getLatestBlockhash('finalized');
|
|
101
|
+
|
|
102
|
+
return blockhash.blockhash;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
printTxLogs(signature: string): void {
|
|
106
|
+
this.connection.printTxLogs(signature);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
async moveTimeForward(increment: number): Promise<void> {
|
|
110
|
+
const currentClock = await this.context.banksClient.getClock();
|
|
111
|
+
const newUnixTimestamp = currentClock.unixTimestamp + BigInt(increment);
|
|
112
|
+
const newClock = new Clock(
|
|
113
|
+
currentClock.slot,
|
|
114
|
+
currentClock.epochStartTimestamp,
|
|
115
|
+
currentClock.epoch,
|
|
116
|
+
currentClock.leaderScheduleEpoch,
|
|
117
|
+
newUnixTimestamp
|
|
118
|
+
);
|
|
119
|
+
await this.context.setClock(newClock);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export class BankrunConnection {
|
|
124
|
+
private readonly _banksClient: BanksClient;
|
|
125
|
+
private readonly context: ProgramTestContext;
|
|
126
|
+
private transactionToMeta: Map<
|
|
127
|
+
TransactionSignature,
|
|
128
|
+
BanksTransactionResultWithMeta
|
|
129
|
+
> = new Map();
|
|
130
|
+
private clock: Clock;
|
|
131
|
+
|
|
132
|
+
private nextClientSubscriptionId = 0;
|
|
133
|
+
private onLogCallbacks = new Map<number, LogsCallback>();
|
|
134
|
+
private onAccountChangeCallbacks = new Map<
|
|
135
|
+
number,
|
|
136
|
+
[PublicKey, AccountChangeCallback]
|
|
137
|
+
>();
|
|
138
|
+
|
|
139
|
+
constructor(banksClient: BanksClient, context: ProgramTestContext) {
|
|
140
|
+
this._banksClient = banksClient;
|
|
141
|
+
this.context = context;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
getSlot(): Promise<bigint> {
|
|
145
|
+
return this._banksClient.getSlot();
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
toConnection(): SolanaConnection {
|
|
149
|
+
return this as unknown as SolanaConnection;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
async getTokenAccount(publicKey: PublicKey): Promise<Account> {
|
|
153
|
+
const info = await this.getAccountInfo(publicKey);
|
|
154
|
+
return unpackAccount(publicKey, info, TOKEN_PROGRAM_ID);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
async getMultipleAccountsInfo(
|
|
158
|
+
publicKeys: PublicKey[],
|
|
159
|
+
_commitmentOrConfig?: Commitment
|
|
160
|
+
): Promise<AccountInfo<Buffer>[]> {
|
|
161
|
+
const accountInfos = [];
|
|
162
|
+
|
|
163
|
+
for (const publicKey of publicKeys) {
|
|
164
|
+
const accountInfo = await this.getAccountInfo(publicKey);
|
|
165
|
+
accountInfos.push(accountInfo);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
return accountInfos;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
async getAccountInfo(
|
|
172
|
+
publicKey: PublicKey
|
|
173
|
+
): Promise<null | AccountInfo<Buffer>> {
|
|
174
|
+
const parsedAccountInfo = await this.getParsedAccountInfo(publicKey);
|
|
175
|
+
return parsedAccountInfo ? parsedAccountInfo.value : null;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
async getAccountInfoAndContext(
|
|
179
|
+
publicKey: PublicKey,
|
|
180
|
+
_commitment?: Commitment
|
|
181
|
+
): Promise<RpcResponseAndContext<null | AccountInfo<Buffer>>> {
|
|
182
|
+
return await this.getParsedAccountInfo(publicKey);
|
|
183
|
+
}
|
|
184
|
+
async sendRawTransaction(
|
|
185
|
+
rawTransaction: Buffer | Uint8Array | Array<number>,
|
|
186
|
+
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
|
187
|
+
_options?: any
|
|
188
|
+
): Promise<TransactionSignature> {
|
|
189
|
+
const tx = Transaction.from(rawTransaction);
|
|
190
|
+
const signature = await this.sendTransaction(tx);
|
|
191
|
+
return signature;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
async sendTransaction(tx: Transaction): Promise<TransactionSignature> {
|
|
195
|
+
const banksTransactionMeta = await this._banksClient.tryProcessTransaction(
|
|
196
|
+
tx
|
|
197
|
+
);
|
|
198
|
+
if (banksTransactionMeta.result) {
|
|
199
|
+
throw new Error(banksTransactionMeta.result);
|
|
200
|
+
}
|
|
201
|
+
const signature = bs58.encode(tx.signatures[0].signature);
|
|
202
|
+
this.transactionToMeta.set(signature, banksTransactionMeta);
|
|
203
|
+
let finalizedCount = 0;
|
|
204
|
+
while (finalizedCount < 10) {
|
|
205
|
+
const signatureStatus = (await this.getSignatureStatus(signature)).value
|
|
206
|
+
.confirmationStatus;
|
|
207
|
+
if (signatureStatus.toString() == '"finalized"') {
|
|
208
|
+
finalizedCount += 1;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// update the clock slot/timestamp
|
|
213
|
+
// sometimes race condition causes failures so we retry
|
|
214
|
+
try {
|
|
215
|
+
await this.updateSlotAndClock();
|
|
216
|
+
} catch (e) {
|
|
217
|
+
await this.updateSlotAndClock();
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
if (this.onLogCallbacks.size > 0) {
|
|
221
|
+
const transaction = await this.getTransaction(signature);
|
|
222
|
+
|
|
223
|
+
const context = { slot: transaction.slot };
|
|
224
|
+
const logs = {
|
|
225
|
+
logs: transaction.meta.logMessages,
|
|
226
|
+
err: transaction.meta.err,
|
|
227
|
+
signature,
|
|
228
|
+
};
|
|
229
|
+
for (const logCallback of this.onLogCallbacks.values()) {
|
|
230
|
+
logCallback(logs, context);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
for (const [
|
|
235
|
+
publicKey,
|
|
236
|
+
callback,
|
|
237
|
+
] of this.onAccountChangeCallbacks.values()) {
|
|
238
|
+
const accountInfo = await this.getParsedAccountInfo(publicKey);
|
|
239
|
+
callback(accountInfo.value, accountInfo.context);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
return signature;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
private async updateSlotAndClock() {
|
|
246
|
+
const currentSlot = await this.getSlot();
|
|
247
|
+
const nextSlot = currentSlot + BigInt(1);
|
|
248
|
+
this.context.warpToSlot(nextSlot);
|
|
249
|
+
const currentClock = await this._banksClient.getClock();
|
|
250
|
+
const newClock = new Clock(
|
|
251
|
+
nextSlot,
|
|
252
|
+
currentClock.epochStartTimestamp,
|
|
253
|
+
currentClock.epoch,
|
|
254
|
+
currentClock.leaderScheduleEpoch,
|
|
255
|
+
currentClock.unixTimestamp + BigInt(1)
|
|
256
|
+
);
|
|
257
|
+
this.context.setClock(newClock);
|
|
258
|
+
this.clock = newClock;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
getTime(): number {
|
|
262
|
+
return Number(this.clock.unixTimestamp);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
async getParsedAccountInfo(
|
|
266
|
+
publicKey: PublicKey
|
|
267
|
+
): Promise<RpcResponseAndContext<AccountInfo<Buffer>>> {
|
|
268
|
+
const accountInfoBytes = await this._banksClient.getAccount(publicKey);
|
|
269
|
+
if (accountInfoBytes === null) {
|
|
270
|
+
return {
|
|
271
|
+
context: { slot: Number(await this._banksClient.getSlot()) },
|
|
272
|
+
value: null,
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
accountInfoBytes.data = Buffer.from(accountInfoBytes.data);
|
|
276
|
+
const accountInfoBuffer = accountInfoBytes as AccountInfo<Buffer>;
|
|
277
|
+
return {
|
|
278
|
+
context: { slot: Number(await this._banksClient.getSlot()) },
|
|
279
|
+
value: accountInfoBuffer,
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
async getLatestBlockhash(commitment?: Commitment): Promise<
|
|
284
|
+
Readonly<{
|
|
285
|
+
blockhash: string;
|
|
286
|
+
lastValidBlockHeight: number;
|
|
287
|
+
}>
|
|
288
|
+
> {
|
|
289
|
+
const blockhashAndBlockheight = await this._banksClient.getLatestBlockhash(
|
|
290
|
+
commitment
|
|
291
|
+
);
|
|
292
|
+
return {
|
|
293
|
+
blockhash: blockhashAndBlockheight[0],
|
|
294
|
+
lastValidBlockHeight: Number(blockhashAndBlockheight[1]),
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
async getSignatureStatus(
|
|
299
|
+
signature: string,
|
|
300
|
+
_config?: SignatureStatusConfig
|
|
301
|
+
): Promise<RpcResponseAndContext<null | SignatureStatus>> {
|
|
302
|
+
const transactionStatus = await this._banksClient.getTransactionStatus(
|
|
303
|
+
signature
|
|
304
|
+
);
|
|
305
|
+
if (transactionStatus === null) {
|
|
306
|
+
return {
|
|
307
|
+
context: { slot: Number(await this._banksClient.getSlot()) },
|
|
308
|
+
value: null,
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
return {
|
|
312
|
+
context: { slot: Number(await this._banksClient.getSlot()) },
|
|
313
|
+
value: {
|
|
314
|
+
slot: Number(transactionStatus.slot),
|
|
315
|
+
confirmations: Number(transactionStatus.confirmations),
|
|
316
|
+
err: transactionStatus.err,
|
|
317
|
+
confirmationStatus:
|
|
318
|
+
transactionStatus.confirmationStatus as TransactionConfirmationStatus,
|
|
319
|
+
},
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* There's really no direct equivalent to getTransaction exposed by SolanaProgramTest, so we do the best that we can here - it's a little hacky.
|
|
325
|
+
*/
|
|
326
|
+
async getTransaction(
|
|
327
|
+
signature: string,
|
|
328
|
+
_rawConfig?: GetTransactionConfig | GetVersionedTransactionConfig
|
|
329
|
+
): Promise<BankrunTransactionRespose | null> {
|
|
330
|
+
const txMeta = this.transactionToMeta.get(
|
|
331
|
+
signature as TransactionSignature
|
|
332
|
+
);
|
|
333
|
+
if (txMeta === undefined) {
|
|
334
|
+
return null;
|
|
335
|
+
}
|
|
336
|
+
const transactionStatus = await this._banksClient.getTransactionStatus(
|
|
337
|
+
signature
|
|
338
|
+
);
|
|
339
|
+
const meta: BankrunTransactionMetaNormalized = {
|
|
340
|
+
logMessages: txMeta.meta.logMessages,
|
|
341
|
+
err: txMeta.result,
|
|
342
|
+
};
|
|
343
|
+
return {
|
|
344
|
+
slot: Number(transactionStatus.slot),
|
|
345
|
+
meta,
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
findComputeUnitConsumption(signature: string): bigint {
|
|
350
|
+
const txMeta = this.transactionToMeta.get(
|
|
351
|
+
signature as TransactionSignature
|
|
352
|
+
);
|
|
353
|
+
if (txMeta === undefined) {
|
|
354
|
+
throw new Error('Transaction not found');
|
|
355
|
+
}
|
|
356
|
+
return txMeta.meta.computeUnitsConsumed;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
printTxLogs(signature: string): void {
|
|
360
|
+
const txMeta = this.transactionToMeta.get(
|
|
361
|
+
signature as TransactionSignature
|
|
362
|
+
);
|
|
363
|
+
if (txMeta === undefined) {
|
|
364
|
+
throw new Error('Transaction not found');
|
|
365
|
+
}
|
|
366
|
+
console.log(txMeta.meta.logMessages);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
async simulateTransaction(
|
|
370
|
+
transaction: Transaction | VersionedTransaction,
|
|
371
|
+
_config?: SimulateTransactionConfig
|
|
372
|
+
): Promise<RpcResponseAndContext<SimulatedTransactionResponse>> {
|
|
373
|
+
const simulationResult = await this._banksClient.simulateTransaction(
|
|
374
|
+
transaction
|
|
375
|
+
);
|
|
376
|
+
const returnDataProgramId =
|
|
377
|
+
simulationResult.meta?.returnData?.programId.toBase58();
|
|
378
|
+
const returnDataNormalized = Buffer.from(
|
|
379
|
+
simulationResult.meta?.returnData?.data
|
|
380
|
+
).toString('base64');
|
|
381
|
+
const returnData: TransactionReturnData = {
|
|
382
|
+
programId: returnDataProgramId,
|
|
383
|
+
data: [returnDataNormalized, 'base64'],
|
|
384
|
+
};
|
|
385
|
+
return {
|
|
386
|
+
context: { slot: Number(await this._banksClient.getSlot()) },
|
|
387
|
+
value: {
|
|
388
|
+
err: simulationResult.result,
|
|
389
|
+
logs: simulationResult.meta.logMessages,
|
|
390
|
+
accounts: undefined,
|
|
391
|
+
unitsConsumed: Number(simulationResult.meta.computeUnitsConsumed),
|
|
392
|
+
returnData,
|
|
393
|
+
},
|
|
394
|
+
};
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
onSignature(
|
|
398
|
+
signature: string,
|
|
399
|
+
callback: SignatureResultCallback,
|
|
400
|
+
commitment?: Commitment
|
|
401
|
+
): ClientSubscriptionId {
|
|
402
|
+
const txMeta = this.transactionToMeta.get(
|
|
403
|
+
signature as TransactionSignature
|
|
404
|
+
);
|
|
405
|
+
this._banksClient.getSlot(commitment).then((slot) => {
|
|
406
|
+
if (txMeta) {
|
|
407
|
+
callback({ err: txMeta.result }, { slot: Number(slot) });
|
|
408
|
+
}
|
|
409
|
+
});
|
|
410
|
+
return 0;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
async removeSignatureListener(_clientSubscriptionId: number): Promise<void> {
|
|
414
|
+
// Nothing actually has to happen here! Pretty cool, huh?
|
|
415
|
+
// This function signature only exists to match the web3js interface
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
onLogs(
|
|
419
|
+
filter: LogsFilter,
|
|
420
|
+
callback: LogsCallback,
|
|
421
|
+
_commitment?: Commitment
|
|
422
|
+
): ClientSubscriptionId {
|
|
423
|
+
const subscriptId = this.nextClientSubscriptionId;
|
|
424
|
+
|
|
425
|
+
this.onLogCallbacks.set(subscriptId, callback);
|
|
426
|
+
|
|
427
|
+
this.nextClientSubscriptionId += 1;
|
|
428
|
+
|
|
429
|
+
return subscriptId;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
async removeOnLogsListener(
|
|
433
|
+
clientSubscriptionId: ClientSubscriptionId
|
|
434
|
+
): Promise<void> {
|
|
435
|
+
this.onLogCallbacks.delete(clientSubscriptionId);
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
onAccountChange(
|
|
439
|
+
publicKey: PublicKey,
|
|
440
|
+
callback: AccountChangeCallback,
|
|
441
|
+
// @ts-ignore
|
|
442
|
+
_commitment?: Commitment
|
|
443
|
+
): ClientSubscriptionId {
|
|
444
|
+
const subscriptId = this.nextClientSubscriptionId;
|
|
445
|
+
|
|
446
|
+
this.onAccountChangeCallbacks.set(subscriptId, [publicKey, callback]);
|
|
447
|
+
|
|
448
|
+
this.nextClientSubscriptionId += 1;
|
|
449
|
+
|
|
450
|
+
return subscriptId;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
async removeAccountChangeListener(
|
|
454
|
+
clientSubscriptionId: ClientSubscriptionId
|
|
455
|
+
): Promise<void> {
|
|
456
|
+
this.onAccountChangeCallbacks.delete(clientSubscriptionId);
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
async getMinimumBalanceForRentExemption(_: number): Promise<number> {
|
|
460
|
+
return 10 * LAMPORTS_PER_SOL;
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
export function asBN(value: number | bigint): BN {
|
|
465
|
+
return new BN(Number(value));
|
|
466
|
+
}
|
|
@@ -75,27 +75,32 @@ export class BlockhashSubscriber {
|
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
async updateBlockhash() {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
if (
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
78
|
+
try {
|
|
79
|
+
const [resp, lastConfirmedBlockHeight] = await Promise.all([
|
|
80
|
+
this.connection.getLatestBlockhashAndContext({
|
|
81
|
+
commitment: this.commitment,
|
|
82
|
+
}),
|
|
83
|
+
this.connection.getBlockHeight({ commitment: this.commitment }),
|
|
84
|
+
]);
|
|
85
|
+
this.latestBlockHeight = lastConfirmedBlockHeight;
|
|
86
|
+
this.latestBlockHeightContext = resp.context;
|
|
87
|
+
|
|
88
|
+
// avoid caching duplicate blockhashes
|
|
89
|
+
if (this.blockhashes.length > 0) {
|
|
90
|
+
if (
|
|
91
|
+
resp.value.blockhash ===
|
|
92
|
+
this.blockhashes[this.blockhashes.length - 1].blockhash
|
|
93
|
+
) {
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
94
96
|
}
|
|
95
|
-
}
|
|
96
97
|
|
|
97
|
-
|
|
98
|
-
|
|
98
|
+
this.blockhashes.push(resp.value);
|
|
99
|
+
} catch (e) {
|
|
100
|
+
console.error('Error updating blockhash:\n', e);
|
|
101
|
+
} finally {
|
|
102
|
+
this.pruneBlockhashes();
|
|
103
|
+
}
|
|
99
104
|
}
|
|
100
105
|
|
|
101
106
|
async subscribe() {
|
|
@@ -597,6 +597,16 @@ export const MainnetPerpMarkets: PerpMarketConfig[] = [
|
|
|
597
597
|
launchTs: 1718021389000,
|
|
598
598
|
oracleSource: OracleSource.SWITCHBOARD,
|
|
599
599
|
},
|
|
600
|
+
{
|
|
601
|
+
fullName: 'ZEX',
|
|
602
|
+
category: ['DEX', 'Solana'],
|
|
603
|
+
symbol: 'ZEX-PERP',
|
|
604
|
+
baseAssetSymbol: 'ZEX',
|
|
605
|
+
marketIndex: 33,
|
|
606
|
+
oracle: new PublicKey('4gdbqxkMrF1bYVeEJKRmTqCCvJjRCZrRhxvriGY6SwLj'),
|
|
607
|
+
launchTs: 1719415157000,
|
|
608
|
+
oracleSource: OracleSource.SWITCHBOARD,
|
|
609
|
+
},
|
|
600
610
|
];
|
|
601
611
|
|
|
602
612
|
export const PerpMarkets: { [key in DriftEnv]: PerpMarketConfig[] } = {
|
|
@@ -283,6 +283,16 @@ export const MainnetSpotMarkets: SpotMarketConfig[] = [
|
|
|
283
283
|
precisionExp: SIX,
|
|
284
284
|
launchTs: 1718811089000,
|
|
285
285
|
},
|
|
286
|
+
{
|
|
287
|
+
symbol: 'JLP',
|
|
288
|
+
marketIndex: 19,
|
|
289
|
+
oracle: new PublicKey('pmHEXBam7kbmCCg5ED5V7RNMN8e34sKu338KeuFAGof'),
|
|
290
|
+
oracleSource: OracleSource.SWITCHBOARD,
|
|
291
|
+
mint: new PublicKey('27G8MtK7VtTcCHkpASjSDdkWWYfoqT6ggEuKidVJidD4'),
|
|
292
|
+
precision: new BN(10).pow(SIX),
|
|
293
|
+
precisionExp: SIX,
|
|
294
|
+
launchTs: 1719415157000,
|
|
295
|
+
},
|
|
286
296
|
];
|
|
287
297
|
|
|
288
298
|
export const SpotMarkets: { [key in DriftEnv]: SpotMarketConfig[] } = {
|
package/src/driftClient.ts
CHANGED
|
@@ -1527,6 +1527,7 @@ export class DriftClient {
|
|
|
1527
1527
|
|
|
1528
1528
|
if (params.useMarketLastSlotCache) {
|
|
1529
1529
|
const lastUserSlot = this.getUserAccountAndSlot()?.slot;
|
|
1530
|
+
|
|
1530
1531
|
for (const [
|
|
1531
1532
|
marketIndex,
|
|
1532
1533
|
slot,
|
|
@@ -2769,7 +2770,7 @@ export class DriftClient {
|
|
|
2769
2770
|
}
|
|
2770
2771
|
|
|
2771
2772
|
public async sendSignedTx(
|
|
2772
|
-
tx: Transaction,
|
|
2773
|
+
tx: Transaction | VersionedTransaction,
|
|
2773
2774
|
opts?: ConfirmOptions
|
|
2774
2775
|
): Promise<TransactionSignature> {
|
|
2775
2776
|
const { txSig } = await this.sendTransaction(
|
|
@@ -4117,6 +4118,10 @@ export class DriftClient {
|
|
|
4117
4118
|
const outMarket = this.getSpotMarketAccount(outMarketIndex);
|
|
4118
4119
|
const inMarket = this.getSpotMarketAccount(inMarketIndex);
|
|
4119
4120
|
|
|
4121
|
+
const isExactOut = swapMode === 'ExactOut' || quote.swapMode === 'ExactOut';
|
|
4122
|
+
const amountIn = new BN(quote.inAmount);
|
|
4123
|
+
const exactOutBufferedAmountIn = amountIn.muln(1001).divn(1000); // Add 10bp buffer
|
|
4124
|
+
|
|
4120
4125
|
if (!quote) {
|
|
4121
4126
|
const fetchedQuote = await jupiterClient.getQuote({
|
|
4122
4127
|
inputMint: inMarket.mint,
|
|
@@ -4197,7 +4202,7 @@ export class DriftClient {
|
|
|
4197
4202
|
const { beginSwapIx, endSwapIx } = await this.getSwapIx({
|
|
4198
4203
|
outMarketIndex,
|
|
4199
4204
|
inMarketIndex,
|
|
4200
|
-
amountIn:
|
|
4205
|
+
amountIn: isExactOut ? exactOutBufferedAmountIn : amountIn,
|
|
4201
4206
|
inTokenAccount: inAssociatedTokenAccount,
|
|
4202
4207
|
outTokenAccount: outAssociatedTokenAccount,
|
|
4203
4208
|
reduceOnly,
|
|
@@ -4663,7 +4668,8 @@ export class DriftClient {
|
|
|
4663
4668
|
await TransactionParamProcessor.getTxSimComputeUnits(
|
|
4664
4669
|
placeAndTakeTxToSim,
|
|
4665
4670
|
this.connection,
|
|
4666
|
-
txParams.computeUnitsBufferMultiplier ?? 1.2
|
|
4671
|
+
txParams.computeUnitsBufferMultiplier ?? 1.2,
|
|
4672
|
+
txParams.lowerBoundCu
|
|
4667
4673
|
);
|
|
4668
4674
|
|
|
4669
4675
|
if (shouldExitIfSimulationFails && !simulationResult.success) {
|
|
@@ -4783,6 +4789,10 @@ export class DriftClient {
|
|
|
4783
4789
|
exitEarlyIfSimFails
|
|
4784
4790
|
);
|
|
4785
4791
|
|
|
4792
|
+
if (!txsToSign) {
|
|
4793
|
+
return null;
|
|
4794
|
+
}
|
|
4795
|
+
|
|
4786
4796
|
const signedTxs = (
|
|
4787
4797
|
await this.txHandler.getSignedTransactionMap(
|
|
4788
4798
|
txsToSign,
|
|
@@ -6306,7 +6316,8 @@ export class DriftClient {
|
|
|
6306
6316
|
public async getAddInsuranceFundStakeIx(
|
|
6307
6317
|
marketIndex: number,
|
|
6308
6318
|
amount: BN,
|
|
6309
|
-
collateralAccountPublicKey: PublicKey
|
|
6319
|
+
collateralAccountPublicKey: PublicKey,
|
|
6320
|
+
fromSubAccount?: boolean
|
|
6310
6321
|
): Promise<TransactionInstruction> {
|
|
6311
6322
|
const spotMarket = this.getSpotMarketAccount(marketIndex);
|
|
6312
6323
|
const ifStakeAccountPublicKey = getInsuranceFundStakeAccountPublicKey(
|
|
@@ -6316,8 +6327,8 @@ export class DriftClient {
|
|
|
6316
6327
|
);
|
|
6317
6328
|
|
|
6318
6329
|
const remainingAccounts = this.getRemainingAccounts({
|
|
6319
|
-
userAccounts: [this.getUserAccount()],
|
|
6320
|
-
useMarketLastSlotCache: true,
|
|
6330
|
+
userAccounts: fromSubAccount ? [this.getUserAccount()] : [],
|
|
6331
|
+
useMarketLastSlotCache: fromSubAccount ? true : false,
|
|
6321
6332
|
writableSpotMarketIndexes: [marketIndex],
|
|
6322
6333
|
});
|
|
6323
6334
|
|
|
@@ -6416,7 +6427,8 @@ export class DriftClient {
|
|
|
6416
6427
|
const addFundsIx = await this.getAddInsuranceFundStakeIx(
|
|
6417
6428
|
marketIndex,
|
|
6418
6429
|
amount,
|
|
6419
|
-
tokenAccount
|
|
6430
|
+
tokenAccount,
|
|
6431
|
+
fromSubaccount
|
|
6420
6432
|
);
|
|
6421
6433
|
|
|
6422
6434
|
addIfStakeIxs.push(addFundsIx);
|
|
@@ -6456,8 +6468,7 @@ export class DriftClient {
|
|
|
6456
6468
|
);
|
|
6457
6469
|
|
|
6458
6470
|
const remainingAccounts = this.getRemainingAccounts({
|
|
6459
|
-
userAccounts: [
|
|
6460
|
-
useMarketLastSlotCache: true,
|
|
6471
|
+
userAccounts: [],
|
|
6461
6472
|
writableSpotMarketIndexes: [marketIndex],
|
|
6462
6473
|
});
|
|
6463
6474
|
|
|
@@ -6566,13 +6577,8 @@ export class DriftClient {
|
|
|
6566
6577
|
}
|
|
6567
6578
|
}
|
|
6568
6579
|
|
|
6569
|
-
const userAccountExists =
|
|
6570
|
-
!!this.getUser()?.accountSubscriber?.isSubscribed &&
|
|
6571
|
-
(await this.checkIfAccountExists(this.getUser().userAccountPublicKey));
|
|
6572
|
-
|
|
6573
6580
|
const remainingAccounts = this.getRemainingAccounts({
|
|
6574
|
-
userAccounts:
|
|
6575
|
-
useMarketLastSlotCache: false,
|
|
6581
|
+
userAccounts: [],
|
|
6576
6582
|
writableSpotMarketIndexes: [marketIndex],
|
|
6577
6583
|
});
|
|
6578
6584
|
|
|
@@ -45,6 +45,7 @@ export class EventSubscriber {
|
|
|
45
45
|
|
|
46
46
|
if (this.options.logProviderConfig.type === 'websocket') {
|
|
47
47
|
this.logProvider = new WebSocketLogProvider(
|
|
48
|
+
// @ts-ignore
|
|
48
49
|
this.connection,
|
|
49
50
|
this.address,
|
|
50
51
|
this.options.commitment,
|
|
@@ -52,6 +53,7 @@ export class EventSubscriber {
|
|
|
52
53
|
);
|
|
53
54
|
} else {
|
|
54
55
|
this.logProvider = new PollingLogProvider(
|
|
56
|
+
// @ts-ignore
|
|
55
57
|
this.connection,
|
|
56
58
|
this.address,
|
|
57
59
|
options.commitment,
|
|
@@ -101,6 +103,7 @@ export class EventSubscriber {
|
|
|
101
103
|
this.logProvider.eventEmitter.removeAllListeners('reconnect');
|
|
102
104
|
this.unsubscribe().then(() => {
|
|
103
105
|
this.logProvider = new PollingLogProvider(
|
|
106
|
+
// @ts-ignore
|
|
104
107
|
this.connection,
|
|
105
108
|
this.address,
|
|
106
109
|
this.options.commitment,
|
|
@@ -148,6 +151,7 @@ export class EventSubscriber {
|
|
|
148
151
|
}
|
|
149
152
|
|
|
150
153
|
const wrappedEvents = this.parseEventsFromLogs(txSig, slot, logs);
|
|
154
|
+
|
|
151
155
|
for (const wrappedEvent of wrappedEvents) {
|
|
152
156
|
this.eventListMap.get(wrappedEvent.eventType).insert(wrappedEvent);
|
|
153
157
|
}
|
|
@@ -188,6 +192,7 @@ export class EventSubscriber {
|
|
|
188
192
|
const untilTx: TransactionSignature = this.options.untilTx;
|
|
189
193
|
while (txFetched < this.options.maxTx) {
|
|
190
194
|
const response = await fetchLogs(
|
|
195
|
+
// @ts-ignore
|
|
191
196
|
this.connection,
|
|
192
197
|
this.address,
|
|
193
198
|
this.options.commitment === 'finalized' ? 'finalized' : 'confirmed',
|