@drift-labs/sdk 2.30.0-beta.0 → 2.30.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/lib/accounts/pollingTokenAccountSubscriber.d.ts +3 -3
  2. package/lib/accounts/pollingTokenAccountSubscriber.js +5 -2
  3. package/lib/accounts/types.d.ts +3 -3
  4. package/lib/constants/spotMarkets.js +10 -0
  5. package/lib/dlob/DLOB.d.ts +20 -1
  6. package/lib/dlob/DLOB.js +39 -0
  7. package/lib/driftClient.d.ts +51 -2
  8. package/lib/driftClient.js +162 -13
  9. package/lib/events/types.d.ts +3 -2
  10. package/lib/events/types.js +1 -0
  11. package/lib/examples/makeTradeExample.js +1 -1
  12. package/lib/idl/drift.json +279 -2
  13. package/lib/index.d.ts +1 -0
  14. package/lib/index.js +1 -0
  15. package/lib/jupiter/jupiterClient.d.ts +86 -0
  16. package/lib/jupiter/jupiterClient.js +109 -0
  17. package/lib/math/spotBalance.d.ts +41 -0
  18. package/lib/math/spotBalance.js +41 -0
  19. package/lib/token/index.d.ts +3 -2
  20. package/lib/token/index.js +9 -32
  21. package/lib/tokenFaucet.d.ts +3 -3
  22. package/lib/tokenFaucet.js +4 -9
  23. package/lib/tx/retryTxSender.js +3 -0
  24. package/lib/types.d.ts +29 -1
  25. package/lib/types.js +6 -1
  26. package/lib/wallet.d.ts +5 -3
  27. package/lib/wallet.js +19 -7
  28. package/package.json +2 -2
  29. package/src/accounts/pollingTokenAccountSubscriber.ts +8 -5
  30. package/src/accounts/types.ts +3 -3
  31. package/src/assert/assert.js +9 -0
  32. package/src/constants/spotMarkets.ts +11 -0
  33. package/src/dlob/DLOB.ts +75 -0
  34. package/src/driftClient.ts +288 -37
  35. package/src/events/types.ts +5 -1
  36. package/src/examples/makeTradeExample.ts +2 -4
  37. package/src/idl/drift.json +279 -2
  38. package/src/index.ts +1 -0
  39. package/src/jupiter/jupiterClient.ts +214 -0
  40. package/src/math/spotBalance.ts +41 -0
  41. package/src/token/index.js +38 -0
  42. package/src/token/index.ts +12 -36
  43. package/src/tokenFaucet.ts +15 -34
  44. package/src/tx/retryTxSender.ts +4 -0
  45. package/src/types.ts +32 -1
  46. package/src/util/computeUnits.js +27 -0
  47. package/src/util/promiseTimeout.js +14 -0
  48. package/src/util/tps.js +27 -0
  49. package/src/wallet.ts +34 -12
  50. package/tests/dlob/helpers.ts +9 -0
  51. package/tests/dlob/test.ts +218 -40
  52. package/dlob_read.ts +0 -155
  53. package/lib/util/getTokenAddress.d.ts +0 -2
  54. package/lib/util/getTokenAddress.js +0 -9
  55. package/src/util/getTokenAddress.ts +0 -18
@@ -8,7 +8,10 @@ import {
8
8
  import bs58 from 'bs58';
9
9
  import {
10
10
  ASSOCIATED_TOKEN_PROGRAM_ID,
11
- Token,
11
+ createAssociatedTokenAccountInstruction,
12
+ createCloseAccountInstruction,
13
+ createInitializeAccountInstruction,
14
+ getAssociatedTokenAddress,
12
15
  TOKEN_PROGRAM_ID,
13
16
  } from '@solana/spl-token';
14
17
  import {
@@ -39,6 +42,7 @@ import {
39
42
  ModifyOrderParams,
40
43
  PhoenixV1FulfillmentConfigAccount,
41
44
  ModifyOrderPolicy,
45
+ SwapReduceOnly,
42
46
  } from './types';
43
47
  import * as anchor from '@coral-xyz/anchor';
44
48
  import driftIDL from './idl/drift.json';
@@ -113,6 +117,7 @@ import { isSpotPositionAvailable } from './math/spotPosition';
113
117
  import { calculateMarketMaxAvailableInsurance } from './math/market';
114
118
  import { fetchUserStatsAccount } from './accounts/fetch';
115
119
  import { castNumberToSpotPrecision } from './math/spotMarket';
120
+ import { JupiterClient, Route, SwapMode } from './jupiter/jupiterClient';
116
121
 
117
122
  type RemainingAccountParams = {
118
123
  userAccounts: UserAccount[];
@@ -719,9 +724,7 @@ export class DriftClient {
719
724
 
720
725
  const state = this.getStateAccount();
721
726
  if (!state.whitelistMint.equals(PublicKey.default)) {
722
- const associatedTokenPublicKey = await Token.getAssociatedTokenAddress(
723
- ASSOCIATED_TOKEN_PROGRAM_ID,
724
- TOKEN_PROGRAM_ID,
727
+ const associatedTokenPublicKey = await getAssociatedTokenAddress(
725
728
  state.whitelistMint,
726
729
  this.wallet.publicKey
727
730
  );
@@ -1488,12 +1491,31 @@ export class DriftClient {
1488
1491
  return this.wallet.publicKey;
1489
1492
  }
1490
1493
  const mint = spotMarket.mint;
1491
- return await Token.getAssociatedTokenAddress(
1492
- ASSOCIATED_TOKEN_PROGRAM_ID,
1493
- TOKEN_PROGRAM_ID,
1494
- mint,
1495
- this.wallet.publicKey
1496
- );
1494
+ return await getAssociatedTokenAddress(mint, this.wallet.publicKey);
1495
+ }
1496
+
1497
+ public createAssociatedTokenAccountIdempotentInstruction(
1498
+ account: PublicKey,
1499
+ payer: PublicKey,
1500
+ owner: PublicKey,
1501
+ mint: PublicKey
1502
+ ): TransactionInstruction {
1503
+ return new TransactionInstruction({
1504
+ keys: [
1505
+ { pubkey: payer, isSigner: true, isWritable: true },
1506
+ { pubkey: account, isSigner: false, isWritable: true },
1507
+ { pubkey: owner, isSigner: false, isWritable: false },
1508
+ { pubkey: mint, isSigner: false, isWritable: false },
1509
+ {
1510
+ pubkey: anchor.web3.SystemProgram.programId,
1511
+ isSigner: false,
1512
+ isWritable: false,
1513
+ },
1514
+ { pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
1515
+ ],
1516
+ programId: ASSOCIATED_TOKEN_PROGRAM_ID,
1517
+ data: Buffer.from([0x1]),
1518
+ });
1497
1519
  }
1498
1520
 
1499
1521
  /**
@@ -1557,8 +1579,7 @@ export class DriftClient {
1557
1579
  // Close the wrapped sol account at the end of the transaction
1558
1580
  if (createWSOLTokenAccount) {
1559
1581
  tx.add(
1560
- Token.createCloseAccountInstruction(
1561
- TOKEN_PROGRAM_ID,
1582
+ createCloseAccountInstruction(
1562
1583
  associatedTokenAccount,
1563
1584
  signerAuthority,
1564
1585
  signerAuthority,
@@ -1671,10 +1692,9 @@ export class DriftClient {
1671
1692
  );
1672
1693
 
1673
1694
  result.ixs.push(
1674
- Token.createInitAccountInstruction(
1675
- TOKEN_PROGRAM_ID,
1676
- WRAPPED_SOL_MINT,
1695
+ createInitializeAccountInstruction(
1677
1696
  wrappedSolAccount.publicKey,
1697
+ WRAPPED_SOL_MINT,
1678
1698
  authority
1679
1699
  )
1680
1700
  );
@@ -1688,17 +1708,12 @@ export class DriftClient {
1688
1708
  tokenMintAddress: PublicKey,
1689
1709
  associatedTokenAddress: PublicKey
1690
1710
  ): anchor.web3.TransactionInstruction {
1691
- const createAssociatedAccountIx =
1692
- Token.createAssociatedTokenAccountInstruction(
1693
- ASSOCIATED_TOKEN_PROGRAM_ID,
1694
- TOKEN_PROGRAM_ID,
1695
- tokenMintAddress,
1696
- associatedTokenAddress,
1697
- this.wallet.publicKey,
1698
- this.wallet.publicKey
1699
- );
1700
-
1701
- return createAssociatedAccountIx;
1711
+ return createAssociatedTokenAccountInstruction(
1712
+ this.wallet.publicKey,
1713
+ associatedTokenAddress,
1714
+ this.wallet.publicKey,
1715
+ tokenMintAddress
1716
+ );
1702
1717
  }
1703
1718
 
1704
1719
  /**
@@ -1800,8 +1815,7 @@ export class DriftClient {
1800
1815
  // Close the wrapped sol account at the end of the transaction
1801
1816
  if (createWSOLTokenAccount) {
1802
1817
  tx.add(
1803
- Token.createCloseAccountInstruction(
1804
- TOKEN_PROGRAM_ID,
1818
+ createCloseAccountInstruction(
1805
1819
  userTokenAccount,
1806
1820
  authority,
1807
1821
  authority,
@@ -1940,8 +1954,7 @@ export class DriftClient {
1940
1954
  // Close the wrapped sol account at the end of the transaction
1941
1955
  if (createWSOLTokenAccount) {
1942
1956
  tx.add(
1943
- Token.createCloseAccountInstruction(
1944
- TOKEN_PROGRAM_ID,
1957
+ createCloseAccountInstruction(
1945
1958
  associatedTokenAddress,
1946
1959
  authority,
1947
1960
  authority,
@@ -3266,6 +3279,240 @@ export class DriftClient {
3266
3279
  });
3267
3280
  }
3268
3281
 
3282
+ /**
3283
+ * Swap tokens in drift account using jupiter
3284
+ * @param jupiterClient jupiter client to find routes and jupiter instructions
3285
+ * @param outMarketIndex the market index of the token you're buying
3286
+ * @param inMarketIndex the market index of the token you're selling
3287
+ * @param outAssociatedTokenAccount the token account to receive the token being sold on jupiter
3288
+ * @param inAssociatedTokenAccount the token account to
3289
+ * @param amount the amount of the token to sell
3290
+ * @param slippageBps the max slippage passed to jupiter api
3291
+ * @param route the jupiter route to use for the swap
3292
+ * @param txParams
3293
+ */
3294
+ public async swap({
3295
+ jupiterClient,
3296
+ outMarketIndex,
3297
+ inMarketIndex,
3298
+ outAssociatedTokenAccount,
3299
+ inAssociatedTokenAccount,
3300
+ amount,
3301
+ slippageBps,
3302
+ swapMode,
3303
+ route,
3304
+ reduceOnly,
3305
+ txParams,
3306
+ }: {
3307
+ jupiterClient: JupiterClient;
3308
+ outMarketIndex: number;
3309
+ inMarketIndex: number;
3310
+ outAssociatedTokenAccount?: PublicKey;
3311
+ inAssociatedTokenAccount?: PublicKey;
3312
+ amount: BN;
3313
+ slippageBps?: number;
3314
+ swapMode?: SwapMode;
3315
+ route?: Route;
3316
+ reduceOnly?: SwapReduceOnly;
3317
+ txParams?: TxParams;
3318
+ }): Promise<TransactionSignature> {
3319
+ const outMarket = this.getSpotMarketAccount(outMarketIndex);
3320
+ const inMarket = this.getSpotMarketAccount(inMarketIndex);
3321
+
3322
+ if (!route) {
3323
+ const routes = await jupiterClient.getRoutes({
3324
+ inputMint: inMarket.mint,
3325
+ outputMint: outMarket.mint,
3326
+ amount,
3327
+ slippageBps,
3328
+ swapMode,
3329
+ });
3330
+
3331
+ if (!routes || routes.length === 0) {
3332
+ throw new Error('No jupiter routes found');
3333
+ }
3334
+
3335
+ route = routes[0];
3336
+ }
3337
+
3338
+ const transaction = await jupiterClient.getSwapTransaction({
3339
+ route,
3340
+ userPublicKey: this.provider.wallet.publicKey,
3341
+ slippageBps,
3342
+ });
3343
+
3344
+ const { transactionMessage, lookupTables } =
3345
+ await jupiterClient.getTransactionMessageAndLookupTables({
3346
+ transaction,
3347
+ });
3348
+
3349
+ const jupiterInstructions = jupiterClient.getJupiterInstructions({
3350
+ transactionMessage,
3351
+ inputMint: inMarket.mint,
3352
+ outputMint: outMarket.mint,
3353
+ });
3354
+
3355
+ const preInstructions = [];
3356
+ if (!outAssociatedTokenAccount) {
3357
+ outAssociatedTokenAccount = await this.getAssociatedTokenAccount(
3358
+ outMarket.marketIndex,
3359
+ false
3360
+ );
3361
+
3362
+ const accountInfo = await this.connection.getAccountInfo(
3363
+ outAssociatedTokenAccount
3364
+ );
3365
+ if (!accountInfo) {
3366
+ preInstructions.push(
3367
+ this.createAssociatedTokenAccountIdempotentInstruction(
3368
+ outAssociatedTokenAccount,
3369
+ this.provider.wallet.publicKey,
3370
+ this.provider.wallet.publicKey,
3371
+ outMarket.mint
3372
+ )
3373
+ );
3374
+ }
3375
+ }
3376
+
3377
+ if (!inAssociatedTokenAccount) {
3378
+ inAssociatedTokenAccount = await this.getAssociatedTokenAccount(
3379
+ inMarket.marketIndex,
3380
+ false
3381
+ );
3382
+
3383
+ const accountInfo = await this.connection.getAccountInfo(
3384
+ inAssociatedTokenAccount
3385
+ );
3386
+ if (!accountInfo) {
3387
+ preInstructions.push(
3388
+ this.createAssociatedTokenAccountIdempotentInstruction(
3389
+ inAssociatedTokenAccount,
3390
+ this.provider.wallet.publicKey,
3391
+ this.provider.wallet.publicKey,
3392
+ inMarket.mint
3393
+ )
3394
+ );
3395
+ }
3396
+ }
3397
+
3398
+ const { beginSwapIx, endSwapIx } = await this.getSwapIx({
3399
+ outMarketIndex,
3400
+ inMarketIndex,
3401
+ amountIn: amount,
3402
+ inTokenAccount: inAssociatedTokenAccount,
3403
+ outTokenAccount: outAssociatedTokenAccount,
3404
+ reduceOnly,
3405
+ });
3406
+
3407
+ const instructions = [
3408
+ ...preInstructions,
3409
+ beginSwapIx,
3410
+ ...jupiterInstructions,
3411
+ endSwapIx,
3412
+ ];
3413
+
3414
+ const tx = await this.buildTransaction(
3415
+ instructions,
3416
+ txParams,
3417
+ 0,
3418
+ lookupTables
3419
+ );
3420
+
3421
+ const { txSig, slot } = await this.sendTransaction(tx);
3422
+ this.spotMarketLastSlotCache.set(outMarketIndex, slot);
3423
+ this.spotMarketLastSlotCache.set(inMarketIndex, slot);
3424
+
3425
+ return txSig;
3426
+ }
3427
+
3428
+ /**
3429
+ * Get the drift begin_swap and end_swap instructions
3430
+ *
3431
+ * @param outMarketIndex the market index of the token you're buying
3432
+ * @param inMarketIndex the market index of the token you're selling
3433
+ * @param amountIn the amount of the token to sell
3434
+ * @param inTokenAccount the token account to move the tokens being sold
3435
+ * @param outTokenAccount the token account to receive the tokens being bought
3436
+ * @param limitPrice the limit price of the swap
3437
+ */
3438
+ public async getSwapIx({
3439
+ outMarketIndex,
3440
+ inMarketIndex,
3441
+ amountIn,
3442
+ inTokenAccount,
3443
+ outTokenAccount,
3444
+ limitPrice,
3445
+ reduceOnly,
3446
+ }: {
3447
+ outMarketIndex: number;
3448
+ inMarketIndex: number;
3449
+ amountIn: BN;
3450
+ inTokenAccount: PublicKey;
3451
+ outTokenAccount: PublicKey;
3452
+ limitPrice?: BN;
3453
+ reduceOnly?: SwapReduceOnly;
3454
+ }): Promise<{
3455
+ beginSwapIx: TransactionInstruction;
3456
+ endSwapIx: TransactionInstruction;
3457
+ }> {
3458
+ const userAccountPublicKey = await this.getUserAccountPublicKey();
3459
+
3460
+ const remainingAccounts = this.getRemainingAccounts({
3461
+ userAccounts: [this.getUserAccount()],
3462
+ writableSpotMarketIndexes: [outMarketIndex, inMarketIndex],
3463
+ });
3464
+
3465
+ const outSpotMarket = this.getSpotMarketAccount(outMarketIndex);
3466
+ const inSpotMarket = this.getSpotMarketAccount(inMarketIndex);
3467
+
3468
+ const beginSwapIx = await this.program.instruction.beginSwap(
3469
+ inMarketIndex,
3470
+ outMarketIndex,
3471
+ amountIn,
3472
+ {
3473
+ accounts: {
3474
+ state: await this.getStatePublicKey(),
3475
+ user: userAccountPublicKey,
3476
+ userStats: this.getUserStatsAccountPublicKey(),
3477
+ authority: this.authority,
3478
+ outSpotMarketVault: outSpotMarket.vault,
3479
+ inSpotMarketVault: inSpotMarket.vault,
3480
+ inTokenAccount,
3481
+ outTokenAccount,
3482
+ tokenProgram: TOKEN_PROGRAM_ID,
3483
+ driftSigner: this.getStateAccount().signer,
3484
+ instructions: anchor.web3.SYSVAR_INSTRUCTIONS_PUBKEY,
3485
+ },
3486
+ remainingAccounts,
3487
+ }
3488
+ );
3489
+
3490
+ const endSwapIx = await this.program.instruction.endSwap(
3491
+ inMarketIndex,
3492
+ outMarketIndex,
3493
+ limitPrice ?? null,
3494
+ reduceOnly ?? null,
3495
+ {
3496
+ accounts: {
3497
+ state: await this.getStatePublicKey(),
3498
+ user: userAccountPublicKey,
3499
+ userStats: this.getUserStatsAccountPublicKey(),
3500
+ authority: this.authority,
3501
+ outSpotMarketVault: outSpotMarket.vault,
3502
+ inSpotMarketVault: inSpotMarket.vault,
3503
+ inTokenAccount,
3504
+ outTokenAccount,
3505
+ tokenProgram: TOKEN_PROGRAM_ID,
3506
+ driftSigner: this.getStateAccount().signer,
3507
+ instructions: anchor.web3.SYSVAR_INSTRUCTIONS_PUBKEY,
3508
+ },
3509
+ remainingAccounts,
3510
+ }
3511
+ );
3512
+
3513
+ return { beginSwapIx, endSwapIx };
3514
+ }
3515
+
3269
3516
  public async triggerOrder(
3270
3517
  userAccountPublicKey: PublicKey,
3271
3518
  user: UserAccount,
@@ -4769,8 +5016,7 @@ export class DriftClient {
4769
5016
 
4770
5017
  if (createWSOLTokenAccount) {
4771
5018
  tx.add(
4772
- Token.createCloseAccountInstruction(
4773
- TOKEN_PROGRAM_ID,
5019
+ createCloseAccountInstruction(
4774
5020
  tokenAccount,
4775
5021
  this.wallet.publicKey,
4776
5022
  this.wallet.publicKey,
@@ -4920,8 +5166,7 @@ export class DriftClient {
4920
5166
  // Close the wrapped sol account at the end of the transaction
4921
5167
  if (createWSOLTokenAccount) {
4922
5168
  tx.add(
4923
- Token.createCloseAccountInstruction(
4924
- TOKEN_PROGRAM_ID,
5169
+ createCloseAccountInstruction(
4925
5170
  tokenAccount,
4926
5171
  this.wallet.publicKey,
4927
5172
  this.wallet.publicKey,
@@ -5094,7 +5339,9 @@ export class DriftClient {
5094
5339
 
5095
5340
  async buildTransaction(
5096
5341
  instructions: TransactionInstruction | TransactionInstruction[],
5097
- txParams?: TxParams
5342
+ txParams?: TxParams,
5343
+ txVersion?: TransactionVersion,
5344
+ lookupTables?: AddressLookupTableAccount[]
5098
5345
  ): Promise<Transaction | VersionedTransaction> {
5099
5346
  const allIx = [];
5100
5347
  const computeUnits = txParams?.computeUnits ?? 600_000;
@@ -5120,10 +5367,14 @@ export class DriftClient {
5120
5367
  allIx.push(instructions);
5121
5368
  }
5122
5369
 
5123
- if (this.txVersion === 'legacy') {
5370
+ txVersion = txVersion ?? this.txVersion;
5371
+ if (txVersion === 'legacy') {
5124
5372
  return new Transaction().add(...allIx);
5125
5373
  } else {
5126
5374
  const marketLookupTable = await this.fetchMarketLookupTableAccount();
5375
+ lookupTables = lookupTables
5376
+ ? [...lookupTables, marketLookupTable]
5377
+ : [marketLookupTable];
5127
5378
  const message = new TransactionMessage({
5128
5379
  payerKey: this.provider.wallet.publicKey,
5129
5380
  recentBlockhash: (
@@ -5132,7 +5383,7 @@ export class DriftClient {
5132
5383
  )
5133
5384
  ).blockhash,
5134
5385
  instructions: allIx,
5135
- }).compileToV0Message([marketLookupTable]);
5386
+ }).compileToV0Message(lookupTables);
5136
5387
 
5137
5388
  return new VersionedTransaction(message);
5138
5389
  }
@@ -13,6 +13,7 @@ import {
13
13
  SpotInterestRecord,
14
14
  InsuranceFundStakeRecord,
15
15
  CurveRecord,
16
+ SwapRecord,
16
17
  } from '../index';
17
18
 
18
19
  export type EventSubscriptionOptions = {
@@ -43,6 +44,7 @@ export const DefaultEventSubscriptionOptions: EventSubscriptionOptions = {
43
44
  'SpotInterestRecord',
44
45
  'InsuranceFundStakeRecord',
45
46
  'CurveRecord',
47
+ 'SwapRecord',
46
48
  ],
47
49
  maxEventsPerType: 4096,
48
50
  orderBy: 'blockchain',
@@ -83,6 +85,7 @@ export type EventMap = {
83
85
  SpotInterestRecord: Event<SpotInterestRecord>;
84
86
  InsuranceFundStakeRecord: Event<InsuranceFundStakeRecord>;
85
87
  CurveRecord: Event<CurveRecord>;
88
+ SwapRecord: Event<SwapRecord>;
86
89
  };
87
90
 
88
91
  export type EventType = keyof EventMap;
@@ -100,7 +103,8 @@ export type DriftEvent =
100
103
  | Event<InsuranceFundRecord>
101
104
  | Event<SpotInterestRecord>
102
105
  | Event<InsuranceFundStakeRecord>
103
- | Event<CurveRecord>;
106
+ | Event<CurveRecord>
107
+ | Event<SwapRecord>;
104
108
 
105
109
  export interface EventSubscriberEvents {
106
110
  newEvent: (event: WrappedEvent<EventType>) => void;
@@ -5,7 +5,7 @@ import {
5
5
  getMarketOrderParams,
6
6
  Wallet,
7
7
  } from '..';
8
- import { Token, TOKEN_PROGRAM_ID } from '@solana/spl-token';
8
+ import { getAssociatedTokenAddress } from '@solana/spl-token';
9
9
  import { Connection, Keypair, PublicKey } from '@solana/web3.js';
10
10
  import {
11
11
  DriftClient,
@@ -25,9 +25,7 @@ export const getTokenAddress = (
25
25
  mintAddress: string,
26
26
  userPubKey: string
27
27
  ): Promise<PublicKey> => {
28
- return Token.getAssociatedTokenAddress(
29
- new PublicKey(`ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL`),
30
- TOKEN_PROGRAM_ID,
28
+ return getAssociatedTokenAddress(
31
29
  new PublicKey(mintAddress),
32
30
  new PublicKey(userPubKey)
33
31
  );