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 mod jupiter {
22 use crate::prelude::*;
23 use anchor_lang::prelude::*;
24
25 declare_id!("JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4");
26
27 #[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 #[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 #[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 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 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}