use crate::fees::Fees;
use arrayref::{array_mut_ref, array_ref, array_refs, mut_array_refs};
use solana_program::{
program_error::ProgramError,
program_pack::{IsInitialized, Pack, Sealed},
pubkey::Pubkey,
};
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct SwapInfo {
pub is_initialized: bool,
pub is_paused: bool,
pub nonce: u8,
pub initial_amp_factor: u64,
pub target_amp_factor: u64,
pub start_ramp_ts: i64,
pub stop_ramp_ts: i64,
pub future_admin_deadline: i64,
pub future_admin_key: Pubkey,
pub admin_key: Pubkey,
pub token_a: SwapTokenInfo,
pub token_b: SwapTokenInfo,
pub pool_mint: Pubkey,
pub fees: Fees,
}
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct SwapTokenInfo {
pub reserves: Pubkey,
pub mint: Pubkey,
pub admin_fees: Pubkey,
pub index: u8,
}
impl Sealed for SwapInfo {}
impl IsInitialized for SwapInfo {
fn is_initialized(&self) -> bool {
self.is_initialized
}
}
impl Pack for SwapInfo {
const LEN: usize = 395;
fn unpack_from_slice(input: &[u8]) -> Result<Self, ProgramError> {
let input = array_ref![input, 0, 395];
#[allow(clippy::ptr_offset_with_cast)]
let (
is_initialized,
is_paused,
nonce,
initial_amp_factor,
target_amp_factor,
start_ramp_ts,
stop_ramp_ts,
future_admin_deadline,
future_admin_key,
admin_key,
token_a,
token_b,
pool_mint,
token_a_mint,
token_b_mint,
admin_fee_key_a,
admin_fee_key_b,
fees,
) = array_refs![input, 1, 1, 1, 8, 8, 8, 8, 8, 32, 32, 32, 32, 32, 32, 32, 32, 32, 64];
Ok(Self {
is_initialized: match is_initialized {
[0] => false,
[1] => true,
_ => return Err(ProgramError::InvalidAccountData),
},
is_paused: match is_paused {
[0] => false,
[1] => true,
_ => return Err(ProgramError::InvalidAccountData),
},
nonce: nonce[0],
initial_amp_factor: u64::from_le_bytes(*initial_amp_factor),
target_amp_factor: u64::from_le_bytes(*target_amp_factor),
start_ramp_ts: i64::from_le_bytes(*start_ramp_ts),
stop_ramp_ts: i64::from_le_bytes(*stop_ramp_ts),
future_admin_deadline: i64::from_le_bytes(*future_admin_deadline),
future_admin_key: Pubkey::new_from_array(*future_admin_key),
admin_key: Pubkey::new_from_array(*admin_key),
token_a: SwapTokenInfo {
reserves: Pubkey::new_from_array(*token_a),
mint: Pubkey::new_from_array(*token_a_mint),
admin_fees: Pubkey::new_from_array(*admin_fee_key_a),
index: 0,
},
token_b: SwapTokenInfo {
reserves: Pubkey::new_from_array(*token_b),
mint: Pubkey::new_from_array(*token_b_mint),
admin_fees: Pubkey::new_from_array(*admin_fee_key_b),
index: 1,
},
pool_mint: Pubkey::new_from_array(*pool_mint),
fees: Fees::unpack_from_slice(fees)?,
})
}
fn pack_into_slice(&self, output: &mut [u8]) {
let output = array_mut_ref![output, 0, 395];
let (
is_initialized,
is_paused,
nonce,
initial_amp_factor,
target_amp_factor,
start_ramp_ts,
stop_ramp_ts,
future_admin_deadline,
future_admin_key,
admin_key,
token_a,
token_b,
pool_mint,
token_a_mint,
token_b_mint,
admin_fee_key_a,
admin_fee_key_b,
fees,
) = mut_array_refs![output, 1, 1, 1, 8, 8, 8, 8, 8, 32, 32, 32, 32, 32, 32, 32, 32, 32, 64];
is_initialized[0] = self.is_initialized as u8;
is_paused[0] = self.is_paused as u8;
nonce[0] = self.nonce;
*initial_amp_factor = self.initial_amp_factor.to_le_bytes();
*target_amp_factor = self.target_amp_factor.to_le_bytes();
*start_ramp_ts = self.start_ramp_ts.to_le_bytes();
*stop_ramp_ts = self.stop_ramp_ts.to_le_bytes();
*future_admin_deadline = self.future_admin_deadline.to_le_bytes();
future_admin_key.copy_from_slice(self.future_admin_key.as_ref());
admin_key.copy_from_slice(self.admin_key.as_ref());
token_a.copy_from_slice(self.token_a.reserves.as_ref());
token_b.copy_from_slice(self.token_b.reserves.as_ref());
pool_mint.copy_from_slice(self.pool_mint.as_ref());
token_a_mint.copy_from_slice(self.token_a.mint.as_ref());
token_b_mint.copy_from_slice(self.token_b.mint.as_ref());
admin_fee_key_a.copy_from_slice(self.token_a.admin_fees.as_ref());
admin_fee_key_b.copy_from_slice(self.token_b.admin_fees.as_ref());
self.fees.pack_into_slice(&mut fees[..]);
}
}
#[cfg(test)]
#[allow(clippy::unwrap_used)]
mod tests {
use super::*;
#[test]
fn test_swap_info_packing() {
let nonce = 255;
let initial_amp_factor: u64 = 1;
let target_amp_factor: u64 = 1;
let start_ramp_ts: i64 = i64::MAX;
let stop_ramp_ts: i64 = i64::MAX;
let future_admin_deadline: i64 = i64::MAX;
let future_admin_key_raw = [1u8; 32];
let admin_key_raw = [2u8; 32];
let token_a_raw = [3u8; 32];
let token_b_raw = [4u8; 32];
let pool_mint_raw = [5u8; 32];
let token_a_mint_raw = [6u8; 32];
let token_b_mint_raw = [7u8; 32];
let admin_fee_key_a_raw = [8u8; 32];
let admin_fee_key_b_raw = [9u8; 32];
let admin_key = Pubkey::new_from_array(admin_key_raw);
let future_admin_key = Pubkey::new_from_array(future_admin_key_raw);
let token_a = Pubkey::new_from_array(token_a_raw);
let token_b = Pubkey::new_from_array(token_b_raw);
let pool_mint = Pubkey::new_from_array(pool_mint_raw);
let token_a_mint = Pubkey::new_from_array(token_a_mint_raw);
let token_b_mint = Pubkey::new_from_array(token_b_mint_raw);
let admin_fee_key_a = Pubkey::new_from_array(admin_fee_key_a_raw);
let admin_fee_key_b = Pubkey::new_from_array(admin_fee_key_b_raw);
let admin_trade_fee_numerator = 1;
let admin_trade_fee_denominator = 2;
let admin_withdraw_fee_numerator = 3;
let admin_withdraw_fee_denominator = 4;
let trade_fee_numerator = 5;
let trade_fee_denominator = 6;
let withdraw_fee_numerator = 7;
let withdraw_fee_denominator = 8;
let fees = Fees {
admin_trade_fee_numerator,
admin_trade_fee_denominator,
admin_withdraw_fee_numerator,
admin_withdraw_fee_denominator,
trade_fee_numerator,
trade_fee_denominator,
withdraw_fee_numerator,
withdraw_fee_denominator,
};
let is_initialized = true;
let is_paused = false;
let swap_info = SwapInfo {
is_initialized,
is_paused,
nonce,
initial_amp_factor,
target_amp_factor,
start_ramp_ts,
stop_ramp_ts,
future_admin_deadline,
future_admin_key,
admin_key,
token_a: SwapTokenInfo {
reserves: token_a,
mint: token_a_mint,
admin_fees: admin_fee_key_a,
index: 0,
},
token_b: SwapTokenInfo {
reserves: token_b,
mint: token_b_mint,
admin_fees: admin_fee_key_b,
index: 1,
},
pool_mint,
fees,
};
let mut packed = [0u8; SwapInfo::LEN];
SwapInfo::pack(swap_info, &mut packed).unwrap();
let unpacked = SwapInfo::unpack(&packed).unwrap();
assert_eq!(swap_info, unpacked);
let mut packed = vec![
1_u8, 0_u8, nonce,
];
packed.extend_from_slice(&initial_amp_factor.to_le_bytes());
packed.extend_from_slice(&target_amp_factor.to_le_bytes());
packed.extend_from_slice(&start_ramp_ts.to_le_bytes());
packed.extend_from_slice(&stop_ramp_ts.to_le_bytes());
packed.extend_from_slice(&future_admin_deadline.to_le_bytes());
packed.extend_from_slice(&future_admin_key_raw);
packed.extend_from_slice(&admin_key_raw);
packed.extend_from_slice(&token_a_raw);
packed.extend_from_slice(&token_b_raw);
packed.extend_from_slice(&pool_mint_raw);
packed.extend_from_slice(&token_a_mint_raw);
packed.extend_from_slice(&token_b_mint_raw);
packed.extend_from_slice(&admin_fee_key_a_raw);
packed.extend_from_slice(&admin_fee_key_b_raw);
packed.extend_from_slice(&admin_trade_fee_numerator.to_le_bytes());
packed.extend_from_slice(&admin_trade_fee_denominator.to_le_bytes());
packed.extend_from_slice(&admin_withdraw_fee_numerator.to_le_bytes());
packed.extend_from_slice(&admin_withdraw_fee_denominator.to_le_bytes());
packed.extend_from_slice(&trade_fee_numerator.to_le_bytes());
packed.extend_from_slice(&trade_fee_denominator.to_le_bytes());
packed.extend_from_slice(&withdraw_fee_numerator.to_le_bytes());
packed.extend_from_slice(&withdraw_fee_denominator.to_le_bytes());
let unpacked = SwapInfo::unpack(&packed).unwrap();
assert_eq!(swap_info, unpacked);
}
}