solana_trader_client_rust/provider/ws/
swap.rs

1use anyhow::Result;
2use base64::{engine::general_purpose, Engine};
3use serde_json::json;
4use solana_sdk::{
5    message::{v0, VersionedMessage},
6    transaction::VersionedTransaction,
7};
8use solana_trader_proto::api;
9
10use crate::{
11    common::signing::SubmitParams,
12    provider::utils::{
13        convert_address_lookup_table, convert_jupiter_instructions, convert_raydium_instructions,
14        create_transaction_message,
15    },
16};
17
18use super::WebSocketClient;
19
20impl WebSocketClient {
21    pub async fn post_raydium_swap(
22        &self,
23        request: &api::PostRaydiumSwapRequest,
24    ) -> Result<api::PostRaydiumSwapResponse> {
25        let params = json!({
26            "ownerAddress": request.owner_address,
27            "inToken": request.in_token,
28            "outToken": request.out_token,
29            "inAmount": request.in_amount,
30            "slippage": request.slippage,
31            "computeLimit": request.compute_limit,
32            "computePrice": request.compute_price,
33            "tip": request.tip,
34        });
35
36        self.conn.request("PostRaydiumSwap", params).await
37    }
38
39    pub async fn post_raydium_route_swap(
40        &self,
41        request: &api::PostRaydiumRouteSwapRequest,
42    ) -> Result<api::PostRaydiumRouteSwapResponse> {
43        let params = json!({
44            "ownerAddress": request.owner_address,
45            "slippage": request.slippage,
46            "steps": request.steps,
47            "computeLimit": request.compute_limit,
48            "computePrice": request.compute_price,
49            "tip": request.tip,
50        });
51
52        self.conn.request("PostRaydiumRouteSwap", params).await
53    }
54
55    pub async fn post_raydium_swap_instructions(
56        &self,
57        request: &api::PostRaydiumSwapInstructionsRequest,
58    ) -> Result<api::PostRaydiumSwapInstructionsResponse> {
59        let params = json!({
60            "ownerAddress": request.owner_address,
61            "inToken": request.in_token,
62            "outToken": request.out_token,
63            "inAmount": request.in_amount,
64            "slippage": request.slippage,
65            "computeLimit": request.compute_limit,
66            "computePrice": request.compute_price,
67            "tip": request.tip,
68        });
69
70        self.conn
71            .request("PostRaydiumSwapInstructions", params)
72            .await
73    }
74
75    pub async fn submit_raydium_swap_instructions(
76        &self,
77        request: api::PostRaydiumSwapInstructionsRequest,
78        submit_opts: SubmitParams,
79        use_bundle: bool,
80    ) -> Result<Vec<String>> {
81        let swap_instructions = self.post_raydium_swap_instructions(&request).await?;
82
83        let instructions = convert_raydium_instructions(&swap_instructions.instructions)?;
84
85        let hash_res: api::GetRecentBlockHashResponseV2 =
86            self.conn.request("GetRecentBlockHashV2", json!({})).await?;
87
88        let tx_message = create_transaction_message(instructions, &hash_res.block_hash)?;
89
90        self.sign_and_submit(vec![tx_message], submit_opts, use_bundle)
91            .await
92    }
93
94    pub async fn post_raydium_cpmm_swap(
95        &self,
96        request: &api::PostRaydiumCpmmSwapRequest,
97    ) -> Result<api::PostRaydiumCpmmSwapResponse> {
98        let params = json!({
99            "ownerAddress": request.owner_address,
100            "poolAddress": request.pool_address,
101            "inToken": request.in_token,
102            "outToken": request.out_token,
103            "inAmount": request.in_amount,
104            "slippage": request.slippage,
105            "computeLimit": request.compute_limit,
106            "computePrice": request.compute_price,
107            "tip": request.tip,
108        });
109
110        self.conn.request("PostRaydiumCPMMSwap", params).await
111    }
112
113    pub async fn post_raydium_clmm_swap(
114        &self,
115        request: &api::PostRaydiumSwapRequest,
116    ) -> Result<api::PostRaydiumSwapResponse> {
117        let params = json!({
118            "ownerAddress": request.owner_address,
119            "inToken": request.in_token,
120            "outToken": request.out_token,
121            "inAmount": request.in_amount,
122            "slippage": request.slippage,
123            "computeLimit": request.compute_limit,
124            "computePrice": request.compute_price,
125            "tip": request.tip,
126        });
127
128        self.conn.request("PostRaydiumCLMMSwap", params).await
129    }
130
131    pub async fn post_raydium_clmm_route_swap(
132        &self,
133        request: &api::PostRaydiumRouteSwapRequest,
134    ) -> Result<api::PostRaydiumRouteSwapResponse> {
135        let params = json!({
136            "ownerAddress": request.owner_address,
137            "slippage": request.slippage,
138            "steps": request.steps,
139        });
140
141        self.conn.request("PostRaydiumCLMMRouteSwap", params).await
142    }
143
144    // NOTE: Fast mode is not used as of 11/1, breaks the endpoint.
145    pub async fn post_jupiter_swap(
146        &self,
147        request: &api::PostJupiterSwapRequest,
148    ) -> Result<api::PostJupiterSwapResponse> {
149        let modified_request = json!({
150            "ownerAddress": request.owner_address,
151            "inToken": request.in_token,
152            "outToken": request.out_token,
153            "inAmount": request.in_amount,
154            "slippage": request.slippage,
155            "computeLimit": request.compute_limit,
156            "computePrice": request.compute_price,
157            "tip": request.tip,
158        });
159
160        self.conn.request("PostJupiterSwap", modified_request).await
161    }
162
163    pub async fn post_jupiter_route_swap(
164        &self,
165        request: &api::PostJupiterRouteSwapRequest,
166    ) -> Result<api::PostJupiterRouteSwapResponse> {
167        let params = json!({
168            "ownerAddress": request.owner_address,
169            "slippage": request.slippage,
170            "steps": request.steps,
171            "computeLimit": request.compute_limit,
172            "computePrice": request.compute_price,
173            "tip": request.tip,
174        });
175
176        self.conn.request("PostJupiterRouteSwap", params).await
177    }
178
179    pub async fn post_jupiter_swap_instructions(
180        &self,
181        request: &api::PostJupiterSwapInstructionsRequest,
182    ) -> Result<api::PostJupiterSwapInstructionsResponse> {
183        let params = json!({
184            "ownerAddress": request.owner_address,
185            "inToken": request.in_token,
186            "outToken": request.out_token,
187            "inAmount": request.in_amount,
188            "slippage": request.slippage,
189            "computePrice": request.compute_price,
190            "tip": request.tip,
191        });
192
193        self.conn
194            .request("PostJupiterSwapInstructions", params)
195            .await
196    }
197
198    pub async fn submit_jupiter_swap_instructions(
199        &self,
200        request: api::PostJupiterSwapInstructionsRequest,
201        submit_opts: SubmitParams,
202        use_bundle: bool,
203    ) -> Result<Vec<String>> {
204        let keypair = self.get_keypair()?;
205
206        let swap_instructions = self.post_jupiter_swap_instructions(&request).await?;
207
208        let address_lookup_tables =
209            convert_address_lookup_table(&swap_instructions.address_lookup_table_addresses)?;
210
211        let instructions = convert_jupiter_instructions(&swap_instructions.instructions)?;
212
213        let hash_res: api::GetRecentBlockHashResponseV2 =
214            self.conn.request("GetRecentBlockHashV2", json!({})).await?;
215
216        let message = VersionedMessage::V0(v0::Message::try_compile(
217            &self.public_key.unwrap(),
218            &instructions,
219            &address_lookup_tables,
220            hash_res.block_hash.parse()?,
221        )?);
222
223        let tx = VersionedTransaction::try_new(message, &[keypair])?;
224
225        let tx_message = api::TransactionMessage {
226            content: general_purpose::STANDARD.encode(bincode::serialize(&tx)?),
227            is_cleanup: false,
228        };
229
230        self.sign_and_submit(vec![tx_message], submit_opts, use_bundle)
231            .await
232    }
233
234    pub async fn post_trade_swap(
235        &self,
236        request: &api::TradeSwapRequest,
237    ) -> Result<api::TradeSwapResponse> {
238        let params = json!({
239            "ownerAddress": request.owner_address,
240            "inToken": request.in_token,
241            "outToken": request.out_token,
242            "inAmount": request.in_amount,
243            "slippage": request.slippage,
244            "project": request.project,
245            "computeLimit": request.compute_limit,
246            "computePrice": request.compute_price,
247            "tip": request.tip,
248        });
249
250        self.conn.request("PostTradeSwap", params).await
251    }
252
253    pub async fn post_route_trade_swap(
254        &self,
255        request: &api::RouteTradeSwapRequest,
256    ) -> Result<api::TradeSwapResponse> {
257        let params = json!({
258            "ownerAddress": request.owner_address,
259            "project": request.project,
260            "slippage": request.slippage,
261            "steps": request.steps,
262        });
263
264        self.conn.request("PostRouteTradeSwap", params).await
265    }
266
267    pub async fn post_pump_fun_swap(
268        &self,
269        request: &api::PostPumpFunSwapRequest
270    ) -> Result<api::PostPumpFunSwapResponse> {
271        let params = json!({
272            "userAddress": request.user_address,
273            "bondingCurveAddress": request.bonding_curve_address,
274            "tokenAddress": request.token_address,
275            "creator": request.creator,
276            "tokenAmount": request.token_amount,
277            "solThreshold": request.sol_threshold,
278            "isBuy": request.is_buy,
279            "slippage": request.slippage,
280            "computeLimit": request.compute_limit,
281            "computePrice": request.compute_price,
282            "tip": request.tip
283        });
284        self.conn.request("PostPumpFunSwap", params).await
285    }
286
287    pub async fn post_pump_fun_swap_sol(
288        &self,
289        request: &api::PostPumpFunSwapRequestSol
290    ) -> Result<api::PostPumpFunSwapResponse> {
291        let params = json!({
292            "userAddress": request.user_address,
293            "bondingCurveAddress": request.bonding_curve_address,
294            "tokenAddress": request.token_address,
295            "creator": request.creator,
296            "solAmount": request.sol_amount,
297            "slippage": request.slippage,
298            "computeLimit": request.compute_limit,
299            "computePrice": request.compute_price,
300            "tip": request.tip
301        });
302        self.conn.request("PostPumpFunSwapSol", params).await
303    }
304}