1use crate::fees::Fees;
4use arrayref::{array_mut_ref, array_ref, array_refs, mut_array_refs};
5use solana_program::{
6 program_error::ProgramError,
7 program_pack::{IsInitialized, Pack, Sealed},
8 pubkey::Pubkey,
9};
10
11#[repr(C)]
13#[derive(Clone, Copy, Debug, PartialEq)]
14pub struct SwapInfo {
15 pub is_initialized: bool,
17
18 pub is_paused: bool,
20
21 pub nonce: u8,
27
28 pub initial_amp_factor: u64,
30 pub target_amp_factor: u64,
32 pub start_ramp_ts: i64,
34 pub stop_ramp_ts: i64,
36
37 pub future_admin_deadline: i64,
39 pub future_admin_key: Pubkey,
41 pub admin_key: Pubkey,
43
44 pub token_a: SwapTokenInfo,
46 pub token_b: SwapTokenInfo,
48
49 pub pool_mint: Pubkey,
52 pub fees: Fees,
54}
55
56#[repr(C)]
58#[derive(Clone, Copy, Debug, PartialEq)]
59pub struct SwapTokenInfo {
60 pub reserves: Pubkey,
62 pub mint: Pubkey,
64 pub admin_fees: Pubkey,
66 pub index: u8,
68}
69
70impl Sealed for SwapInfo {}
71impl IsInitialized for SwapInfo {
72 fn is_initialized(&self) -> bool {
73 self.is_initialized
74 }
75}
76
77impl Pack for SwapInfo {
78 const LEN: usize = 395;
79
80 fn unpack_from_slice(input: &[u8]) -> Result<Self, ProgramError> {
82 let input = array_ref![input, 0, 395];
83 #[allow(clippy::ptr_offset_with_cast)]
84 let (
85 is_initialized,
86 is_paused,
87 nonce,
88 initial_amp_factor,
89 target_amp_factor,
90 start_ramp_ts,
91 stop_ramp_ts,
92 future_admin_deadline,
93 future_admin_key,
94 admin_key,
95 token_a,
96 token_b,
97 pool_mint,
98 token_a_mint,
99 token_b_mint,
100 admin_fee_key_a,
101 admin_fee_key_b,
102 fees,
103 ) = array_refs![input, 1, 1, 1, 8, 8, 8, 8, 8, 32, 32, 32, 32, 32, 32, 32, 32, 32, 64];
104 Ok(Self {
105 is_initialized: match is_initialized {
106 [0] => false,
107 [1] => true,
108 _ => return Err(ProgramError::InvalidAccountData),
109 },
110 is_paused: match is_paused {
111 [0] => false,
112 [1] => true,
113 _ => return Err(ProgramError::InvalidAccountData),
114 },
115 nonce: nonce[0],
116 initial_amp_factor: u64::from_le_bytes(*initial_amp_factor),
117 target_amp_factor: u64::from_le_bytes(*target_amp_factor),
118 start_ramp_ts: i64::from_le_bytes(*start_ramp_ts),
119 stop_ramp_ts: i64::from_le_bytes(*stop_ramp_ts),
120 future_admin_deadline: i64::from_le_bytes(*future_admin_deadline),
121 future_admin_key: Pubkey::new_from_array(*future_admin_key),
122 admin_key: Pubkey::new_from_array(*admin_key),
123 token_a: SwapTokenInfo {
124 reserves: Pubkey::new_from_array(*token_a),
125 mint: Pubkey::new_from_array(*token_a_mint),
126 admin_fees: Pubkey::new_from_array(*admin_fee_key_a),
127 index: 0,
128 },
129 token_b: SwapTokenInfo {
130 reserves: Pubkey::new_from_array(*token_b),
131 mint: Pubkey::new_from_array(*token_b_mint),
132 admin_fees: Pubkey::new_from_array(*admin_fee_key_b),
133 index: 1,
134 },
135 pool_mint: Pubkey::new_from_array(*pool_mint),
136 fees: Fees::unpack_from_slice(fees)?,
137 })
138 }
139
140 fn pack_into_slice(&self, output: &mut [u8]) {
141 let output = array_mut_ref![output, 0, 395];
142 let (
143 is_initialized,
144 is_paused,
145 nonce,
146 initial_amp_factor,
147 target_amp_factor,
148 start_ramp_ts,
149 stop_ramp_ts,
150 future_admin_deadline,
151 future_admin_key,
152 admin_key,
153 token_a,
154 token_b,
155 pool_mint,
156 token_a_mint,
157 token_b_mint,
158 admin_fee_key_a,
159 admin_fee_key_b,
160 fees,
161 ) = mut_array_refs![output, 1, 1, 1, 8, 8, 8, 8, 8, 32, 32, 32, 32, 32, 32, 32, 32, 32, 64];
162 is_initialized[0] = self.is_initialized as u8;
163 is_paused[0] = self.is_paused as u8;
164 nonce[0] = self.nonce;
165 *initial_amp_factor = self.initial_amp_factor.to_le_bytes();
166 *target_amp_factor = self.target_amp_factor.to_le_bytes();
167 *start_ramp_ts = self.start_ramp_ts.to_le_bytes();
168 *stop_ramp_ts = self.stop_ramp_ts.to_le_bytes();
169 *future_admin_deadline = self.future_admin_deadline.to_le_bytes();
170 future_admin_key.copy_from_slice(self.future_admin_key.as_ref());
171 admin_key.copy_from_slice(self.admin_key.as_ref());
172 token_a.copy_from_slice(self.token_a.reserves.as_ref());
173 token_b.copy_from_slice(self.token_b.reserves.as_ref());
174 pool_mint.copy_from_slice(self.pool_mint.as_ref());
175 token_a_mint.copy_from_slice(self.token_a.mint.as_ref());
176 token_b_mint.copy_from_slice(self.token_b.mint.as_ref());
177 admin_fee_key_a.copy_from_slice(self.token_a.admin_fees.as_ref());
178 admin_fee_key_b.copy_from_slice(self.token_b.admin_fees.as_ref());
179 self.fees.pack_into_slice(&mut fees[..]);
180 }
181}
182
183#[cfg(test)]
184#[allow(clippy::unwrap_used)]
185mod tests {
186 use super::*;
187
188 #[test]
189 fn test_swap_info_packing() {
190 let nonce = 255;
191 let initial_amp_factor: u64 = 1;
192 let target_amp_factor: u64 = 1;
193 let start_ramp_ts: i64 = i64::MAX;
194 let stop_ramp_ts: i64 = i64::MAX;
195 let future_admin_deadline: i64 = i64::MAX;
196 let future_admin_key_raw = [1u8; 32];
197 let admin_key_raw = [2u8; 32];
198 let token_a_raw = [3u8; 32];
199 let token_b_raw = [4u8; 32];
200 let pool_mint_raw = [5u8; 32];
201 let token_a_mint_raw = [6u8; 32];
202 let token_b_mint_raw = [7u8; 32];
203 let admin_fee_key_a_raw = [8u8; 32];
204 let admin_fee_key_b_raw = [9u8; 32];
205 let admin_key = Pubkey::new_from_array(admin_key_raw);
206 let future_admin_key = Pubkey::new_from_array(future_admin_key_raw);
207 let token_a = Pubkey::new_from_array(token_a_raw);
208 let token_b = Pubkey::new_from_array(token_b_raw);
209 let pool_mint = Pubkey::new_from_array(pool_mint_raw);
210 let token_a_mint = Pubkey::new_from_array(token_a_mint_raw);
211 let token_b_mint = Pubkey::new_from_array(token_b_mint_raw);
212 let admin_fee_key_a = Pubkey::new_from_array(admin_fee_key_a_raw);
213 let admin_fee_key_b = Pubkey::new_from_array(admin_fee_key_b_raw);
214 let admin_trade_fee_numerator = 1;
215 let admin_trade_fee_denominator = 2;
216 let admin_withdraw_fee_numerator = 3;
217 let admin_withdraw_fee_denominator = 4;
218 let trade_fee_numerator = 5;
219 let trade_fee_denominator = 6;
220 let withdraw_fee_numerator = 7;
221 let withdraw_fee_denominator = 8;
222 let fees = Fees {
223 admin_trade_fee_numerator,
224 admin_trade_fee_denominator,
225 admin_withdraw_fee_numerator,
226 admin_withdraw_fee_denominator,
227 trade_fee_numerator,
228 trade_fee_denominator,
229 withdraw_fee_numerator,
230 withdraw_fee_denominator,
231 };
232
233 let is_initialized = true;
234 let is_paused = false;
235 let swap_info = SwapInfo {
236 is_initialized,
237 is_paused,
238 nonce,
239 initial_amp_factor,
240 target_amp_factor,
241 start_ramp_ts,
242 stop_ramp_ts,
243 future_admin_deadline,
244 future_admin_key,
245 admin_key,
246 token_a: SwapTokenInfo {
247 reserves: token_a,
248 mint: token_a_mint,
249 admin_fees: admin_fee_key_a,
250 index: 0,
251 },
252 token_b: SwapTokenInfo {
253 reserves: token_b,
254 mint: token_b_mint,
255 admin_fees: admin_fee_key_b,
256 index: 1,
257 },
258 pool_mint,
259 fees,
260 };
261
262 let mut packed = [0u8; SwapInfo::LEN];
263 SwapInfo::pack(swap_info, &mut packed).unwrap();
264 let unpacked = SwapInfo::unpack(&packed).unwrap();
265 assert_eq!(swap_info, unpacked);
266
267 let mut packed = vec![
268 1_u8, 0_u8, nonce,
271 ];
272 packed.extend_from_slice(&initial_amp_factor.to_le_bytes());
273 packed.extend_from_slice(&target_amp_factor.to_le_bytes());
274 packed.extend_from_slice(&start_ramp_ts.to_le_bytes());
275 packed.extend_from_slice(&stop_ramp_ts.to_le_bytes());
276 packed.extend_from_slice(&future_admin_deadline.to_le_bytes());
277 packed.extend_from_slice(&future_admin_key_raw);
278 packed.extend_from_slice(&admin_key_raw);
279 packed.extend_from_slice(&token_a_raw);
280 packed.extend_from_slice(&token_b_raw);
281 packed.extend_from_slice(&pool_mint_raw);
282 packed.extend_from_slice(&token_a_mint_raw);
283 packed.extend_from_slice(&token_b_mint_raw);
284 packed.extend_from_slice(&admin_fee_key_a_raw);
285 packed.extend_from_slice(&admin_fee_key_b_raw);
286 packed.extend_from_slice(&admin_trade_fee_numerator.to_le_bytes());
287 packed.extend_from_slice(&admin_trade_fee_denominator.to_le_bytes());
288 packed.extend_from_slice(&admin_withdraw_fee_numerator.to_le_bytes());
289 packed.extend_from_slice(&admin_withdraw_fee_denominator.to_le_bytes());
290 packed.extend_from_slice(&trade_fee_numerator.to_le_bytes());
291 packed.extend_from_slice(&trade_fee_denominator.to_le_bytes());
292 packed.extend_from_slice(&withdraw_fee_numerator.to_le_bytes());
293 packed.extend_from_slice(&withdraw_fee_denominator.to_le_bytes());
294 let unpacked = SwapInfo::unpack(&packed).unwrap();
295 assert_eq!(swap_info, unpacked);
296 }
297}