anchor_i11n/
lib.rs

1pub use anchor_i11n_derive::{AnchorDiscriminator, TryFromAccountMetas, TryFromInstruction};
2pub use anchor_lang::Discriminator;
3
4pub mod prelude {
5    pub use super::{AnchorDiscriminator, Discriminator, TryFromAccountMetas, TryFromInstruction};
6}
7
8#[cfg(test)]
9mod tests {
10    use crate::prelude::*;
11    use anchor_lang::{solana_program::pubkey, AnchorSerialize};
12    use hex_literal::hex;
13    use solana_program::{
14        instruction::{AccountMeta, Instruction},
15        pubkey::Pubkey,
16    };
17
18    use crate::tests::jupiter::SharedAccountsRoute;
19
20    // This is a mini version of the Jupiter SDK that we generated for unit tests
21    mod jupiter {
22        use crate::prelude::*;
23        use anchor_lang::prelude::*;
24
25        declare_id!("JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4");
26
27        // Route
28        #[derive(TryFromInstruction)]
29        pub struct SharedAccountsRoute<'info> {
30            pub accounts: SharedAccountsRouteAccountMetas<'info>,
31            pub args: instructions::SharedAccountsRoute,
32            pub remaining_accounts: Vec<&'info AccountMeta>,
33        }
34
35        // Route
36        #[derive(TryFromInstruction)]
37        pub struct FakeRoute<'info> {
38            pub accounts: FakeRouteAccountMetas,
39            pub args: instructions::SharedAccountsRoute,
40            pub remaining_accounts: Vec<&'info AccountMeta>,
41        }
42
43        pub mod instructions {
44            use super::*;
45            #[derive(AnchorDiscriminator, AnchorDeserialize, AnchorSerialize)]
46            pub struct SharedAccountsRoute {
47                pub id: u8,
48                pub route_plan: Vec<RoutePlanStep>,
49                pub in_amount: u64,
50                pub quoted_out_amount: u64,
51                pub slippage_bps: u16,
52                pub platform_fee_bps: u8,
53            }
54        }
55
56        #[derive(TryFromAccountMetas)]
57        pub struct SharedAccountsRouteAccountMetas<'info> {
58            pub token_program: &'info AccountMeta,
59            pub program_authority: &'info AccountMeta,
60            pub user_transfer_authority: &'info AccountMeta,
61            pub source_token_account: &'info AccountMeta,
62            pub program_source_token_account: &'info AccountMeta,
63            pub program_destination_token_account: &'info AccountMeta,
64            pub destination_token_account: &'info AccountMeta,
65            pub source_mint: &'info AccountMeta,
66            pub destination_mint: &'info AccountMeta,
67            pub platform_fee_account: Option<&'info AccountMeta>,
68            pub token_2022_program: Option<&'info AccountMeta>,
69            pub event_authority: &'info AccountMeta,
70            pub program: &'info AccountMeta,
71        }
72        
73        #[derive(TryFromAccountMetas)]
74        pub struct FakeRouteAccountMetas {
75        }
76
77        // Common
78        #[derive(AnchorSerialize, AnchorDeserialize)]
79        pub struct RoutePlanStep {
80            pub swap: Swap,
81            pub percent: u8,
82            pub input_index: u8,
83            pub output_index: u8,
84        }
85
86        #[derive(AnchorSerialize, AnchorDeserialize)]
87        pub enum Side {
88            Bid,
89            Ask,
90        }
91
92        #[derive(AnchorSerialize, AnchorDeserialize)]
93        pub enum Swap {
94            Saber,
95            SaberAddDecimalsDeposit,
96            SaberAddDecimalsWithdraw,
97            TokenSwap,
98            Sencha,
99            Step,
100            Cropper,
101            Raydium,
102            Crema {
103                x_to_y: bool,
104            },
105            Lifinity,
106            Mercurial,
107            Cykura,
108            Serum {
109                side: Side,
110            },
111            MarinadeDeposit,
112            MarinadeUnstake,
113            Aldrin {
114                side: Side,
115            },
116            AldrinV2 {
117                side: Side,
118            },
119            Whirlpool {
120                a_to_b: bool,
121            },
122            Invariant {
123                x_to_y: bool,
124            },
125            Meteora,
126            GooseFX,
127            DeltaFi {
128                stable: bool,
129            },
130            Balansol,
131            MarcoPolo {
132                x_to_y: bool,
133            },
134            Dradex {
135                side: Side,
136            },
137            LifinityV2,
138            RaydiumClmm,
139            Openbook {
140                side: Side,
141            },
142            Phoenix {
143                side: Side,
144            },
145            Symmetry {
146                from_token_id: u64,
147                to_token_id: u64,
148            },
149            TokenSwapV2,
150            HeliumTreasuryManagementRedeemV0,
151            StakeDexStakeWrappedSol,
152            StakeDexSwapViaStake {
153                bridge_stake_seed: u32,
154            },
155            GooseFXV2,
156            Perps,
157            PerpsAddLiquidity,
158            PerpsRemoveLiquidity,
159            MeteoraDlmm,
160            OpenBookV2 {
161                side: Side,
162            },
163            RaydiumClmmV2,
164        }
165    }
166
167    #[test]
168    fn test_derive() {
169        // There are 13 accounts in a Jupiter SharedAccountsRoute IX, plus a bunch of remaining accounts
170        let mut accounts = vec![
171            AccountMeta::new_readonly(
172                pubkey!("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"),
173                false,
174            ),
175            AccountMeta::new(
176                pubkey!("7iWnBRRhBCiNXXPhqiGzvvBkKrvFSWqqmxRyu9VyYBxE"),
177                false,
178            ),
179            AccountMeta::new(Pubkey::new_unique(), true),
180            AccountMeta::new(Pubkey::new_unique(), false),
181            AccountMeta::new(
182                pubkey!("8VAK5Zk2q9F4W9Kxj9K8buaHTvTduPY2KEUPxzD1oJsf"),
183                false,
184            ),
185            AccountMeta::new(
186                pubkey!("2oL6my4QDDCfpgJZX1bZV1NgbmuNptKdgcE8wJm6efgk"),
187                false,
188            ),
189            AccountMeta::new(Pubkey::new_unique(), false),
190            AccountMeta::new_readonly(
191                pubkey!("DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263"),
192                false,
193            ),
194            AccountMeta::new_readonly(
195                pubkey!("So11111111111111111111111111111111111111112"),
196                false,
197            ),
198            AccountMeta::new_readonly(
199                pubkey!("JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4"),
200                false,
201            ),
202            AccountMeta::new_readonly(
203                pubkey!("JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4"),
204                false,
205            ),
206            AccountMeta::new_readonly(
207                pubkey!("D8cy77BBepLMngZx6ZukaTff5hCt1HrWyKk3Hnd9oitf"),
208                false,
209            ),
210            AccountMeta::new_readonly(
211                pubkey!("JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4"),
212                false,
213            ),
214        ];
215
216        // Make some random remaining accounts to make it more realistic
217        for i in 0..40 {
218            match i % 3 == 0 {
219                true => accounts.push(AccountMeta::new_readonly(Pubkey::new_unique(), false)),
220                false => accounts.push(AccountMeta::new(Pubkey::new_unique(), false)),
221            }
222        }
223
224        let ix = Instruction::new_with_bytes(
225            jupiter::ID,
226            &hex!("c1209b3341d69c810f04000000261e000317013c000417010a00031c00640304002027b8ba04000034f881a201000000320000"), 
227            accounts
228        );
229
230        let ctx = SharedAccountsRoute::try_from(&ix).unwrap();
231
232        assert_eq!(
233            ctx.accounts.source_mint.pubkey,
234            pubkey!("DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263")
235        );
236        assert_eq!(ctx.args.try_to_vec().unwrap(), &hex!("0f04000000261e000317013c000417010a00031c00640304002027b8ba04000034f881a201000000320000"));
237        assert_eq!(ctx.remaining_accounts.len(), 40);
238    }
239
240    #[test]
241    fn test_discriminator() {
242        #[derive(AnchorDiscriminator)]
243        struct IX {}
244        assert_eq!(IX::DISCRIMINATOR, hex!("1c693616723a62b5"))
245    }
246}