carbon_fluxbeam_decoder/instructions/
mod.rs

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        // Arrange
68        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        // Act
162        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
173        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        // Arrange
182        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        // Act
327        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
337        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        // Arrange
346        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        // Act
495        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
509        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}