1use crate::PROGRAM_ID;
2
3use super::FluxbeamDecoder;
4pub mod deposit_all_token_types;
5pub mod deposit_single_token_type_exact_amount_in;
6pub mod initialize;
7pub mod swap;
8pub mod withdraw_all_token_types;
9pub mod withdraw_single_token_type_exact_amount_out;
10
11#[derive(
12 carbon_core::InstructionType,
13 serde::Serialize,
14 serde::Deserialize,
15 PartialEq,
16 Eq,
17 Debug,
18 Clone,
19 Hash,
20)]
21pub enum FluxbeamInstruction {
22 Initialize(initialize::Initialize),
23 Swap(swap::Swap),
24 DepositAllTokenTypes(deposit_all_token_types::DepositAllTokenTypes),
25 WithdrawAllTokenTypes(withdraw_all_token_types::WithdrawAllTokenTypes),
26 DepositSingleTokenTypeExactAmountIn(
27 deposit_single_token_type_exact_amount_in::DepositSingleTokenTypeExactAmountIn,
28 ),
29 WithdrawSingleTokenTypeExactAmountOut(
30 withdraw_single_token_type_exact_amount_out::WithdrawSingleTokenTypeExactAmountOut,
31 ),
32}
33
34impl carbon_core::instruction::InstructionDecoder<'_> for FluxbeamDecoder {
35 type InstructionType = FluxbeamInstruction;
36
37 fn decode_instruction(
38 &self,
39 instruction: &solana_instruction::Instruction,
40 ) -> Option<carbon_core::instruction::DecodedInstruction<Self::InstructionType>> {
41 if !instruction.program_id.eq(&PROGRAM_ID) {
42 return None;
43 }
44
45 carbon_core::try_decode_instructions!(instruction,
46 FluxbeamInstruction::Initialize => initialize::Initialize,
47 FluxbeamInstruction::Swap => swap::Swap,
48 FluxbeamInstruction::DepositAllTokenTypes => deposit_all_token_types::DepositAllTokenTypes,
49 FluxbeamInstruction::WithdrawAllTokenTypes => withdraw_all_token_types::WithdrawAllTokenTypes,
50 FluxbeamInstruction::DepositSingleTokenTypeExactAmountIn => deposit_single_token_type_exact_amount_in::DepositSingleTokenTypeExactAmountIn,
51 FluxbeamInstruction::WithdrawSingleTokenTypeExactAmountOut => withdraw_single_token_type_exact_amount_out::WithdrawSingleTokenTypeExactAmountOut,
52 )
53 }
54}
55
56#[cfg(test)]
57mod tests {
58 use carbon_core::{deserialize::ArrangeAccounts, instruction::InstructionDecoder};
59 use solana_instruction::AccountMeta;
60
61 use crate::types::{CurveType, Fees, SwapCurve};
62
63 use super::*;
64
65 #[test]
66 fn test_decode_initialize() {
67 let expected_ix = FluxbeamInstruction::Initialize(initialize::Initialize {
69 fees: Fees {
70 trade_fee_numerator: 2,
71 trade_fee_denominator: 1000,
72 owner_trade_fee_numerator: 90,
73 owner_trade_fee_denominator: 100,
74 owner_withdraw_fee_numerator: 98,
75 owner_withdraw_fee_denominator: 100,
76 host_fee_numerator: 0,
77 host_fee_denominator: 10000,
78 },
79 swap_curve: SwapCurve {
80 curve_type: CurveType::ConstantProduct,
81 calculator: [0u8; 32],
82 },
83 });
84 let expected_accounts = vec![
85 AccountMeta::new(
86 solana_pubkey::Pubkey::from_str_const(
87 "6bJUX2XqmGp6nZGrnEoZh3mAt8M73G1AZbgUhT4hooAC",
88 ),
89 true,
90 ),
91 AccountMeta::new_readonly(
92 solana_pubkey::Pubkey::from_str_const(
93 "2CQ6cW8RzowMEcdEiRRgEWzaYjpLWaHv1WoVyWfF8nsY",
94 ),
95 false,
96 ),
97 AccountMeta::new(
98 solana_pubkey::Pubkey::from_str_const(
99 "jM5cFHP9iPj9en1fJFJLfRpLt68Y81UdWfXHv9an3HK",
100 ),
101 false,
102 ),
103 AccountMeta::new(
104 solana_pubkey::Pubkey::from_str_const(
105 "8a4WD4hbfuPyiistrVU8qcpwMcJmf3RBuw1s1tvVYJ1Q",
106 ),
107 false,
108 ),
109 AccountMeta::new(
110 solana_pubkey::Pubkey::from_str_const(
111 "7XeJQykinTiK1EveXb9y4zodFtdtu1YwkygBmWbz1pC3",
112 ),
113 true,
114 ),
115 AccountMeta::new(
116 solana_pubkey::Pubkey::from_str_const(
117 "396TeW1MeyQvFGgxjaxJxRFkuiir4Ye4imuxVDcqfE88",
118 ),
119 false,
120 ),
121 AccountMeta::new(
122 solana_pubkey::Pubkey::from_str_const(
123 "396TeW1MeyQvFGgxjaxJxRFkuiir4Ye4imuxVDcqfE88",
124 ),
125 false,
126 ),
127 AccountMeta::new_readonly(
128 solana_pubkey::Pubkey::from_str_const(
129 "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb",
130 ),
131 false,
132 ),
133 ];
134 let expected_arranged_accounts = initialize::InitializeInstructionAccounts {
135 swap: solana_pubkey::Pubkey::from_str_const(
136 "6bJUX2XqmGp6nZGrnEoZh3mAt8M73G1AZbgUhT4hooAC",
137 ),
138 authority: solana_pubkey::Pubkey::from_str_const(
139 "2CQ6cW8RzowMEcdEiRRgEWzaYjpLWaHv1WoVyWfF8nsY",
140 ),
141 token_a: solana_pubkey::Pubkey::from_str_const(
142 "jM5cFHP9iPj9en1fJFJLfRpLt68Y81UdWfXHv9an3HK",
143 ),
144 token_b: solana_pubkey::Pubkey::from_str_const(
145 "8a4WD4hbfuPyiistrVU8qcpwMcJmf3RBuw1s1tvVYJ1Q",
146 ),
147 pool: solana_pubkey::Pubkey::from_str_const(
148 "7XeJQykinTiK1EveXb9y4zodFtdtu1YwkygBmWbz1pC3",
149 ),
150 fee: solana_pubkey::Pubkey::from_str_const(
151 "396TeW1MeyQvFGgxjaxJxRFkuiir4Ye4imuxVDcqfE88",
152 ),
153 destination: solana_pubkey::Pubkey::from_str_const(
154 "396TeW1MeyQvFGgxjaxJxRFkuiir4Ye4imuxVDcqfE88",
155 ),
156 token_program: solana_pubkey::Pubkey::from_str_const(
157 "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb",
158 ),
159 };
160
161 let decoder = FluxbeamDecoder;
163 let instruction = carbon_test_utils::read_instruction("tests/fixtures/initialize_ix.json")
164 .expect("read fixture");
165 let decoded = decoder
166 .decode_instruction(&instruction)
167 .expect("decode instruction");
168 let decoded_arranged_accounts =
169 initialize::Initialize::arrange_accounts(&instruction.accounts)
170 .expect("aranage accounts");
171
172 assert_eq!(decoded.data, expected_ix);
174 assert_eq!(decoded.accounts, expected_accounts);
175 assert_eq!(decoded.program_id, PROGRAM_ID);
176 assert_eq!(decoded_arranged_accounts, expected_arranged_accounts);
177 }
178
179 #[test]
180 fn test_decode_swap() {
181 let expected_ix = FluxbeamInstruction::Swap(swap::Swap {
183 amount_in: 800000000,
184 minimum_amount_out: 0,
185 });
186 let expected_accounts = vec![
187 AccountMeta::new_readonly(
188 solana_pubkey::Pubkey::from_str_const(
189 "6bJUX2XqmGp6nZGrnEoZh3mAt8M73G1AZbgUhT4hooAC",
190 ),
191 false,
192 ),
193 AccountMeta::new_readonly(
194 solana_pubkey::Pubkey::from_str_const(
195 "2CQ6cW8RzowMEcdEiRRgEWzaYjpLWaHv1WoVyWfF8nsY",
196 ),
197 false,
198 ),
199 AccountMeta::new(
200 solana_pubkey::Pubkey::from_str_const(
201 "AB1daTZcHAySAexN1SpacinrwRixNP7nLd31TVnNXMLx",
202 ),
203 true,
204 ),
205 AccountMeta::new(
206 solana_pubkey::Pubkey::from_str_const(
207 "DX1mX7WN7jQJXzaiiQR6W1G69xHg3kXjDtrEyYXxgZAm",
208 ),
209 false,
210 ),
211 AccountMeta::new(
212 solana_pubkey::Pubkey::from_str_const(
213 "jM5cFHP9iPj9en1fJFJLfRpLt68Y81UdWfXHv9an3HK",
214 ),
215 false,
216 ),
217 AccountMeta::new(
218 solana_pubkey::Pubkey::from_str_const(
219 "8a4WD4hbfuPyiistrVU8qcpwMcJmf3RBuw1s1tvVYJ1Q",
220 ),
221 false,
222 ),
223 AccountMeta::new(
224 solana_pubkey::Pubkey::from_str_const(
225 "Ew1Aj2Mm82KCN9dtMNnhVXZDjUfiiu18CNj9Qx6Vystk",
226 ),
227 false,
228 ),
229 AccountMeta::new(
230 solana_pubkey::Pubkey::from_str_const(
231 "7XeJQykinTiK1EveXb9y4zodFtdtu1YwkygBmWbz1pC3",
232 ),
233 false,
234 ),
235 AccountMeta::new(
236 solana_pubkey::Pubkey::from_str_const(
237 "396TeW1MeyQvFGgxjaxJxRFkuiir4Ye4imuxVDcqfE88",
238 ),
239 false,
240 ),
241 AccountMeta::new_readonly(
242 solana_pubkey::Pubkey::from_str_const(
243 "So11111111111111111111111111111111111111112",
244 ),
245 false,
246 ),
247 AccountMeta::new(
248 solana_pubkey::Pubkey::from_str_const(
249 "3YkBR2w1ttpWKzdP5XQtzXqsGFS9i1mGg9pDrqn4e9j6",
250 ),
251 false,
252 ),
253 AccountMeta::new_readonly(
254 solana_pubkey::Pubkey::from_str_const(
255 "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
256 ),
257 false,
258 ),
259 AccountMeta::new_readonly(
260 solana_pubkey::Pubkey::from_str_const(
261 "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb",
262 ),
263 false,
264 ),
265 AccountMeta::new_readonly(
266 solana_pubkey::Pubkey::from_str_const(
267 "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb",
268 ),
269 false,
270 ),
271 AccountMeta::new(
272 solana_pubkey::Pubkey::from_str_const(
273 "396TeW1MeyQvFGgxjaxJxRFkuiir4Ye4imuxVDcqfE88",
274 ),
275 false,
276 ),
277 ];
278 let expected_arranged_accounts = swap::SwapInstructionAccounts {
279 swap: solana_pubkey::Pubkey::from_str_const(
280 "6bJUX2XqmGp6nZGrnEoZh3mAt8M73G1AZbgUhT4hooAC",
281 ),
282 authority: solana_pubkey::Pubkey::from_str_const(
283 "2CQ6cW8RzowMEcdEiRRgEWzaYjpLWaHv1WoVyWfF8nsY",
284 ),
285 user_transfer_authority: solana_pubkey::Pubkey::from_str_const(
286 "AB1daTZcHAySAexN1SpacinrwRixNP7nLd31TVnNXMLx",
287 ),
288 source: solana_pubkey::Pubkey::from_str_const(
289 "DX1mX7WN7jQJXzaiiQR6W1G69xHg3kXjDtrEyYXxgZAm",
290 ),
291 swap_source: solana_pubkey::Pubkey::from_str_const(
292 "jM5cFHP9iPj9en1fJFJLfRpLt68Y81UdWfXHv9an3HK",
293 ),
294 swap_destination: solana_pubkey::Pubkey::from_str_const(
295 "8a4WD4hbfuPyiistrVU8qcpwMcJmf3RBuw1s1tvVYJ1Q",
296 ),
297 destination: solana_pubkey::Pubkey::from_str_const(
298 "Ew1Aj2Mm82KCN9dtMNnhVXZDjUfiiu18CNj9Qx6Vystk",
299 ),
300 pool_mint: solana_pubkey::Pubkey::from_str_const(
301 "7XeJQykinTiK1EveXb9y4zodFtdtu1YwkygBmWbz1pC3",
302 ),
303 pool_fee: solana_pubkey::Pubkey::from_str_const(
304 "396TeW1MeyQvFGgxjaxJxRFkuiir4Ye4imuxVDcqfE88",
305 ),
306 source_mint: solana_pubkey::Pubkey::from_str_const(
307 "So11111111111111111111111111111111111111112",
308 ),
309 destination_mint: solana_pubkey::Pubkey::from_str_const(
310 "3YkBR2w1ttpWKzdP5XQtzXqsGFS9i1mGg9pDrqn4e9j6",
311 ),
312 source_token_program: solana_pubkey::Pubkey::from_str_const(
313 "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
314 ),
315 destination_token_program: solana_pubkey::Pubkey::from_str_const(
316 "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb",
317 ),
318 pool_token_program: solana_pubkey::Pubkey::from_str_const(
319 "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb",
320 ),
321 swap_program: solana_pubkey::Pubkey::from_str_const(
322 "396TeW1MeyQvFGgxjaxJxRFkuiir4Ye4imuxVDcqfE88",
323 ),
324 };
325
326 let decoder = FluxbeamDecoder;
328 let instruction = carbon_test_utils::read_instruction("tests/fixtures/swap_ix.json")
329 .expect("read fixture");
330 let decoded = decoder
331 .decode_instruction(&instruction)
332 .expect("decode instruction");
333 let decoded_arranged_accounts =
334 swap::Swap::arrange_accounts(&instruction.accounts).expect("aranage accounts");
335
336 assert_eq!(decoded.data, expected_ix);
338 assert_eq!(decoded.accounts, expected_accounts);
339 assert_eq!(decoded.program_id, PROGRAM_ID);
340 assert_eq!(decoded_arranged_accounts, expected_arranged_accounts);
341 }
342
343 #[test]
344 fn test_decode_withdraw_all_token_types() {
345 let expected_ix = FluxbeamInstruction::WithdrawAllTokenTypes(
347 withdraw_all_token_types::WithdrawAllTokenTypes {
348 pool_token_amount: 1149106633,
349 minimum_token_a_amount: 0,
350 minimum_token_b_amount: 0,
351 },
352 );
353 let expected_accounts = vec![
354 AccountMeta::new_readonly(
355 solana_pubkey::Pubkey::from_str_const(
356 "6bJUX2XqmGp6nZGrnEoZh3mAt8M73G1AZbgUhT4hooAC",
357 ),
358 false,
359 ),
360 AccountMeta::new_readonly(
361 solana_pubkey::Pubkey::from_str_const(
362 "2CQ6cW8RzowMEcdEiRRgEWzaYjpLWaHv1WoVyWfF8nsY",
363 ),
364 false,
365 ),
366 AccountMeta::new(
367 solana_pubkey::Pubkey::from_str_const(
368 "3oYDvLGyqdTsZ5mU8gKPxjjkKsZiE6y9qUE2Ed4qR6iG",
369 ),
370 true,
371 ),
372 AccountMeta::new(
373 solana_pubkey::Pubkey::from_str_const(
374 "7XeJQykinTiK1EveXb9y4zodFtdtu1YwkygBmWbz1pC3",
375 ),
376 false,
377 ),
378 AccountMeta::new(
379 solana_pubkey::Pubkey::from_str_const(
380 "396TeW1MeyQvFGgxjaxJxRFkuiir4Ye4imuxVDcqfE88",
381 ),
382 false,
383 ),
384 AccountMeta::new(
385 solana_pubkey::Pubkey::from_str_const(
386 "jM5cFHP9iPj9en1fJFJLfRpLt68Y81UdWfXHv9an3HK",
387 ),
388 false,
389 ),
390 AccountMeta::new(
391 solana_pubkey::Pubkey::from_str_const(
392 "8a4WD4hbfuPyiistrVU8qcpwMcJmf3RBuw1s1tvVYJ1Q",
393 ),
394 false,
395 ),
396 AccountMeta::new(
397 solana_pubkey::Pubkey::from_str_const(
398 "D86hML8ecnD7kPLpjKDPzRCToPKyJ6xfQUoJ39wLTxYZ",
399 ),
400 false,
401 ),
402 AccountMeta::new(
403 solana_pubkey::Pubkey::from_str_const(
404 "7xUofx3SxTGYgsVbdnYd6qMuxzaBGbHFoiAfD2q5d1aA",
405 ),
406 false,
407 ),
408 AccountMeta::new(
409 solana_pubkey::Pubkey::from_str_const(
410 "396TeW1MeyQvFGgxjaxJxRFkuiir4Ye4imuxVDcqfE88",
411 ),
412 false,
413 ),
414 AccountMeta::new_readonly(
415 solana_pubkey::Pubkey::from_str_const(
416 "So11111111111111111111111111111111111111112",
417 ),
418 false,
419 ),
420 AccountMeta::new_readonly(
421 solana_pubkey::Pubkey::from_str_const(
422 "3YkBR2w1ttpWKzdP5XQtzXqsGFS9i1mGg9pDrqn4e9j6",
423 ),
424 false,
425 ),
426 AccountMeta::new_readonly(
427 solana_pubkey::Pubkey::from_str_const(
428 "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb",
429 ),
430 false,
431 ),
432 AccountMeta::new_readonly(
433 solana_pubkey::Pubkey::from_str_const(
434 "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
435 ),
436 false,
437 ),
438 AccountMeta::new_readonly(
439 solana_pubkey::Pubkey::from_str_const(
440 "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb",
441 ),
442 false,
443 ),
444 ];
445 let expected_arranged_accounts =
446 withdraw_all_token_types::WithdrawAllTokenTypesInstructionAccounts {
447 swap: solana_pubkey::Pubkey::from_str_const(
448 "6bJUX2XqmGp6nZGrnEoZh3mAt8M73G1AZbgUhT4hooAC",
449 ),
450 authority: solana_pubkey::Pubkey::from_str_const(
451 "2CQ6cW8RzowMEcdEiRRgEWzaYjpLWaHv1WoVyWfF8nsY",
452 ),
453 user_transfer_authority: solana_pubkey::Pubkey::from_str_const(
454 "3oYDvLGyqdTsZ5mU8gKPxjjkKsZiE6y9qUE2Ed4qR6iG",
455 ),
456 pool_mint: solana_pubkey::Pubkey::from_str_const(
457 "7XeJQykinTiK1EveXb9y4zodFtdtu1YwkygBmWbz1pC3",
458 ),
459 source: solana_pubkey::Pubkey::from_str_const(
460 "396TeW1MeyQvFGgxjaxJxRFkuiir4Ye4imuxVDcqfE88",
461 ),
462 swap_token_a: solana_pubkey::Pubkey::from_str_const(
463 "jM5cFHP9iPj9en1fJFJLfRpLt68Y81UdWfXHv9an3HK",
464 ),
465 swap_token_b: solana_pubkey::Pubkey::from_str_const(
466 "8a4WD4hbfuPyiistrVU8qcpwMcJmf3RBuw1s1tvVYJ1Q",
467 ),
468 destination_token_a: solana_pubkey::Pubkey::from_str_const(
469 "D86hML8ecnD7kPLpjKDPzRCToPKyJ6xfQUoJ39wLTxYZ",
470 ),
471 destination_token_b: solana_pubkey::Pubkey::from_str_const(
472 "7xUofx3SxTGYgsVbdnYd6qMuxzaBGbHFoiAfD2q5d1aA",
473 ),
474 fee_account: solana_pubkey::Pubkey::from_str_const(
475 "396TeW1MeyQvFGgxjaxJxRFkuiir4Ye4imuxVDcqfE88",
476 ),
477 token_a_mint: solana_pubkey::Pubkey::from_str_const(
478 "So11111111111111111111111111111111111111112",
479 ),
480 token_b_mint: solana_pubkey::Pubkey::from_str_const(
481 "3YkBR2w1ttpWKzdP5XQtzXqsGFS9i1mGg9pDrqn4e9j6",
482 ),
483 pool_token_program: solana_pubkey::Pubkey::from_str_const(
484 "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb",
485 ),
486 token_a_program: solana_pubkey::Pubkey::from_str_const(
487 "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
488 ),
489 token_b_program: solana_pubkey::Pubkey::from_str_const(
490 "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb",
491 ),
492 };
493
494 let decoder = FluxbeamDecoder;
496 let instruction =
497 carbon_test_utils::read_instruction("tests/fixtures/withdraw_all_token_types_ix.json")
498 .expect("read fixture");
499 let decoded = decoder
500 .decode_instruction(&instruction)
501 .expect("decode instruction");
502 let decoded_arranged_accounts =
503 withdraw_all_token_types::WithdrawAllTokenTypes::arrange_accounts(
504 &instruction.accounts,
505 )
506 .expect("aranage accounts");
507
508 assert_eq!(decoded.data, expected_ix);
510 assert_eq!(decoded.accounts, expected_accounts);
511 assert_eq!(decoded.program_id, PROGRAM_ID);
512 assert_eq!(decoded_arranged_accounts, expected_arranged_accounts);
513 }
514}