@drift-labs/sdk 2.29.0 → 2.30.0-beta.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.
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "2.29.0",
2
+ "version": "2.30.0-beta.1",
3
3
  "name": "drift",
4
4
  "instructions": [
5
5
  {
@@ -710,6 +710,162 @@
710
710
  }
711
711
  ]
712
712
  },
713
+ {
714
+ "name": "beginSwap",
715
+ "accounts": [
716
+ {
717
+ "name": "state",
718
+ "isMut": false,
719
+ "isSigner": false
720
+ },
721
+ {
722
+ "name": "user",
723
+ "isMut": true,
724
+ "isSigner": false
725
+ },
726
+ {
727
+ "name": "userStats",
728
+ "isMut": true,
729
+ "isSigner": false
730
+ },
731
+ {
732
+ "name": "authority",
733
+ "isMut": false,
734
+ "isSigner": true
735
+ },
736
+ {
737
+ "name": "outSpotMarketVault",
738
+ "isMut": true,
739
+ "isSigner": false
740
+ },
741
+ {
742
+ "name": "inSpotMarketVault",
743
+ "isMut": true,
744
+ "isSigner": false
745
+ },
746
+ {
747
+ "name": "outTokenAccount",
748
+ "isMut": true,
749
+ "isSigner": false
750
+ },
751
+ {
752
+ "name": "inTokenAccount",
753
+ "isMut": true,
754
+ "isSigner": false
755
+ },
756
+ {
757
+ "name": "tokenProgram",
758
+ "isMut": false,
759
+ "isSigner": false
760
+ },
761
+ {
762
+ "name": "driftSigner",
763
+ "isMut": false,
764
+ "isSigner": false
765
+ },
766
+ {
767
+ "name": "instructions",
768
+ "isMut": false,
769
+ "isSigner": false,
770
+ "docs": [
771
+ "Instructions Sysvar for instruction introspection"
772
+ ]
773
+ }
774
+ ],
775
+ "args": [
776
+ {
777
+ "name": "inMarketIndex",
778
+ "type": "u16"
779
+ },
780
+ {
781
+ "name": "outMarketIndex",
782
+ "type": "u16"
783
+ },
784
+ {
785
+ "name": "amountIn",
786
+ "type": "u64"
787
+ }
788
+ ]
789
+ },
790
+ {
791
+ "name": "endSwap",
792
+ "accounts": [
793
+ {
794
+ "name": "state",
795
+ "isMut": false,
796
+ "isSigner": false
797
+ },
798
+ {
799
+ "name": "user",
800
+ "isMut": true,
801
+ "isSigner": false
802
+ },
803
+ {
804
+ "name": "userStats",
805
+ "isMut": true,
806
+ "isSigner": false
807
+ },
808
+ {
809
+ "name": "authority",
810
+ "isMut": false,
811
+ "isSigner": true
812
+ },
813
+ {
814
+ "name": "outSpotMarketVault",
815
+ "isMut": true,
816
+ "isSigner": false
817
+ },
818
+ {
819
+ "name": "inSpotMarketVault",
820
+ "isMut": true,
821
+ "isSigner": false
822
+ },
823
+ {
824
+ "name": "outTokenAccount",
825
+ "isMut": true,
826
+ "isSigner": false
827
+ },
828
+ {
829
+ "name": "inTokenAccount",
830
+ "isMut": true,
831
+ "isSigner": false
832
+ },
833
+ {
834
+ "name": "tokenProgram",
835
+ "isMut": false,
836
+ "isSigner": false
837
+ },
838
+ {
839
+ "name": "driftSigner",
840
+ "isMut": false,
841
+ "isSigner": false
842
+ },
843
+ {
844
+ "name": "instructions",
845
+ "isMut": false,
846
+ "isSigner": false,
847
+ "docs": [
848
+ "Instructions Sysvar for instruction introspection"
849
+ ]
850
+ }
851
+ ],
852
+ "args": [
853
+ {
854
+ "name": "inMarketIndex",
855
+ "type": "u16"
856
+ },
857
+ {
858
+ "name": "outMarketIndex",
859
+ "type": "u16"
860
+ },
861
+ {
862
+ "name": "limitPrice",
863
+ "type": {
864
+ "option": "u64"
865
+ }
866
+ }
867
+ ]
868
+ },
713
869
  {
714
870
  "name": "addPerpLpShares",
715
871
  "accounts": [
@@ -4895,12 +5051,46 @@
4895
5051
  "defined": "AssetTier"
4896
5052
  }
4897
5053
  },
5054
+ {
5055
+ "name": "padding1",
5056
+ "type": {
5057
+ "array": [
5058
+ "u8",
5059
+ 6
5060
+ ]
5061
+ }
5062
+ },
5063
+ {
5064
+ "name": "flashLoanAmount",
5065
+ "docs": [
5066
+ "For swaps, the amount of token loaned out in the begin_swap ix",
5067
+ "precision: token mint precision"
5068
+ ],
5069
+ "type": "u64"
5070
+ },
5071
+ {
5072
+ "name": "flashLoanInitialTokenAmount",
5073
+ "docs": [
5074
+ "For swaps, the amount in the users token account in the begin_swap ix",
5075
+ "Used to calculate how much of the token left the system in end_swap ix",
5076
+ "precision: token mint precision"
5077
+ ],
5078
+ "type": "u64"
5079
+ },
5080
+ {
5081
+ "name": "totalSwapFee",
5082
+ "docs": [
5083
+ "The total fees received from swaps",
5084
+ "precision: token mint precision"
5085
+ ],
5086
+ "type": "u64"
5087
+ },
4898
5088
  {
4899
5089
  "name": "padding",
4900
5090
  "type": {
4901
5091
  "array": [
4902
5092
  "u8",
4903
- 86
5093
+ 56
4904
5094
  ]
4905
5095
  }
4906
5096
  }
@@ -8845,6 +9035,56 @@
8845
9035
  "index": false
8846
9036
  }
8847
9037
  ]
9038
+ },
9039
+ {
9040
+ "name": "SwapRecord",
9041
+ "fields": [
9042
+ {
9043
+ "name": "ts",
9044
+ "type": "i64",
9045
+ "index": false
9046
+ },
9047
+ {
9048
+ "name": "user",
9049
+ "type": "publicKey",
9050
+ "index": false
9051
+ },
9052
+ {
9053
+ "name": "amountOut",
9054
+ "type": "u64",
9055
+ "index": false
9056
+ },
9057
+ {
9058
+ "name": "amountIn",
9059
+ "type": "u64",
9060
+ "index": false
9061
+ },
9062
+ {
9063
+ "name": "outMarketIndex",
9064
+ "type": "u16",
9065
+ "index": false
9066
+ },
9067
+ {
9068
+ "name": "inMarketIndex",
9069
+ "type": "u16",
9070
+ "index": false
9071
+ },
9072
+ {
9073
+ "name": "outOraclePrice",
9074
+ "type": "i64",
9075
+ "index": false
9076
+ },
9077
+ {
9078
+ "name": "inOraclePrice",
9079
+ "type": "i64",
9080
+ "index": false
9081
+ },
9082
+ {
9083
+ "name": "fee",
9084
+ "type": "u64",
9085
+ "index": false
9086
+ }
9087
+ ]
8848
9088
  }
8849
9089
  ],
8850
9090
  "errors": [
@@ -10087,6 +10327,21 @@
10087
10327
  "code": 6247,
10088
10328
  "name": "InvalidPhoenixMarket",
10089
10329
  "msg": "InvalidPhoenixMarket"
10330
+ },
10331
+ {
10332
+ "code": 6248,
10333
+ "name": "InvalidSwap",
10334
+ "msg": "InvalidSwap"
10335
+ },
10336
+ {
10337
+ "code": 6249,
10338
+ "name": "SwapLimitPriceBreached",
10339
+ "msg": "SwapLimitPriceBreached"
10340
+ },
10341
+ {
10342
+ "code": 6250,
10343
+ "name": "SpotMarketReduceOnly",
10344
+ "msg": "SpotMarketReduceOnly"
10090
10345
  }
10091
10346
  ]
10092
10347
  }
package/src/index.ts CHANGED
@@ -32,6 +32,7 @@ export * from './factory/bigNum';
32
32
  export * from './events/types';
33
33
  export * from './events/eventSubscriber';
34
34
  export * from './events/fetchLogs';
35
+ export * from './jupiter/jupiterClient';
35
36
  export * from './math/auction';
36
37
  export * from './math/spotMarket';
37
38
  export * from './math/conversion';
@@ -0,0 +1,206 @@
1
+ import {
2
+ AddressLookupTableAccount,
3
+ Connection,
4
+ PublicKey,
5
+ TransactionInstruction,
6
+ TransactionMessage,
7
+ VersionedTransaction,
8
+ } from '@solana/web3.js';
9
+ import fetch from 'node-fetch';
10
+ import { BN } from '@coral-xyz/anchor';
11
+
12
+ export type SwapMode = 'ExactIn' | 'ExactOut';
13
+
14
+ export interface MarketInfo {
15
+ id: string;
16
+ inAmount: number;
17
+ inputMint: string;
18
+ label: string;
19
+ lpFee: Fee;
20
+ notEnoughLiquidity: boolean;
21
+ outAmount: number;
22
+ outputMint: string;
23
+ platformFee: Fee;
24
+ priceImpactPct: number;
25
+ }
26
+
27
+ export interface Fee {
28
+ amount: number;
29
+ mint: string;
30
+ pct: number;
31
+ }
32
+
33
+ export interface Route {
34
+ amount: number;
35
+ inAmount: number;
36
+ marketInfos: MarketInfo[];
37
+ otherAmountThreshold: number;
38
+ outAmount: number;
39
+ priceImpactPct: number;
40
+ slippageBps: number;
41
+ swapMode: SwapMode;
42
+ }
43
+
44
+ export class JupiterClient {
45
+ url = 'https://quote-api.jup.ag/v4';
46
+ connection: Connection;
47
+ lookupTableCahce = new Map<string, AddressLookupTableAccount>();
48
+
49
+ constructor({ connection }: { connection: Connection }) {
50
+ this.connection = connection;
51
+ }
52
+
53
+ /**
54
+ * Get routes for a swap
55
+ * @param inputMint the mint of the input token
56
+ * @param outputMint the mint of the output token
57
+ * @param amount the amount of the input token
58
+ * @param slippageBps the slippage tolerance in basis points
59
+ * @param swapMode the swap mode (ExactIn or ExactOut)
60
+ */
61
+ public async getRoutes({
62
+ inputMint,
63
+ outputMint,
64
+ amount,
65
+ slippageBps = 50,
66
+ swapMode = 'ExactIn',
67
+ }: {
68
+ inputMint: PublicKey;
69
+ outputMint: PublicKey;
70
+ amount: BN;
71
+ slippageBps?: number;
72
+ swapMode?: SwapMode;
73
+ }): Promise<Route[]> {
74
+ const params = new URLSearchParams({
75
+ inputMint: inputMint.toString(),
76
+ outputMint: outputMint.toString(),
77
+ amount: amount.toString(),
78
+ slippageBps: slippageBps.toString(),
79
+ swapMode,
80
+ }).toString();
81
+
82
+ const { data: routes } = await (
83
+ await fetch(`https://quote-api.jup.ag/v4/quote?${params}`)
84
+ ).json();
85
+
86
+ return routes;
87
+ }
88
+
89
+ /**
90
+ * Get a swap transaction for a route
91
+ * @param route the route to perform swap
92
+ * @param userPublicKey the user's wallet public key
93
+ * @param slippageBps the slippage tolerance in basis points
94
+ */
95
+ public async getSwapTransaction({
96
+ route,
97
+ userPublicKey,
98
+ slippageBps = 50,
99
+ }: {
100
+ route: Route;
101
+ userPublicKey: PublicKey;
102
+ slippageBps?: number;
103
+ }): Promise<VersionedTransaction> {
104
+ const resp = await (
105
+ await fetch(`${this.url}/swap`, {
106
+ method: 'POST',
107
+ headers: {
108
+ 'Content-Type': 'application/json',
109
+ },
110
+ body: JSON.stringify({
111
+ route,
112
+ userPublicKey,
113
+ slippageBps,
114
+ }),
115
+ })
116
+ ).json();
117
+
118
+ const { swapTransaction } = resp;
119
+
120
+ const swapTransactionBuf = Buffer.from(swapTransaction, 'base64');
121
+ return VersionedTransaction.deserialize(swapTransactionBuf);
122
+ }
123
+
124
+ /**
125
+ * Get the transaction message and lookup tables for a transaction
126
+ * @param transaction
127
+ */
128
+ public async getTransactionMessageAndLookupTables({
129
+ transaction,
130
+ }: {
131
+ transaction: VersionedTransaction;
132
+ }): Promise<{
133
+ transactionMessage: TransactionMessage;
134
+ lookupTables: AddressLookupTableAccount[];
135
+ }> {
136
+ const message = transaction.message;
137
+
138
+ const lookupTables = (
139
+ await Promise.all(
140
+ message.addressTableLookups.map(async (lookup) => {
141
+ return await this.getLookupTable(lookup.accountKey);
142
+ })
143
+ )
144
+ ).filter((lookup) => lookup);
145
+
146
+ const transactionMessage = TransactionMessage.decompile(message);
147
+ return {
148
+ transactionMessage,
149
+ lookupTables,
150
+ };
151
+ }
152
+
153
+ async getLookupTable(
154
+ accountKey: PublicKey
155
+ ): Promise<AddressLookupTableAccount> {
156
+ if (this.lookupTableCahce.has(accountKey.toString())) {
157
+ return this.lookupTableCahce.get(accountKey.toString());
158
+ }
159
+
160
+ return (await this.connection.getAddressLookupTable(accountKey)).value;
161
+ }
162
+
163
+ /**
164
+ * Get the jupiter instructions from transaction by filtering out instructions to compute budget and associated token programs
165
+ * @param transactionMessage the transaction message
166
+ * @param inputMint the input mint
167
+ * @param outputMint the output mint
168
+ */
169
+ public getJupiterInstructions({
170
+ transactionMessage,
171
+ inputMint,
172
+ outputMint,
173
+ }: {
174
+ transactionMessage: TransactionMessage;
175
+ inputMint: PublicKey;
176
+ outputMint: PublicKey;
177
+ }): TransactionInstruction[] {
178
+ return transactionMessage.instructions.filter((instruction) => {
179
+ if (
180
+ instruction.programId.toString() ===
181
+ 'ComputeBudget111111111111111111111111111111'
182
+ ) {
183
+ return false;
184
+ }
185
+
186
+ if (
187
+ instruction.programId.toString() ===
188
+ 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA'
189
+ ) {
190
+ return false;
191
+ }
192
+
193
+ if (
194
+ instruction.programId.toString() ===
195
+ 'ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL'
196
+ ) {
197
+ const mint = instruction.keys[3].pubkey;
198
+ if (mint.equals(inputMint) || mint.equals(outputMint)) {
199
+ return false;
200
+ }
201
+ }
202
+
203
+ return true;
204
+ });
205
+ }
206
+ }
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseTokenAccount = void 0;
4
+ const spl_token_1 = require("@solana/spl-token");
5
+ const web3_js_1 = require("@solana/web3.js");
6
+ function parseTokenAccount(data) {
7
+ const accountInfo = spl_token_1.AccountLayout.decode(data);
8
+ accountInfo.mint = new web3_js_1.PublicKey(accountInfo.mint);
9
+ accountInfo.owner = new web3_js_1.PublicKey(accountInfo.owner);
10
+ accountInfo.amount = spl_token_1.u64.fromBuffer(accountInfo.amount);
11
+ if (accountInfo.delegateOption === 0) {
12
+ accountInfo.delegate = null;
13
+ // eslint-disable-next-line new-cap
14
+ accountInfo.delegatedAmount = new spl_token_1.u64(0);
15
+ }
16
+ else {
17
+ accountInfo.delegate = new web3_js_1.PublicKey(accountInfo.delegate);
18
+ accountInfo.delegatedAmount = spl_token_1.u64.fromBuffer(accountInfo.delegatedAmount);
19
+ }
20
+ accountInfo.isInitialized = accountInfo.state !== 0;
21
+ accountInfo.isFrozen = accountInfo.state === 2;
22
+ if (accountInfo.isNativeOption === 1) {
23
+ accountInfo.rentExemptReserve = spl_token_1.u64.fromBuffer(accountInfo.isNative);
24
+ accountInfo.isNative = true;
25
+ }
26
+ else {
27
+ accountInfo.rentExemptReserve = null;
28
+ accountInfo.isNative = false;
29
+ }
30
+ if (accountInfo.closeAuthorityOption === 0) {
31
+ accountInfo.closeAuthority = null;
32
+ }
33
+ else {
34
+ accountInfo.closeAuthority = new web3_js_1.PublicKey(accountInfo.closeAuthority);
35
+ }
36
+ return accountInfo;
37
+ }
38
+ exports.parseTokenAccount = parseTokenAccount;
package/src/types.ts CHANGED
@@ -505,6 +505,18 @@ export type OrderActionRecord = {
505
505
  oraclePrice: BN;
506
506
  };
507
507
 
508
+ export type SwapRecord = {
509
+ ts: number;
510
+ user: PublicKey;
511
+ amountOut: BN;
512
+ amountIn: BN;
513
+ outMarketIndex: number;
514
+ inMarketIndex: number;
515
+ outOraclePrice: BN;
516
+ inOraclePrice: BN;
517
+ fee: BN;
518
+ };
519
+
508
520
  export type StateAccount = {
509
521
  admin: PublicKey;
510
522
  exchangeStatus: number;
@@ -645,6 +657,10 @@ export type SpotMarketAccount = {
645
657
  nextFillRecordId: BN;
646
658
  spotFeePool: PoolBalance;
647
659
  totalSpotFee: BN;
660
+ totalSwapFee: BN;
661
+
662
+ flashLoanAmount: BN;
663
+ flashLoanInitialTokenAmount: BN;
648
664
 
649
665
  ordersEnabled: boolean;
650
666
  };
@@ -801,6 +817,7 @@ export type UserAccount = {
801
817
  totalWithdraws: BN;
802
818
  totalSocialLoss: BN;
803
819
  cumulativePerpFunding: BN;
820
+ cumulativeSpotFees: BN;
804
821
  liquidationMarginFreed: BN;
805
822
  lastActiveSlot: BN;
806
823
  isMarginTradingEnabled: boolean;
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.findComputeUnitConsumption = void 0;
13
+ function findComputeUnitConsumption(programId, connection, txSignature, commitment = 'confirmed') {
14
+ return __awaiter(this, void 0, void 0, function* () {
15
+ const tx = yield connection.getTransaction(txSignature, { commitment });
16
+ const computeUnits = [];
17
+ const regex = new RegExp(`Program ${programId.toString()} consumed ([0-9]{0,6}) of ([0-9]{0,7}) compute units`);
18
+ tx.meta.logMessages.forEach((logMessage) => {
19
+ const match = logMessage.match(regex);
20
+ if (match && match[1]) {
21
+ computeUnits.push(match[1]);
22
+ }
23
+ });
24
+ return computeUnits;
25
+ });
26
+ }
27
+ exports.findComputeUnitConsumption = findComputeUnitConsumption;
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.promiseTimeout = void 0;
4
+ function promiseTimeout(promise, timeoutMs) {
5
+ let timeoutId;
6
+ const timeoutPromise = new Promise((resolve) => {
7
+ timeoutId = setTimeout(() => resolve(null), timeoutMs);
8
+ });
9
+ return Promise.race([promise, timeoutPromise]).then((result) => {
10
+ clearTimeout(timeoutId);
11
+ return result;
12
+ });
13
+ }
14
+ exports.promiseTimeout = promiseTimeout;
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.estimateTps = void 0;
13
+ function estimateTps(programId, connection, failed) {
14
+ return __awaiter(this, void 0, void 0, function* () {
15
+ let signatures = yield connection.getSignaturesForAddress(programId, undefined, 'finalized');
16
+ if (failed) {
17
+ signatures = signatures.filter((signature) => signature.err);
18
+ }
19
+ const numberOfSignatures = signatures.length;
20
+ if (numberOfSignatures === 0) {
21
+ return 0;
22
+ }
23
+ return (numberOfSignatures /
24
+ (signatures[0].blockTime - signatures[numberOfSignatures - 1].blockTime));
25
+ });
26
+ }
27
+ exports.estimateTps = estimateTps;
@@ -309,6 +309,9 @@ export const mockSpotMarkets: Array<SpotMarketAccount> = [
309
309
  marketIndex: 0,
310
310
  },
311
311
  totalSpotFee: new BN(0),
312
+ totalSwapFee: new BN(0),
313
+ flashLoanAmount: new BN(0),
314
+ flashLoanInitialTokenAmount: new BN(0),
312
315
  oracleSource: OracleSource.PYTH,
313
316
  historicalOracleData: {
314
317
  lastOraclePrice: new BN(0),
@@ -384,6 +387,9 @@ export const mockSpotMarkets: Array<SpotMarketAccount> = [
384
387
  marketIndex: 0,
385
388
  },
386
389
  totalSpotFee: new BN(0),
390
+ totalSwapFee: new BN(0),
391
+ flashLoanAmount: new BN(0),
392
+ flashLoanInitialTokenAmount: new BN(0),
387
393
  oracleSource: OracleSource.PYTH,
388
394
  historicalOracleData: {
389
395
  lastOraclePrice: new BN(0),
@@ -459,6 +465,9 @@ export const mockSpotMarkets: Array<SpotMarketAccount> = [
459
465
  marketIndex: 0,
460
466
  },
461
467
  totalSpotFee: new BN(0),
468
+ totalSwapFee: new BN(0),
469
+ flashLoanAmount: new BN(0),
470
+ flashLoanInitialTokenAmount: new BN(0),
462
471
  oracleSource: OracleSource.PYTH,
463
472
  historicalOracleData: {
464
473
  lastOraclePrice: new BN(0),