Skip to main content

spl_token_swap/
instruction.rs

1//! Instruction types
2
3#![allow(clippy::too_many_arguments)]
4
5use crate::curve::{base::SwapCurve, fees::Fees};
6use crate::error::SwapError;
7use solana_program::{
8    instruction::{AccountMeta, Instruction},
9    program_error::ProgramError,
10    program_pack::Pack,
11    pubkey::Pubkey,
12};
13use std::convert::TryInto;
14use std::mem::size_of;
15
16#[cfg(feature = "fuzz")]
17use arbitrary::Arbitrary;
18
19/// Initialize instruction data
20#[repr(C)]
21#[derive(Debug, PartialEq)]
22pub struct Initialize {
23    /// all swap fees
24    pub fees: Fees,
25    /// swap curve info for pool, including CurveType and anything
26    /// else that may be required
27    pub swap_curve: SwapCurve,
28}
29
30/// Swap instruction data
31#[cfg_attr(feature = "fuzz", derive(Arbitrary))]
32#[repr(C)]
33#[derive(Clone, Debug, PartialEq)]
34pub struct Swap {
35    /// SOURCE amount to transfer, output to DESTINATION is based on the exchange rate
36    pub amount_in: u64,
37    /// Minimum amount of DESTINATION token to output, prevents excessive slippage
38    pub minimum_amount_out: u64,
39}
40
41/// DepositAllTokenTypes instruction data
42#[cfg_attr(feature = "fuzz", derive(Arbitrary))]
43#[repr(C)]
44#[derive(Clone, Debug, PartialEq)]
45pub struct DepositAllTokenTypes {
46    /// Pool token amount to transfer. token_a and token_b amount are set by
47    /// the current exchange rate and size of the pool
48    pub pool_token_amount: u64,
49    /// Maximum token A amount to deposit, prevents excessive slippage
50    pub maximum_token_a_amount: u64,
51    /// Maximum token B amount to deposit, prevents excessive slippage
52    pub maximum_token_b_amount: u64,
53}
54
55/// WithdrawAllTokenTypes instruction data
56#[cfg_attr(feature = "fuzz", derive(Arbitrary))]
57#[repr(C)]
58#[derive(Clone, Debug, PartialEq)]
59pub struct WithdrawAllTokenTypes {
60    /// Amount of pool tokens to burn. User receives an output of token a
61    /// and b based on the percentage of the pool tokens that are returned.
62    pub pool_token_amount: u64,
63    /// Minimum amount of token A to receive, prevents excessive slippage
64    pub minimum_token_a_amount: u64,
65    /// Minimum amount of token B to receive, prevents excessive slippage
66    pub minimum_token_b_amount: u64,
67}
68
69/// Deposit one token type, exact amount in instruction data
70#[cfg_attr(feature = "fuzz", derive(Arbitrary))]
71#[repr(C)]
72#[derive(Clone, Debug, PartialEq)]
73pub struct DepositSingleTokenTypeExactAmountIn {
74    /// Token amount to deposit
75    pub source_token_amount: u64,
76    /// Pool token amount to receive in exchange. The amount is set by
77    /// the current exchange rate and size of the pool
78    pub minimum_pool_token_amount: u64,
79}
80
81/// WithdrawSingleTokenTypeExactAmountOut instruction data
82#[cfg_attr(feature = "fuzz", derive(Arbitrary))]
83#[repr(C)]
84#[derive(Clone, Debug, PartialEq)]
85pub struct WithdrawSingleTokenTypeExactAmountOut {
86    /// Amount of token A or B to receive
87    pub destination_token_amount: u64,
88    /// Maximum amount of pool tokens to burn. User receives an output of token A
89    /// or B based on the percentage of the pool tokens that are returned.
90    pub maximum_pool_token_amount: u64,
91}
92
93/// Instructions supported by the token swap program.
94#[repr(C)]
95#[derive(Debug, PartialEq)]
96pub enum SwapInstruction {
97    ///   Initializes a new swap
98    ///
99    ///   0. `[writable, signer]` New Token-swap to create.
100    ///   1. `[]` swap authority derived from `create_program_address(&[Token-swap account])`
101    ///   2. `[]` token_a Account. Must be non zero, owned by swap authority.
102    ///   3. `[]` token_b Account. Must be non zero, owned by swap authority.
103    ///   4. `[writable]` Pool Token Mint. Must be empty, owned by swap authority.
104    ///   5. `[]` Pool Token Account to deposit trading and withdraw fees.
105    ///   Must be empty, not owned by swap authority
106    ///   6. `[writable]` Pool Token Account to deposit the initial pool token
107    ///   supply.  Must be empty, not owned by swap authority.
108    ///   7. `[]` Token program id
109    Initialize(Initialize),
110
111    ///   Swap the tokens in the pool.
112    ///
113    ///   0. `[]` Token-swap
114    ///   1. `[]` swap authority
115    ///   2. `[]` user transfer authority
116    ///   3. `[writable]` token_(A|B) SOURCE Account, amount is transferable by user transfer authority,
117    ///   4. `[writable]` token_(A|B) Base Account to swap INTO.  Must be the SOURCE token.
118    ///   5. `[writable]` token_(A|B) Base Account to swap FROM.  Must be the DESTINATION token.
119    ///   6. `[writable]` token_(A|B) DESTINATION Account assigned to USER as the owner.
120    ///   7. `[writable]` Pool token mint, to generate trading fees
121    ///   8. `[writable]` Fee account, to receive trading fees
122    ///   9. `[]` Token program id
123    ///   10. `[optional, writable]` Host fee account to receive additional trading fees
124    Swap(Swap),
125
126    ///   Deposit both types of tokens into the pool.  The output is a "pool"
127    ///   token representing ownership in the pool. Inputs are converted to
128    ///   the current ratio.
129    ///
130    ///   0. `[]` Token-swap
131    ///   1. `[]` swap authority
132    ///   2. `[]` user transfer authority
133    ///   3. `[writable]` token_a user transfer authority can transfer amount,
134    ///   4. `[writable]` token_b user transfer authority can transfer amount,
135    ///   5. `[writable]` token_a Base Account to deposit into.
136    ///   6. `[writable]` token_b Base Account to deposit into.
137    ///   7. `[writable]` Pool MINT account, swap authority is the owner.
138    ///   8. `[writable]` Pool Account to deposit the generated tokens, user is the owner.
139    ///   9. `[]` Token program id
140    DepositAllTokenTypes(DepositAllTokenTypes),
141
142    ///   Withdraw both types of tokens from the pool at the current ratio, given
143    ///   pool tokens.  The pool tokens are burned in exchange for an equivalent
144    ///   amount of token A and B.
145    ///
146    ///   0. `[]` Token-swap
147    ///   1. `[]` swap authority
148    ///   2. `[]` user transfer authority
149    ///   3. `[writable]` Pool mint account, swap authority is the owner
150    ///   4. `[writable]` SOURCE Pool account, amount is transferable by user transfer authority.
151    ///   5. `[writable]` token_a Swap Account to withdraw FROM.
152    ///   6. `[writable]` token_b Swap Account to withdraw FROM.
153    ///   7. `[writable]` token_a user Account to credit.
154    ///   8. `[writable]` token_b user Account to credit.
155    ///   9. `[writable]` Fee account, to receive withdrawal fees
156    ///   10. `[]` Token program id
157    WithdrawAllTokenTypes(WithdrawAllTokenTypes),
158
159    ///   Deposit one type of tokens into the pool.  The output is a "pool" token
160    ///   representing ownership into the pool. Input token is converted as if
161    ///   a swap and deposit all token types were performed.
162    ///
163    ///   0. `[]` Token-swap
164    ///   1. `[]` swap authority
165    ///   2. `[]` user transfer authority
166    ///   3. `[writable]` token_(A|B) SOURCE Account, amount is transferable by user transfer authority,
167    ///   4. `[writable]` token_a Swap Account, may deposit INTO.
168    ///   5. `[writable]` token_b Swap Account, may deposit INTO.
169    ///   6. `[writable]` Pool MINT account, swap authority is the owner.
170    ///   7. `[writable]` Pool Account to deposit the generated tokens, user is the owner.
171    ///   8. `[]` Token program id
172    DepositSingleTokenTypeExactAmountIn(DepositSingleTokenTypeExactAmountIn),
173
174    ///   Withdraw one token type from the pool at the current ratio given the
175    ///   exact amount out expected.
176    ///
177    ///   0. `[]` Token-swap
178    ///   1. `[]` swap authority
179    ///   2. `[]` user transfer authority
180    ///   3. `[writable]` Pool mint account, swap authority is the owner
181    ///   4. `[writable]` SOURCE Pool account, amount is transferable by user transfer authority.
182    ///   5. `[writable]` token_a Swap Account to potentially withdraw from.
183    ///   6. `[writable]` token_b Swap Account to potentially withdraw from.
184    ///   7. `[writable]` token_(A|B) User Account to credit
185    ///   8. `[writable]` Fee account, to receive withdrawal fees
186    ///   9. `[]` Token program id
187    WithdrawSingleTokenTypeExactAmountOut(WithdrawSingleTokenTypeExactAmountOut),
188}
189
190impl SwapInstruction {
191    /// Unpacks a byte buffer into a [SwapInstruction](enum.SwapInstruction.html).
192    pub fn unpack(input: &[u8]) -> Result<Self, ProgramError> {
193        let (&tag, rest) = input.split_first().ok_or(SwapError::InvalidInstruction)?;
194        Ok(match tag {
195            0 => {
196                if rest.len() >= Fees::LEN {
197                    let (fees, rest) = rest.split_at(Fees::LEN);
198                    let fees = Fees::unpack_unchecked(fees)?;
199                    let swap_curve = SwapCurve::unpack_unchecked(rest)?;
200                    Self::Initialize(Initialize { fees, swap_curve })
201                } else {
202                    return Err(SwapError::InvalidInstruction.into());
203                }
204            }
205            1 => {
206                let (amount_in, rest) = Self::unpack_u64(rest)?;
207                let (minimum_amount_out, _rest) = Self::unpack_u64(rest)?;
208                Self::Swap(Swap {
209                    amount_in,
210                    minimum_amount_out,
211                })
212            }
213            2 => {
214                let (pool_token_amount, rest) = Self::unpack_u64(rest)?;
215                let (maximum_token_a_amount, rest) = Self::unpack_u64(rest)?;
216                let (maximum_token_b_amount, _rest) = Self::unpack_u64(rest)?;
217                Self::DepositAllTokenTypes(DepositAllTokenTypes {
218                    pool_token_amount,
219                    maximum_token_a_amount,
220                    maximum_token_b_amount,
221                })
222            }
223            3 => {
224                let (pool_token_amount, rest) = Self::unpack_u64(rest)?;
225                let (minimum_token_a_amount, rest) = Self::unpack_u64(rest)?;
226                let (minimum_token_b_amount, _rest) = Self::unpack_u64(rest)?;
227                Self::WithdrawAllTokenTypes(WithdrawAllTokenTypes {
228                    pool_token_amount,
229                    minimum_token_a_amount,
230                    minimum_token_b_amount,
231                })
232            }
233            4 => {
234                let (source_token_amount, rest) = Self::unpack_u64(rest)?;
235                let (minimum_pool_token_amount, _rest) = Self::unpack_u64(rest)?;
236                Self::DepositSingleTokenTypeExactAmountIn(DepositSingleTokenTypeExactAmountIn {
237                    source_token_amount,
238                    minimum_pool_token_amount,
239                })
240            }
241            5 => {
242                let (destination_token_amount, rest) = Self::unpack_u64(rest)?;
243                let (maximum_pool_token_amount, _rest) = Self::unpack_u64(rest)?;
244                Self::WithdrawSingleTokenTypeExactAmountOut(WithdrawSingleTokenTypeExactAmountOut {
245                    destination_token_amount,
246                    maximum_pool_token_amount,
247                })
248            }
249            _ => return Err(SwapError::InvalidInstruction.into()),
250        })
251    }
252
253    fn unpack_u64(input: &[u8]) -> Result<(u64, &[u8]), ProgramError> {
254        if input.len() >= 8 {
255            let (amount, rest) = input.split_at(8);
256            let amount = amount
257                .get(..8)
258                .and_then(|slice| slice.try_into().ok())
259                .map(u64::from_le_bytes)
260                .ok_or(SwapError::InvalidInstruction)?;
261            Ok((amount, rest))
262        } else {
263            Err(SwapError::InvalidInstruction.into())
264        }
265    }
266
267    /// Packs a [SwapInstruction](enum.SwapInstruction.html) into a byte buffer.
268    pub fn pack(&self) -> Vec<u8> {
269        let mut buf = Vec::with_capacity(size_of::<Self>());
270        match &*self {
271            Self::Initialize(Initialize { fees, swap_curve }) => {
272                buf.push(0);
273                let mut fees_slice = [0u8; Fees::LEN];
274                Pack::pack_into_slice(fees, &mut fees_slice[..]);
275                buf.extend_from_slice(&fees_slice);
276                let mut swap_curve_slice = [0u8; SwapCurve::LEN];
277                Pack::pack_into_slice(swap_curve, &mut swap_curve_slice[..]);
278                buf.extend_from_slice(&swap_curve_slice);
279            }
280            Self::Swap(Swap {
281                amount_in,
282                minimum_amount_out,
283            }) => {
284                buf.push(1);
285                buf.extend_from_slice(&amount_in.to_le_bytes());
286                buf.extend_from_slice(&minimum_amount_out.to_le_bytes());
287            }
288            Self::DepositAllTokenTypes(DepositAllTokenTypes {
289                pool_token_amount,
290                maximum_token_a_amount,
291                maximum_token_b_amount,
292            }) => {
293                buf.push(2);
294                buf.extend_from_slice(&pool_token_amount.to_le_bytes());
295                buf.extend_from_slice(&maximum_token_a_amount.to_le_bytes());
296                buf.extend_from_slice(&maximum_token_b_amount.to_le_bytes());
297            }
298            Self::WithdrawAllTokenTypes(WithdrawAllTokenTypes {
299                pool_token_amount,
300                minimum_token_a_amount,
301                minimum_token_b_amount,
302            }) => {
303                buf.push(3);
304                buf.extend_from_slice(&pool_token_amount.to_le_bytes());
305                buf.extend_from_slice(&minimum_token_a_amount.to_le_bytes());
306                buf.extend_from_slice(&minimum_token_b_amount.to_le_bytes());
307            }
308            Self::DepositSingleTokenTypeExactAmountIn(DepositSingleTokenTypeExactAmountIn {
309                source_token_amount,
310                minimum_pool_token_amount,
311            }) => {
312                buf.push(4);
313                buf.extend_from_slice(&source_token_amount.to_le_bytes());
314                buf.extend_from_slice(&minimum_pool_token_amount.to_le_bytes());
315            }
316            Self::WithdrawSingleTokenTypeExactAmountOut(
317                WithdrawSingleTokenTypeExactAmountOut {
318                    destination_token_amount,
319                    maximum_pool_token_amount,
320                },
321            ) => {
322                buf.push(5);
323                buf.extend_from_slice(&destination_token_amount.to_le_bytes());
324                buf.extend_from_slice(&maximum_pool_token_amount.to_le_bytes());
325            }
326        }
327        buf
328    }
329}
330
331/// Creates an 'initialize' instruction.
332pub fn initialize(
333    program_id: &Pubkey,
334    token_program_id: &Pubkey,
335    swap_pubkey: &Pubkey,
336    authority_pubkey: &Pubkey,
337    token_a_pubkey: &Pubkey,
338    token_b_pubkey: &Pubkey,
339    pool_pubkey: &Pubkey,
340    fee_pubkey: &Pubkey,
341    destination_pubkey: &Pubkey,
342    fees: Fees,
343    swap_curve: SwapCurve,
344) -> Result<Instruction, ProgramError> {
345    let init_data = SwapInstruction::Initialize(Initialize { fees, swap_curve });
346    let data = init_data.pack();
347
348    let accounts = vec![
349        AccountMeta::new(*swap_pubkey, true),
350        AccountMeta::new_readonly(*authority_pubkey, false),
351        AccountMeta::new_readonly(*token_a_pubkey, false),
352        AccountMeta::new_readonly(*token_b_pubkey, false),
353        AccountMeta::new(*pool_pubkey, false),
354        AccountMeta::new_readonly(*fee_pubkey, false),
355        AccountMeta::new(*destination_pubkey, false),
356        AccountMeta::new_readonly(*token_program_id, false),
357    ];
358
359    Ok(Instruction {
360        program_id: *program_id,
361        accounts,
362        data,
363    })
364}
365
366/// Creates a 'deposit_all_token_types' instruction.
367pub fn deposit_all_token_types(
368    program_id: &Pubkey,
369    token_program_id: &Pubkey,
370    swap_pubkey: &Pubkey,
371    authority_pubkey: &Pubkey,
372    user_transfer_authority_pubkey: &Pubkey,
373    deposit_token_a_pubkey: &Pubkey,
374    deposit_token_b_pubkey: &Pubkey,
375    swap_token_a_pubkey: &Pubkey,
376    swap_token_b_pubkey: &Pubkey,
377    pool_mint_pubkey: &Pubkey,
378    destination_pubkey: &Pubkey,
379    instruction: DepositAllTokenTypes,
380) -> Result<Instruction, ProgramError> {
381    let data = SwapInstruction::DepositAllTokenTypes(instruction).pack();
382
383    let accounts = vec![
384        AccountMeta::new_readonly(*swap_pubkey, false),
385        AccountMeta::new_readonly(*authority_pubkey, false),
386        AccountMeta::new_readonly(*user_transfer_authority_pubkey, true),
387        AccountMeta::new(*deposit_token_a_pubkey, false),
388        AccountMeta::new(*deposit_token_b_pubkey, false),
389        AccountMeta::new(*swap_token_a_pubkey, false),
390        AccountMeta::new(*swap_token_b_pubkey, false),
391        AccountMeta::new(*pool_mint_pubkey, false),
392        AccountMeta::new(*destination_pubkey, false),
393        AccountMeta::new_readonly(*token_program_id, false),
394    ];
395
396    Ok(Instruction {
397        program_id: *program_id,
398        accounts,
399        data,
400    })
401}
402
403/// Creates a 'withdraw_all_token_types' instruction.
404pub fn withdraw_all_token_types(
405    program_id: &Pubkey,
406    token_program_id: &Pubkey,
407    swap_pubkey: &Pubkey,
408    authority_pubkey: &Pubkey,
409    user_transfer_authority_pubkey: &Pubkey,
410    pool_mint_pubkey: &Pubkey,
411    fee_account_pubkey: &Pubkey,
412    source_pubkey: &Pubkey,
413    swap_token_a_pubkey: &Pubkey,
414    swap_token_b_pubkey: &Pubkey,
415    destination_token_a_pubkey: &Pubkey,
416    destination_token_b_pubkey: &Pubkey,
417    instruction: WithdrawAllTokenTypes,
418) -> Result<Instruction, ProgramError> {
419    let data = SwapInstruction::WithdrawAllTokenTypes(instruction).pack();
420
421    let accounts = vec![
422        AccountMeta::new_readonly(*swap_pubkey, false),
423        AccountMeta::new_readonly(*authority_pubkey, false),
424        AccountMeta::new_readonly(*user_transfer_authority_pubkey, true),
425        AccountMeta::new(*pool_mint_pubkey, false),
426        AccountMeta::new(*source_pubkey, false),
427        AccountMeta::new(*swap_token_a_pubkey, false),
428        AccountMeta::new(*swap_token_b_pubkey, false),
429        AccountMeta::new(*destination_token_a_pubkey, false),
430        AccountMeta::new(*destination_token_b_pubkey, false),
431        AccountMeta::new(*fee_account_pubkey, false),
432        AccountMeta::new_readonly(*token_program_id, false),
433    ];
434
435    Ok(Instruction {
436        program_id: *program_id,
437        accounts,
438        data,
439    })
440}
441
442/// Creates a 'deposit_single_token_type_exact_amount_in' instruction.
443pub fn deposit_single_token_type_exact_amount_in(
444    program_id: &Pubkey,
445    token_program_id: &Pubkey,
446    swap_pubkey: &Pubkey,
447    authority_pubkey: &Pubkey,
448    user_transfer_authority_pubkey: &Pubkey,
449    source_token_pubkey: &Pubkey,
450    swap_token_a_pubkey: &Pubkey,
451    swap_token_b_pubkey: &Pubkey,
452    pool_mint_pubkey: &Pubkey,
453    destination_pubkey: &Pubkey,
454    instruction: DepositSingleTokenTypeExactAmountIn,
455) -> Result<Instruction, ProgramError> {
456    let data = SwapInstruction::DepositSingleTokenTypeExactAmountIn(instruction).pack();
457
458    let accounts = vec![
459        AccountMeta::new_readonly(*swap_pubkey, false),
460        AccountMeta::new_readonly(*authority_pubkey, false),
461        AccountMeta::new_readonly(*user_transfer_authority_pubkey, true),
462        AccountMeta::new(*source_token_pubkey, false),
463        AccountMeta::new(*swap_token_a_pubkey, false),
464        AccountMeta::new(*swap_token_b_pubkey, false),
465        AccountMeta::new(*pool_mint_pubkey, false),
466        AccountMeta::new(*destination_pubkey, false),
467        AccountMeta::new_readonly(*token_program_id, false),
468    ];
469
470    Ok(Instruction {
471        program_id: *program_id,
472        accounts,
473        data,
474    })
475}
476
477/// Creates a 'withdraw_single_token_type_exact_amount_out' instruction.
478pub fn withdraw_single_token_type_exact_amount_out(
479    program_id: &Pubkey,
480    token_program_id: &Pubkey,
481    swap_pubkey: &Pubkey,
482    authority_pubkey: &Pubkey,
483    user_transfer_authority_pubkey: &Pubkey,
484    pool_mint_pubkey: &Pubkey,
485    fee_account_pubkey: &Pubkey,
486    pool_token_source_pubkey: &Pubkey,
487    swap_token_a_pubkey: &Pubkey,
488    swap_token_b_pubkey: &Pubkey,
489    destination_pubkey: &Pubkey,
490    instruction: WithdrawSingleTokenTypeExactAmountOut,
491) -> Result<Instruction, ProgramError> {
492    let data = SwapInstruction::WithdrawSingleTokenTypeExactAmountOut(instruction).pack();
493
494    let accounts = vec![
495        AccountMeta::new_readonly(*swap_pubkey, false),
496        AccountMeta::new_readonly(*authority_pubkey, false),
497        AccountMeta::new_readonly(*user_transfer_authority_pubkey, true),
498        AccountMeta::new(*pool_mint_pubkey, false),
499        AccountMeta::new(*pool_token_source_pubkey, false),
500        AccountMeta::new(*swap_token_a_pubkey, false),
501        AccountMeta::new(*swap_token_b_pubkey, false),
502        AccountMeta::new(*destination_pubkey, false),
503        AccountMeta::new(*fee_account_pubkey, false),
504        AccountMeta::new_readonly(*token_program_id, false),
505    ];
506
507    Ok(Instruction {
508        program_id: *program_id,
509        accounts,
510        data,
511    })
512}
513
514/// Creates a 'swap' instruction.
515pub fn swap(
516    program_id: &Pubkey,
517    token_program_id: &Pubkey,
518    swap_pubkey: &Pubkey,
519    authority_pubkey: &Pubkey,
520    user_transfer_authority_pubkey: &Pubkey,
521    source_pubkey: &Pubkey,
522    swap_source_pubkey: &Pubkey,
523    swap_destination_pubkey: &Pubkey,
524    destination_pubkey: &Pubkey,
525    pool_mint_pubkey: &Pubkey,
526    pool_fee_pubkey: &Pubkey,
527    host_fee_pubkey: Option<&Pubkey>,
528    instruction: Swap,
529) -> Result<Instruction, ProgramError> {
530    let data = SwapInstruction::Swap(instruction).pack();
531
532    let mut accounts = vec![
533        AccountMeta::new_readonly(*swap_pubkey, false),
534        AccountMeta::new_readonly(*authority_pubkey, false),
535        AccountMeta::new_readonly(*user_transfer_authority_pubkey, true),
536        AccountMeta::new(*source_pubkey, false),
537        AccountMeta::new(*swap_source_pubkey, false),
538        AccountMeta::new(*swap_destination_pubkey, false),
539        AccountMeta::new(*destination_pubkey, false),
540        AccountMeta::new(*pool_mint_pubkey, false),
541        AccountMeta::new(*pool_fee_pubkey, false),
542        AccountMeta::new_readonly(*token_program_id, false),
543    ];
544    if let Some(host_fee_pubkey) = host_fee_pubkey {
545        accounts.push(AccountMeta::new(*host_fee_pubkey, false));
546    }
547
548    Ok(Instruction {
549        program_id: *program_id,
550        accounts,
551        data,
552    })
553}
554
555/// Unpacks a reference from a bytes buffer.
556/// TODO actually pack / unpack instead of relying on normal memory layout.
557pub fn unpack<T>(input: &[u8]) -> Result<&T, ProgramError> {
558    if input.len() < size_of::<u8>() + size_of::<T>() {
559        return Err(ProgramError::InvalidAccountData);
560    }
561    #[allow(clippy::cast_ptr_alignment)]
562    let val: &T = unsafe { &*(&input[1] as *const u8 as *const T) };
563    Ok(val)
564}
565
566#[cfg(test)]
567mod tests {
568    use super::*;
569    use crate::curve::{base::CurveType, stable::StableCurve};
570    use std::sync::Arc;
571
572    #[test]
573    fn pack_intialize() {
574        let trade_fee_numerator: u64 = 1;
575        let trade_fee_denominator: u64 = 4;
576        let owner_trade_fee_numerator: u64 = 2;
577        let owner_trade_fee_denominator: u64 = 5;
578        let owner_withdraw_fee_numerator: u64 = 1;
579        let owner_withdraw_fee_denominator: u64 = 3;
580        let host_fee_numerator: u64 = 5;
581        let host_fee_denominator: u64 = 20;
582        let fees = Fees {
583            trade_fee_numerator,
584            trade_fee_denominator,
585            owner_trade_fee_numerator,
586            owner_trade_fee_denominator,
587            owner_withdraw_fee_numerator,
588            owner_withdraw_fee_denominator,
589            host_fee_numerator,
590            host_fee_denominator,
591        };
592        let amp: u64 = 1;
593        let curve_type = CurveType::Stable;
594        let calculator = Arc::new(StableCurve { amp });
595        let swap_curve = SwapCurve {
596            curve_type,
597            calculator,
598        };
599        let check = SwapInstruction::Initialize(Initialize { fees, swap_curve });
600        let packed = check.pack();
601        let mut expect = vec![0u8];
602        expect.extend_from_slice(&trade_fee_numerator.to_le_bytes());
603        expect.extend_from_slice(&trade_fee_denominator.to_le_bytes());
604        expect.extend_from_slice(&owner_trade_fee_numerator.to_le_bytes());
605        expect.extend_from_slice(&owner_trade_fee_denominator.to_le_bytes());
606        expect.extend_from_slice(&owner_withdraw_fee_numerator.to_le_bytes());
607        expect.extend_from_slice(&owner_withdraw_fee_denominator.to_le_bytes());
608        expect.extend_from_slice(&host_fee_numerator.to_le_bytes());
609        expect.extend_from_slice(&host_fee_denominator.to_le_bytes());
610        expect.push(curve_type as u8);
611        expect.extend_from_slice(&amp.to_le_bytes());
612        expect.extend_from_slice(&[0u8; 24]);
613        assert_eq!(packed, expect);
614        let unpacked = SwapInstruction::unpack(&expect).unwrap();
615        assert_eq!(unpacked, check);
616    }
617
618    #[test]
619    fn pack_swap() {
620        let amount_in: u64 = 2;
621        let minimum_amount_out: u64 = 10;
622        let check = SwapInstruction::Swap(Swap {
623            amount_in,
624            minimum_amount_out,
625        });
626        let packed = check.pack();
627        let mut expect = vec![1];
628        expect.extend_from_slice(&amount_in.to_le_bytes());
629        expect.extend_from_slice(&minimum_amount_out.to_le_bytes());
630        assert_eq!(packed, expect);
631        let unpacked = SwapInstruction::unpack(&expect).unwrap();
632        assert_eq!(unpacked, check);
633    }
634
635    #[test]
636    fn pack_deposit() {
637        let pool_token_amount: u64 = 5;
638        let maximum_token_a_amount: u64 = 10;
639        let maximum_token_b_amount: u64 = 20;
640        let check = SwapInstruction::DepositAllTokenTypes(DepositAllTokenTypes {
641            pool_token_amount,
642            maximum_token_a_amount,
643            maximum_token_b_amount,
644        });
645        let packed = check.pack();
646        let mut expect = vec![2];
647        expect.extend_from_slice(&pool_token_amount.to_le_bytes());
648        expect.extend_from_slice(&maximum_token_a_amount.to_le_bytes());
649        expect.extend_from_slice(&maximum_token_b_amount.to_le_bytes());
650        assert_eq!(packed, expect);
651        let unpacked = SwapInstruction::unpack(&expect).unwrap();
652        assert_eq!(unpacked, check);
653    }
654
655    #[test]
656    fn pack_withdraw() {
657        let pool_token_amount: u64 = 1212438012089;
658        let minimum_token_a_amount: u64 = 102198761982612;
659        let minimum_token_b_amount: u64 = 2011239855213;
660        let check = SwapInstruction::WithdrawAllTokenTypes(WithdrawAllTokenTypes {
661            pool_token_amount,
662            minimum_token_a_amount,
663            minimum_token_b_amount,
664        });
665        let packed = check.pack();
666        let mut expect = vec![3];
667        expect.extend_from_slice(&pool_token_amount.to_le_bytes());
668        expect.extend_from_slice(&minimum_token_a_amount.to_le_bytes());
669        expect.extend_from_slice(&minimum_token_b_amount.to_le_bytes());
670        assert_eq!(packed, expect);
671        let unpacked = SwapInstruction::unpack(&expect).unwrap();
672        assert_eq!(unpacked, check);
673    }
674
675    #[test]
676    fn pack_deposit_one_exact_in() {
677        let source_token_amount: u64 = 10;
678        let minimum_pool_token_amount: u64 = 5;
679        let check = SwapInstruction::DepositSingleTokenTypeExactAmountIn(
680            DepositSingleTokenTypeExactAmountIn {
681                source_token_amount,
682                minimum_pool_token_amount,
683            },
684        );
685        let packed = check.pack();
686        let mut expect = vec![4];
687        expect.extend_from_slice(&source_token_amount.to_le_bytes());
688        expect.extend_from_slice(&minimum_pool_token_amount.to_le_bytes());
689        assert_eq!(packed, expect);
690        let unpacked = SwapInstruction::unpack(&expect).unwrap();
691        assert_eq!(unpacked, check);
692    }
693
694    #[test]
695    fn pack_withdraw_one_exact_out() {
696        let destination_token_amount: u64 = 102198761982612;
697        let maximum_pool_token_amount: u64 = 1212438012089;
698        let check = SwapInstruction::WithdrawSingleTokenTypeExactAmountOut(
699            WithdrawSingleTokenTypeExactAmountOut {
700                destination_token_amount,
701                maximum_pool_token_amount,
702            },
703        );
704        let packed = check.pack();
705        let mut expect = vec![5];
706        expect.extend_from_slice(&destination_token_amount.to_le_bytes());
707        expect.extend_from_slice(&maximum_pool_token_amount.to_le_bytes());
708        assert_eq!(packed, expect);
709        let unpacked = SwapInstruction::unpack(&expect).unwrap();
710        assert_eq!(unpacked, check);
711    }
712}