@drift-labs/sdk 2.144.0-beta.2 → 2.144.0-beta.3
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/lib/browser/accounts/grpcDriftClientAccountSubscriberV2.js +0 -12
- package/lib/browser/driftClient.d.ts +29 -8
- package/lib/browser/driftClient.js +97 -21
- package/lib/browser/titan/titanClient.d.ts +86 -0
- package/lib/browser/titan/titanClient.js +214 -0
- package/lib/node/accounts/grpcDriftClientAccountSubscriberV2.d.ts.map +1 -1
- package/lib/node/accounts/grpcDriftClientAccountSubscriberV2.js +0 -12
- package/lib/node/driftClient.d.ts +29 -8
- package/lib/node/driftClient.d.ts.map +1 -1
- package/lib/node/driftClient.js +97 -21
- package/lib/node/titan/titanClient.d.ts +87 -0
- package/lib/node/titan/titanClient.d.ts.map +1 -0
- package/lib/node/titan/titanClient.js +214 -0
- package/package.json +2 -1
- package/src/accounts/grpcDriftClientAccountSubscriberV2.ts +0 -15
- package/src/driftClient.ts +173 -22
- package/src/titan/titanClient.ts +414 -0
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TitanClient = exports.SwapMode = void 0;
|
|
4
|
+
const web3_js_1 = require("@solana/web3.js");
|
|
5
|
+
const msgpack_1 = require("@msgpack/msgpack");
|
|
6
|
+
var SwapMode;
|
|
7
|
+
(function (SwapMode) {
|
|
8
|
+
SwapMode["ExactIn"] = "ExactIn";
|
|
9
|
+
SwapMode["ExactOut"] = "ExactOut";
|
|
10
|
+
})(SwapMode || (exports.SwapMode = SwapMode = {}));
|
|
11
|
+
const TITAN_API_URL = 'https://api.titan.exchange';
|
|
12
|
+
class TitanClient {
|
|
13
|
+
constructor({ connection, authToken, url, }) {
|
|
14
|
+
this.connection = connection;
|
|
15
|
+
this.authToken = authToken;
|
|
16
|
+
this.url = url !== null && url !== void 0 ? url : TITAN_API_URL;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Get routes for a swap
|
|
20
|
+
*/
|
|
21
|
+
async getQuote({ inputMint, outputMint, amount, userPublicKey, maxAccounts = 50, // 50 is an estimated amount with buffer
|
|
22
|
+
slippageBps, swapMode, onlyDirectRoutes, excludeDexes, sizeConstraint, accountsLimitWritable, }) {
|
|
23
|
+
var _a;
|
|
24
|
+
const params = new URLSearchParams({
|
|
25
|
+
inputMint: inputMint.toString(),
|
|
26
|
+
outputMint: outputMint.toString(),
|
|
27
|
+
amount: amount.toString(),
|
|
28
|
+
userPublicKey: userPublicKey.toString(),
|
|
29
|
+
...(slippageBps && { slippageBps: slippageBps.toString() }),
|
|
30
|
+
...(swapMode && {
|
|
31
|
+
swapMode: swapMode === 'ExactOut' ? SwapMode.ExactOut : SwapMode.ExactIn,
|
|
32
|
+
}),
|
|
33
|
+
...(onlyDirectRoutes && {
|
|
34
|
+
onlyDirectRoutes: onlyDirectRoutes.toString(),
|
|
35
|
+
}),
|
|
36
|
+
...(maxAccounts && { accountsLimitTotal: maxAccounts.toString() }),
|
|
37
|
+
...(excludeDexes && { excludeDexes: excludeDexes.join(',') }),
|
|
38
|
+
...(sizeConstraint && { sizeConstraint: sizeConstraint.toString() }),
|
|
39
|
+
...(accountsLimitWritable && {
|
|
40
|
+
accountsLimitWritable: accountsLimitWritable.toString(),
|
|
41
|
+
}),
|
|
42
|
+
});
|
|
43
|
+
const response = await fetch(`${this.url}/api/v1/quote/swap?${params.toString()}`, {
|
|
44
|
+
headers: {
|
|
45
|
+
Accept: 'application/vnd.msgpack',
|
|
46
|
+
'Accept-Encoding': 'gzip, deflate, br',
|
|
47
|
+
Authorization: `Bearer ${this.authToken}`,
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
if (!response.ok) {
|
|
51
|
+
throw new Error(`Titan API error: ${response.status} ${response.statusText}`);
|
|
52
|
+
}
|
|
53
|
+
const buffer = await response.arrayBuffer();
|
|
54
|
+
const data = (0, msgpack_1.decode)(buffer);
|
|
55
|
+
const route = data.quotes[Object.keys(data.quotes).find((key) => key.toLowerCase() === 'titan') ||
|
|
56
|
+
''];
|
|
57
|
+
if (!route) {
|
|
58
|
+
throw new Error('No routes available');
|
|
59
|
+
}
|
|
60
|
+
return {
|
|
61
|
+
inputMint: inputMint.toString(),
|
|
62
|
+
inAmount: amount.toString(),
|
|
63
|
+
outputMint: outputMint.toString(),
|
|
64
|
+
outAmount: route.outAmount.toString(),
|
|
65
|
+
swapMode: data.swapMode,
|
|
66
|
+
slippageBps: route.slippageBps,
|
|
67
|
+
platformFee: route.platformFee
|
|
68
|
+
? {
|
|
69
|
+
amount: route.platformFee.amount.toString(),
|
|
70
|
+
feeBps: route.platformFee.fee_bps,
|
|
71
|
+
}
|
|
72
|
+
: undefined,
|
|
73
|
+
routePlan: ((_a = route.steps) === null || _a === void 0 ? void 0 : _a.map((step) => {
|
|
74
|
+
var _a;
|
|
75
|
+
return ({
|
|
76
|
+
swapInfo: {
|
|
77
|
+
ammKey: new web3_js_1.PublicKey(step.ammKey).toString(),
|
|
78
|
+
label: step.label,
|
|
79
|
+
inputMint: new web3_js_1.PublicKey(step.inputMint).toString(),
|
|
80
|
+
outputMint: new web3_js_1.PublicKey(step.outputMint).toString(),
|
|
81
|
+
inAmount: step.inAmount.toString(),
|
|
82
|
+
outAmount: step.outAmount.toString(),
|
|
83
|
+
feeAmount: ((_a = step.feeAmount) === null || _a === void 0 ? void 0 : _a.toString()) || '0',
|
|
84
|
+
feeMint: step.feeMint ? new web3_js_1.PublicKey(step.feeMint).toString() : '',
|
|
85
|
+
},
|
|
86
|
+
percent: 100,
|
|
87
|
+
});
|
|
88
|
+
})) || [],
|
|
89
|
+
contextSlot: route.contextSlot,
|
|
90
|
+
timeTaken: route.timeTaken,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Get a swap transaction for quote
|
|
95
|
+
*/
|
|
96
|
+
async getSwap({ inputMint, outputMint, amount, userPublicKey, maxAccounts = 50, // 50 is an estimated amount with buffer
|
|
97
|
+
slippageBps, swapMode, onlyDirectRoutes, excludeDexes, sizeConstraint, accountsLimitWritable, }) {
|
|
98
|
+
const params = new URLSearchParams({
|
|
99
|
+
inputMint: inputMint.toString(),
|
|
100
|
+
outputMint: outputMint.toString(),
|
|
101
|
+
amount: amount.toString(),
|
|
102
|
+
userPublicKey: userPublicKey.toString(),
|
|
103
|
+
...(slippageBps && { slippageBps: slippageBps.toString() }),
|
|
104
|
+
...(swapMode && { swapMode: swapMode }),
|
|
105
|
+
...(maxAccounts && { accountsLimitTotal: maxAccounts.toString() }),
|
|
106
|
+
...(excludeDexes && { excludeDexes: excludeDexes.join(',') }),
|
|
107
|
+
...(onlyDirectRoutes && {
|
|
108
|
+
onlyDirectRoutes: onlyDirectRoutes.toString(),
|
|
109
|
+
}),
|
|
110
|
+
...(sizeConstraint && { sizeConstraint: sizeConstraint.toString() }),
|
|
111
|
+
...(accountsLimitWritable && {
|
|
112
|
+
accountsLimitWritable: accountsLimitWritable.toString(),
|
|
113
|
+
}),
|
|
114
|
+
});
|
|
115
|
+
const response = await fetch(`${this.url}/api/v1/quote/swap?${params.toString()}`, {
|
|
116
|
+
headers: {
|
|
117
|
+
Accept: 'application/vnd.msgpack',
|
|
118
|
+
'Accept-Encoding': 'gzip, deflate, br',
|
|
119
|
+
Authorization: `Bearer ${this.authToken}`,
|
|
120
|
+
},
|
|
121
|
+
});
|
|
122
|
+
if (!response.ok) {
|
|
123
|
+
if (response.status === 404) {
|
|
124
|
+
throw new Error('No routes available');
|
|
125
|
+
}
|
|
126
|
+
throw new Error(`Titan API error: ${response.status} ${response.statusText}`);
|
|
127
|
+
}
|
|
128
|
+
const buffer = await response.arrayBuffer();
|
|
129
|
+
const data = (0, msgpack_1.decode)(buffer);
|
|
130
|
+
const route = data.quotes[Object.keys(data.quotes).find((key) => key.toLowerCase() === 'titan') ||
|
|
131
|
+
''];
|
|
132
|
+
if (!route) {
|
|
133
|
+
throw new Error('No routes available');
|
|
134
|
+
}
|
|
135
|
+
if (route.instructions && route.instructions.length > 0) {
|
|
136
|
+
try {
|
|
137
|
+
const { transactionMessage, lookupTables } = await this.getTransactionMessageAndLookupTables(route, userPublicKey);
|
|
138
|
+
return { transactionMessage, lookupTables };
|
|
139
|
+
}
|
|
140
|
+
catch (err) {
|
|
141
|
+
throw new Error('Something went wrong with creating the Titan swap transaction. Please try again.');
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
throw new Error('No instructions provided in the route');
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Get the titan instructions from transaction by filtering out instructions to compute budget and associated token programs
|
|
148
|
+
* @param transactionMessage the transaction message
|
|
149
|
+
* @param inputMint the input mint
|
|
150
|
+
* @param outputMint the output mint
|
|
151
|
+
*/
|
|
152
|
+
getTitanInstructions({ transactionMessage, inputMint, outputMint, }) {
|
|
153
|
+
// Filter out common system instructions that can be handled by DriftClient
|
|
154
|
+
const filteredInstructions = transactionMessage.instructions.filter((instruction) => {
|
|
155
|
+
const programId = instruction.programId.toString();
|
|
156
|
+
// Filter out system programs
|
|
157
|
+
if (programId === 'ComputeBudget111111111111111111111111111111') {
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
if (programId === 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA') {
|
|
161
|
+
return false;
|
|
162
|
+
}
|
|
163
|
+
if (programId === '11111111111111111111111111111111') {
|
|
164
|
+
return false;
|
|
165
|
+
}
|
|
166
|
+
// Filter out Associated Token Account creation for input/output mints
|
|
167
|
+
if (programId === 'ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL') {
|
|
168
|
+
if (instruction.keys.length > 3) {
|
|
169
|
+
const mint = instruction.keys[3].pubkey;
|
|
170
|
+
if (mint.equals(inputMint) || mint.equals(outputMint)) {
|
|
171
|
+
return false;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return true;
|
|
176
|
+
});
|
|
177
|
+
return filteredInstructions;
|
|
178
|
+
}
|
|
179
|
+
async getTransactionMessageAndLookupTables(route, userPublicKey) {
|
|
180
|
+
const solanaInstructions = route.instructions.map((instruction) => ({
|
|
181
|
+
programId: new web3_js_1.PublicKey(instruction.p),
|
|
182
|
+
keys: instruction.a.map((meta) => ({
|
|
183
|
+
pubkey: new web3_js_1.PublicKey(meta.p),
|
|
184
|
+
isSigner: meta.s,
|
|
185
|
+
isWritable: meta.w,
|
|
186
|
+
})),
|
|
187
|
+
data: Buffer.from(instruction.d),
|
|
188
|
+
}));
|
|
189
|
+
// Get recent blockhash
|
|
190
|
+
const { blockhash } = await this.connection.getLatestBlockhash();
|
|
191
|
+
// Build address lookup tables if provided
|
|
192
|
+
const addressLookupTables = [];
|
|
193
|
+
if (route.addressLookupTables && route.addressLookupTables.length > 0) {
|
|
194
|
+
for (const altPubkey of route.addressLookupTables) {
|
|
195
|
+
try {
|
|
196
|
+
const altAccount = await this.connection.getAddressLookupTable(new web3_js_1.PublicKey(altPubkey));
|
|
197
|
+
if (altAccount.value) {
|
|
198
|
+
addressLookupTables.push(altAccount.value);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
catch (err) {
|
|
202
|
+
console.warn(`Failed to fetch address lookup table:`, err);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
const transactionMessage = new web3_js_1.TransactionMessage({
|
|
207
|
+
payerKey: userPublicKey,
|
|
208
|
+
recentBlockhash: blockhash,
|
|
209
|
+
instructions: solanaInstructions,
|
|
210
|
+
});
|
|
211
|
+
return { transactionMessage, lookupTables: addressLookupTables };
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
exports.TitanClient = TitanClient;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@drift-labs/sdk",
|
|
3
|
-
"version": "2.144.0-beta.
|
|
3
|
+
"version": "2.144.0-beta.3",
|
|
4
4
|
"main": "lib/node/index.js",
|
|
5
5
|
"types": "lib/node/index.d.ts",
|
|
6
6
|
"module": "./lib/browser/index.js",
|
|
@@ -44,6 +44,7 @@
|
|
|
44
44
|
"@coral-xyz/anchor-30": "npm:@coral-xyz/anchor@0.30.1",
|
|
45
45
|
"@ellipsis-labs/phoenix-sdk": "1.4.5",
|
|
46
46
|
"@grpc/grpc-js": "1.14.0",
|
|
47
|
+
"@msgpack/msgpack": "^3.1.2",
|
|
47
48
|
"@openbook-dex/openbook-v2": "0.2.10",
|
|
48
49
|
"@project-serum/serum": "0.13.65",
|
|
49
50
|
"@pythnetwork/client": "2.5.3",
|
|
@@ -709,21 +709,6 @@ export class grpcDriftClientAccountSubscriberV2
|
|
|
709
709
|
await this.perpMarketsSubscriber.removeAccounts(
|
|
710
710
|
perpMarketPubkeysToRemove
|
|
711
711
|
);
|
|
712
|
-
// Clean up the mapping for removed perp markets
|
|
713
|
-
for (const pubkey of perpMarketPubkeysToRemove) {
|
|
714
|
-
const pubkeyString = pubkey.toBase58();
|
|
715
|
-
for (const [
|
|
716
|
-
marketIndex,
|
|
717
|
-
accountPubkey,
|
|
718
|
-
] of this.perpMarketIndexToAccountPubkeyMap.entries()) {
|
|
719
|
-
if (accountPubkey === pubkeyString) {
|
|
720
|
-
this.perpMarketIndexToAccountPubkeyMap.delete(marketIndex);
|
|
721
|
-
this.perpOracleMap.delete(marketIndex);
|
|
722
|
-
this.perpOracleStringMap.delete(marketIndex);
|
|
723
|
-
break;
|
|
724
|
-
}
|
|
725
|
-
}
|
|
726
|
-
}
|
|
727
712
|
}
|
|
728
713
|
|
|
729
714
|
// Remove accounts in batches - oracles
|
package/src/driftClient.ts
CHANGED
|
@@ -190,6 +190,7 @@ import { createMinimalEd25519VerifyIx } from './util/ed25519Utils';
|
|
|
190
190
|
import {
|
|
191
191
|
createNativeInstructionDiscriminatorBuffer,
|
|
192
192
|
isVersionedTransaction,
|
|
193
|
+
MAX_TX_BYTE_SIZE,
|
|
193
194
|
} from './tx/utils';
|
|
194
195
|
import pythSolanaReceiverIdl from './idl/pyth_solana_receiver.json';
|
|
195
196
|
import { asV0Tx, PullFeed, AnchorUtils } from '@switchboard-xyz/on-demand';
|
|
@@ -208,6 +209,12 @@ import {
|
|
|
208
209
|
isBuilderOrderReferral,
|
|
209
210
|
isBuilderOrderCompleted,
|
|
210
211
|
} from './math/builder';
|
|
212
|
+
import { TitanClient, SwapMode as TitanSwapMode } from './titan/titanClient';
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Union type for swap clients (Titan and Jupiter)
|
|
216
|
+
*/
|
|
217
|
+
export type SwapClient = TitanClient | JupiterClient;
|
|
211
218
|
|
|
212
219
|
type RemainingAccountParams = {
|
|
213
220
|
userAccounts: UserAccount[];
|
|
@@ -5733,23 +5740,23 @@ export class DriftClient {
|
|
|
5733
5740
|
}
|
|
5734
5741
|
|
|
5735
5742
|
/**
|
|
5736
|
-
* Swap tokens in drift account using jupiter
|
|
5737
|
-
* @param
|
|
5743
|
+
* Swap tokens in drift account using titan or jupiter
|
|
5744
|
+
* @param swapClient swap client to find routes and instructions (Titan or Jupiter)
|
|
5738
5745
|
* @param outMarketIndex the market index of the token you're buying
|
|
5739
5746
|
* @param inMarketIndex the market index of the token you're selling
|
|
5740
|
-
* @param outAssociatedTokenAccount the token account to receive the token being sold on jupiter
|
|
5747
|
+
* @param outAssociatedTokenAccount the token account to receive the token being sold on titan or jupiter
|
|
5741
5748
|
* @param inAssociatedTokenAccount the token account to
|
|
5742
5749
|
* @param amount the amount of TokenIn, regardless of swapMode
|
|
5743
|
-
* @param slippageBps the max slippage passed to jupiter api
|
|
5744
|
-
* @param swapMode jupiter swapMode (ExactIn or ExactOut), default is ExactIn
|
|
5745
|
-
* @param route the jupiter route to use for the swap
|
|
5750
|
+
* @param slippageBps the max slippage passed to titan or jupiter api
|
|
5751
|
+
* @param swapMode titan or jupiter swapMode (ExactIn or ExactOut), default is ExactIn
|
|
5752
|
+
* @param route the titan or jupiter route to use for the swap
|
|
5746
5753
|
* @param reduceOnly specify if In or Out token on the drift account must reduceOnly, checked at end of swap
|
|
5747
5754
|
* @param v6 pass in the quote response from Jupiter quote's API (deprecated, use quote instead)
|
|
5748
5755
|
* @param quote pass in the quote response from Jupiter quote's API
|
|
5749
5756
|
* @param txParams
|
|
5750
5757
|
*/
|
|
5751
5758
|
public async swap({
|
|
5752
|
-
|
|
5759
|
+
swapClient,
|
|
5753
5760
|
outMarketIndex,
|
|
5754
5761
|
inMarketIndex,
|
|
5755
5762
|
outAssociatedTokenAccount,
|
|
@@ -5763,7 +5770,7 @@ export class DriftClient {
|
|
|
5763
5770
|
quote,
|
|
5764
5771
|
onlyDirectRoutes = false,
|
|
5765
5772
|
}: {
|
|
5766
|
-
|
|
5773
|
+
swapClient: SwapClient;
|
|
5767
5774
|
outMarketIndex: number;
|
|
5768
5775
|
inMarketIndex: number;
|
|
5769
5776
|
outAssociatedTokenAccount?: PublicKey;
|
|
@@ -5779,21 +5786,45 @@ export class DriftClient {
|
|
|
5779
5786
|
};
|
|
5780
5787
|
quote?: QuoteResponse;
|
|
5781
5788
|
}): Promise<TransactionSignature> {
|
|
5782
|
-
|
|
5789
|
+
let res: {
|
|
5790
|
+
ixs: TransactionInstruction[];
|
|
5791
|
+
lookupTables: AddressLookupTableAccount[];
|
|
5792
|
+
};
|
|
5793
|
+
|
|
5794
|
+
if (swapClient instanceof TitanClient) {
|
|
5795
|
+
res = await this.getTitanSwapIx({
|
|
5796
|
+
titanClient: swapClient,
|
|
5797
|
+
outMarketIndex,
|
|
5798
|
+
inMarketIndex,
|
|
5799
|
+
outAssociatedTokenAccount,
|
|
5800
|
+
inAssociatedTokenAccount,
|
|
5801
|
+
amount,
|
|
5802
|
+
slippageBps,
|
|
5803
|
+
swapMode,
|
|
5804
|
+
onlyDirectRoutes,
|
|
5805
|
+
reduceOnly,
|
|
5806
|
+
});
|
|
5807
|
+
} else if (swapClient instanceof JupiterClient) {
|
|
5808
|
+
const quoteToUse = quote ?? v6?.quote;
|
|
5809
|
+
res = await this.getJupiterSwapIxV6({
|
|
5810
|
+
jupiterClient: swapClient,
|
|
5811
|
+
outMarketIndex,
|
|
5812
|
+
inMarketIndex,
|
|
5813
|
+
outAssociatedTokenAccount,
|
|
5814
|
+
inAssociatedTokenAccount,
|
|
5815
|
+
amount,
|
|
5816
|
+
slippageBps,
|
|
5817
|
+
swapMode,
|
|
5818
|
+
quote: quoteToUse,
|
|
5819
|
+
reduceOnly,
|
|
5820
|
+
onlyDirectRoutes,
|
|
5821
|
+
});
|
|
5822
|
+
} else {
|
|
5823
|
+
throw new Error(
|
|
5824
|
+
'Invalid swap client type. Must be TitanClient or JupiterClient.'
|
|
5825
|
+
);
|
|
5826
|
+
}
|
|
5783
5827
|
|
|
5784
|
-
const res = await this.getJupiterSwapIxV6({
|
|
5785
|
-
jupiterClient,
|
|
5786
|
-
outMarketIndex,
|
|
5787
|
-
inMarketIndex,
|
|
5788
|
-
outAssociatedTokenAccount,
|
|
5789
|
-
inAssociatedTokenAccount,
|
|
5790
|
-
amount,
|
|
5791
|
-
slippageBps,
|
|
5792
|
-
swapMode,
|
|
5793
|
-
quote: quoteToUse,
|
|
5794
|
-
reduceOnly,
|
|
5795
|
-
onlyDirectRoutes,
|
|
5796
|
-
});
|
|
5797
5828
|
const ixs = res.ixs;
|
|
5798
5829
|
const lookupTables = res.lookupTables;
|
|
5799
5830
|
|
|
@@ -5811,6 +5842,126 @@ export class DriftClient {
|
|
|
5811
5842
|
return txSig;
|
|
5812
5843
|
}
|
|
5813
5844
|
|
|
5845
|
+
public async getTitanSwapIx({
|
|
5846
|
+
titanClient,
|
|
5847
|
+
outMarketIndex,
|
|
5848
|
+
inMarketIndex,
|
|
5849
|
+
outAssociatedTokenAccount,
|
|
5850
|
+
inAssociatedTokenAccount,
|
|
5851
|
+
amount,
|
|
5852
|
+
slippageBps,
|
|
5853
|
+
swapMode,
|
|
5854
|
+
onlyDirectRoutes,
|
|
5855
|
+
reduceOnly,
|
|
5856
|
+
userAccountPublicKey,
|
|
5857
|
+
}: {
|
|
5858
|
+
titanClient: TitanClient;
|
|
5859
|
+
outMarketIndex: number;
|
|
5860
|
+
inMarketIndex: number;
|
|
5861
|
+
outAssociatedTokenAccount?: PublicKey;
|
|
5862
|
+
inAssociatedTokenAccount?: PublicKey;
|
|
5863
|
+
amount: BN;
|
|
5864
|
+
slippageBps?: number;
|
|
5865
|
+
swapMode?: string;
|
|
5866
|
+
onlyDirectRoutes?: boolean;
|
|
5867
|
+
reduceOnly?: SwapReduceOnly;
|
|
5868
|
+
userAccountPublicKey?: PublicKey;
|
|
5869
|
+
}): Promise<{
|
|
5870
|
+
ixs: TransactionInstruction[];
|
|
5871
|
+
lookupTables: AddressLookupTableAccount[];
|
|
5872
|
+
}> {
|
|
5873
|
+
const outMarket = this.getSpotMarketAccount(outMarketIndex);
|
|
5874
|
+
const inMarket = this.getSpotMarketAccount(inMarketIndex);
|
|
5875
|
+
|
|
5876
|
+
const isExactOut = swapMode === 'ExactOut';
|
|
5877
|
+
const exactOutBufferedAmountIn = amount.muln(1001).divn(1000); // Add 10bp buffer
|
|
5878
|
+
|
|
5879
|
+
const preInstructions = [];
|
|
5880
|
+
if (!outAssociatedTokenAccount) {
|
|
5881
|
+
const tokenProgram = this.getTokenProgramForSpotMarket(outMarket);
|
|
5882
|
+
outAssociatedTokenAccount = await this.getAssociatedTokenAccount(
|
|
5883
|
+
outMarket.marketIndex,
|
|
5884
|
+
false,
|
|
5885
|
+
tokenProgram
|
|
5886
|
+
);
|
|
5887
|
+
|
|
5888
|
+
const accountInfo = await this.connection.getAccountInfo(
|
|
5889
|
+
outAssociatedTokenAccount
|
|
5890
|
+
);
|
|
5891
|
+
if (!accountInfo) {
|
|
5892
|
+
preInstructions.push(
|
|
5893
|
+
this.createAssociatedTokenAccountIdempotentInstruction(
|
|
5894
|
+
outAssociatedTokenAccount,
|
|
5895
|
+
this.provider.wallet.publicKey,
|
|
5896
|
+
this.provider.wallet.publicKey,
|
|
5897
|
+
outMarket.mint,
|
|
5898
|
+
tokenProgram
|
|
5899
|
+
)
|
|
5900
|
+
);
|
|
5901
|
+
}
|
|
5902
|
+
}
|
|
5903
|
+
|
|
5904
|
+
if (!inAssociatedTokenAccount) {
|
|
5905
|
+
const tokenProgram = this.getTokenProgramForSpotMarket(inMarket);
|
|
5906
|
+
inAssociatedTokenAccount = await this.getAssociatedTokenAccount(
|
|
5907
|
+
inMarket.marketIndex,
|
|
5908
|
+
false,
|
|
5909
|
+
tokenProgram
|
|
5910
|
+
);
|
|
5911
|
+
|
|
5912
|
+
const accountInfo = await this.connection.getAccountInfo(
|
|
5913
|
+
inAssociatedTokenAccount
|
|
5914
|
+
);
|
|
5915
|
+
if (!accountInfo) {
|
|
5916
|
+
preInstructions.push(
|
|
5917
|
+
this.createAssociatedTokenAccountIdempotentInstruction(
|
|
5918
|
+
inAssociatedTokenAccount,
|
|
5919
|
+
this.provider.wallet.publicKey,
|
|
5920
|
+
this.provider.wallet.publicKey,
|
|
5921
|
+
inMarket.mint,
|
|
5922
|
+
tokenProgram
|
|
5923
|
+
)
|
|
5924
|
+
);
|
|
5925
|
+
}
|
|
5926
|
+
}
|
|
5927
|
+
|
|
5928
|
+
const { beginSwapIx, endSwapIx } = await this.getSwapIx({
|
|
5929
|
+
outMarketIndex,
|
|
5930
|
+
inMarketIndex,
|
|
5931
|
+
amountIn: isExactOut ? exactOutBufferedAmountIn : amount,
|
|
5932
|
+
inTokenAccount: inAssociatedTokenAccount,
|
|
5933
|
+
outTokenAccount: outAssociatedTokenAccount,
|
|
5934
|
+
reduceOnly,
|
|
5935
|
+
userAccountPublicKey,
|
|
5936
|
+
});
|
|
5937
|
+
|
|
5938
|
+
const { transactionMessage, lookupTables } = await titanClient.getSwap({
|
|
5939
|
+
inputMint: inMarket.mint,
|
|
5940
|
+
outputMint: outMarket.mint,
|
|
5941
|
+
amount,
|
|
5942
|
+
userPublicKey: this.provider.wallet.publicKey,
|
|
5943
|
+
slippageBps,
|
|
5944
|
+
swapMode: isExactOut ? TitanSwapMode.ExactOut : TitanSwapMode.ExactIn,
|
|
5945
|
+
onlyDirectRoutes,
|
|
5946
|
+
sizeConstraint: MAX_TX_BYTE_SIZE - 375, // buffer for drift instructions
|
|
5947
|
+
});
|
|
5948
|
+
|
|
5949
|
+
const titanInstructions = titanClient.getTitanInstructions({
|
|
5950
|
+
transactionMessage,
|
|
5951
|
+
inputMint: inMarket.mint,
|
|
5952
|
+
outputMint: outMarket.mint,
|
|
5953
|
+
});
|
|
5954
|
+
|
|
5955
|
+
const ixs = [
|
|
5956
|
+
...preInstructions,
|
|
5957
|
+
beginSwapIx,
|
|
5958
|
+
...titanInstructions,
|
|
5959
|
+
endSwapIx,
|
|
5960
|
+
];
|
|
5961
|
+
|
|
5962
|
+
return { ixs, lookupTables };
|
|
5963
|
+
}
|
|
5964
|
+
|
|
5814
5965
|
public async getJupiterSwapIxV6({
|
|
5815
5966
|
jupiterClient,
|
|
5816
5967
|
outMarketIndex,
|