Skip to content

Commit aa9b1cc

Browse files
committed
Implement Unified Swap Client
1 parent 8ccafa5 commit aa9b1cc

File tree

4 files changed

+435
-8
lines changed

4 files changed

+435
-8
lines changed

sdk/src/driftClient.ts

Lines changed: 151 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -160,8 +160,8 @@ import { castNumberToSpotPrecision } from './math/spotMarket';
160160
import {
161161
JupiterClient,
162162
QuoteResponse,
163-
SwapMode,
164163
} from './jupiter/jupiterClient';
164+
import { SwapMode } from './swap/UnifiedSwapClient';
165165
import { getNonIdleUserFilter } from './memcmp';
166166
import { UserStatsSubscriptionConfig } from './userStatsConfig';
167167
import { getMarinadeDepositIx, getMarinadeFinanceProgram } from './marinade';
@@ -210,9 +210,11 @@ import {
210210
isBuilderOrderCompleted,
211211
} from './math/builder';
212212
import { TitanClient, SwapMode as TitanSwapMode } from './titan/titanClient';
213+
import { UnifiedSwapClient } from './swap/UnifiedSwapClient';
213214

214215
/**
215-
* Union type for swap clients (Titan and Jupiter)
216+
* Union type for swap clients (Titan and Jupiter) - Legacy type
217+
* @deprecated Use UnifiedSwapClient class instead
216218
*/
217219
export type SwapClient = TitanClient | JupiterClient;
218220

@@ -5772,7 +5774,7 @@ export class DriftClient {
57725774
quote,
57735775
onlyDirectRoutes = false,
57745776
}: {
5775-
swapClient: SwapClient;
5777+
swapClient: UnifiedSwapClient | SwapClient;
57765778
outMarketIndex: number;
57775779
inMarketIndex: number;
57785780
outAssociatedTokenAccount?: PublicKey;
@@ -5793,7 +5795,23 @@ export class DriftClient {
57935795
lookupTables: AddressLookupTableAccount[];
57945796
};
57955797

5796-
if (swapClient instanceof TitanClient) {
5798+
// Use unified SwapClient if available
5799+
if (swapClient instanceof UnifiedSwapClient) {
5800+
res = await this.getSwapIxV2({
5801+
swapClient,
5802+
outMarketIndex,
5803+
inMarketIndex,
5804+
outAssociatedTokenAccount,
5805+
inAssociatedTokenAccount,
5806+
amount,
5807+
slippageBps,
5808+
swapMode,
5809+
onlyDirectRoutes,
5810+
reduceOnly,
5811+
quote,
5812+
v6,
5813+
});
5814+
} else if (swapClient instanceof TitanClient) {
57975815
res = await this.getTitanSwapIx({
57985816
titanClient: swapClient,
57995817
outMarketIndex,
@@ -5823,7 +5841,7 @@ export class DriftClient {
58235841
});
58245842
} else {
58255843
throw new Error(
5826-
'Invalid swap client type. Must be TitanClient or JupiterClient.'
5844+
'Invalid swap client type. Must be SwapClient, TitanClient, or JupiterClient.'
58275845
);
58285846
}
58295847

@@ -6243,6 +6261,134 @@ export class DriftClient {
62436261
return { beginSwapIx, endSwapIx };
62446262
}
62456263

6264+
public async getSwapIxV2({
6265+
swapClient,
6266+
outMarketIndex,
6267+
inMarketIndex,
6268+
outAssociatedTokenAccount,
6269+
inAssociatedTokenAccount,
6270+
amount,
6271+
slippageBps,
6272+
swapMode,
6273+
onlyDirectRoutes,
6274+
reduceOnly,
6275+
quote,
6276+
v6,
6277+
}: {
6278+
swapClient: UnifiedSwapClient;
6279+
outMarketIndex: number;
6280+
inMarketIndex: number;
6281+
outAssociatedTokenAccount?: PublicKey;
6282+
inAssociatedTokenAccount?: PublicKey;
6283+
amount: BN;
6284+
slippageBps?: number;
6285+
swapMode?: SwapMode;
6286+
onlyDirectRoutes?: boolean;
6287+
reduceOnly?: SwapReduceOnly;
6288+
quote?: QuoteResponse;
6289+
v6?: {
6290+
quote?: QuoteResponse;
6291+
};
6292+
}): Promise<{
6293+
ixs: TransactionInstruction[];
6294+
lookupTables: AddressLookupTableAccount[];
6295+
}> {
6296+
// Get market accounts to determine mints
6297+
const outMarket = this.getSpotMarketAccount(outMarketIndex);
6298+
const inMarket = this.getSpotMarketAccount(inMarketIndex);
6299+
6300+
const isExactOut = swapMode === 'ExactOut';
6301+
const exactOutBufferedAmountIn = amount.muln(1001).divn(1000); // Add 10bp buffer
6302+
6303+
const preInstructions: TransactionInstruction[] = [];
6304+
6305+
// Handle token accounts if not provided
6306+
let finalOutAssociatedTokenAccount = outAssociatedTokenAccount;
6307+
let finalInAssociatedTokenAccount = inAssociatedTokenAccount;
6308+
6309+
if (!finalOutAssociatedTokenAccount) {
6310+
const tokenProgram = this.getTokenProgramForSpotMarket(outMarket);
6311+
finalOutAssociatedTokenAccount = await this.getAssociatedTokenAccount(
6312+
outMarket.marketIndex,
6313+
false,
6314+
tokenProgram
6315+
);
6316+
6317+
const accountInfo = await this.connection.getAccountInfo(
6318+
finalOutAssociatedTokenAccount
6319+
);
6320+
if (!accountInfo) {
6321+
preInstructions.push(
6322+
this.createAssociatedTokenAccountIdempotentInstruction(
6323+
finalOutAssociatedTokenAccount,
6324+
this.provider.wallet.publicKey,
6325+
this.provider.wallet.publicKey,
6326+
outMarket.mint,
6327+
tokenProgram
6328+
)
6329+
);
6330+
}
6331+
}
6332+
6333+
if (!finalInAssociatedTokenAccount) {
6334+
const tokenProgram = this.getTokenProgramForSpotMarket(inMarket);
6335+
finalInAssociatedTokenAccount = await this.getAssociatedTokenAccount(
6336+
inMarket.marketIndex,
6337+
false,
6338+
tokenProgram
6339+
);
6340+
6341+
const accountInfo = await this.connection.getAccountInfo(
6342+
finalInAssociatedTokenAccount
6343+
);
6344+
if (!accountInfo) {
6345+
preInstructions.push(
6346+
this.createAssociatedTokenAccountIdempotentInstruction(
6347+
finalInAssociatedTokenAccount,
6348+
this.provider.wallet.publicKey,
6349+
this.provider.wallet.publicKey,
6350+
inMarket.mint,
6351+
tokenProgram
6352+
)
6353+
);
6354+
}
6355+
}
6356+
6357+
// Get drift swap instructions for begin and end
6358+
const { beginSwapIx, endSwapIx } = await this.getSwapIx({
6359+
outMarketIndex,
6360+
inMarketIndex,
6361+
amountIn: isExactOut ? exactOutBufferedAmountIn : amount,
6362+
inTokenAccount: finalInAssociatedTokenAccount,
6363+
outTokenAccount: finalOutAssociatedTokenAccount,
6364+
reduceOnly,
6365+
});
6366+
6367+
// Get core swap instructions from SwapClient
6368+
const swapResult = await swapClient.getSwapInstructions({
6369+
inputMint: inMarket.mint,
6370+
outputMint: outMarket.mint,
6371+
amount,
6372+
userPublicKey: this.provider.wallet.publicKey,
6373+
slippageBps,
6374+
swapMode,
6375+
onlyDirectRoutes,
6376+
quote: quote ?? v6?.quote,
6377+
});
6378+
6379+
const allInstructions = [
6380+
...preInstructions,
6381+
beginSwapIx,
6382+
...swapResult.instructions,
6383+
endSwapIx,
6384+
];
6385+
6386+
return {
6387+
ixs: allInstructions,
6388+
lookupTables: swapResult.lookupTables,
6389+
};
6390+
}
6391+
62466392
public async stakeForMSOL({ amount }: { amount: BN }): Promise<TxSigAndSlot> {
62476393
const ixs = await this.getStakeForMSOLIx({ amount });
62486394
const tx = await this.buildTransaction(ixs);

sdk/src/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,8 @@ export * from './events/webSocketLogProvider';
5353
export * from './events/parse';
5454
export * from './events/pollingLogProvider';
5555
export * from './jupiter/jupiterClient';
56-
export { TitanClient } from './titan/titanClient';
56+
// Primary swap client interface - use this for all swap operations
57+
export * from './swap/UnifiedSwapClient';
5758
export * from './math/auction';
5859
export * from './math/builder';
5960
export * from './math/spotMarket';

sdk/src/jupiter/jupiterClient.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,7 @@ import {
88
} from '@solana/web3.js';
99
import fetch from 'node-fetch';
1010
import { BN } from '@coral-xyz/anchor';
11-
12-
export type SwapMode = 'ExactIn' | 'ExactOut';
11+
import { SwapMode } from '../swap/UnifiedSwapClient';
1312

1413
export interface MarketInfo {
1514
id: string;

0 commit comments

Comments
 (0)