use solana_sdk::{
instruction::{AccountMeta, Instruction},
pubkey::Pubkey,
sysvar,
};
use spl_associated_token_account::get_associated_token_address_with_program_id;
use crate::constants::Discriminator;
use crate::helpers::derive::{get_fee_config_address, get_spread_config_address};
use crate::types::{PoolAddresses, SwapParams};
pub fn build_swap_exact_in(
user: &Pubkey,
pool_addresses: &PoolAddresses,
mint_x: &Pubkey,
mint_y: &Pubkey,
token_program_x: &Pubkey,
token_program_y: &Pubkey,
params: &SwapParams,
program_id: &Pubkey,
spread_config_initialized: bool,
fee_config_override: Option<Pubkey>,
) -> Instruction {
let user_x = get_associated_token_address_with_program_id(user, mint_x, token_program_x);
let user_y = get_associated_token_address_with_program_id(user, mint_y, token_program_y);
let (user_source, vault_source, vault_dest, user_dest) = if params.is_x {
(user_x, pool_addresses.vault_x, pool_addresses.vault_y, user_y)
} else {
(user_y, pool_addresses.vault_y, pool_addresses.vault_x, user_x)
};
let fee_config_pda = fee_config_override.unwrap_or_else(|| get_fee_config_address(program_id).0);
let input_mint = if params.is_x { mint_x } else { mint_y };
let input_mint_program = if params.is_x {
token_program_x
} else {
token_program_y
};
let fee_recipient_ata = get_associated_token_address_with_program_id(
¶ms.fee_recipient,
input_mint,
input_mint_program,
);
let expiration = params.expiration.unwrap_or_else(default_expiration);
let mut data = Vec::with_capacity(26);
data.push(Discriminator::SwapExactIn as u8);
data.push(if params.is_x { 1 } else { 0 });
data.extend_from_slice(¶ms.amount_in.to_le_bytes());
data.extend_from_slice(¶ms.min_out.to_le_bytes());
data.extend_from_slice(&expiration.to_le_bytes());
let mut keys = vec![
AccountMeta::new_readonly(*token_program_x, false),
AccountMeta::new_readonly(*token_program_y, false),
AccountMeta::new_readonly(pool_addresses.config, false),
AccountMeta::new_readonly(pool_addresses.midprice_oracle, false),
AccountMeta::new_readonly(pool_addresses.curve_meta, false),
AccountMeta::new(pool_addresses.curve_prefabs, false),
AccountMeta::new_readonly(pool_addresses.config, false), AccountMeta::new_readonly(*user, true),
AccountMeta::new(user_source, false),
AccountMeta::new(vault_source, false),
AccountMeta::new(vault_dest, false),
AccountMeta::new(user_dest, false),
AccountMeta::new_readonly(fee_config_pda, false),
AccountMeta::new(fee_recipient_ata, false),
AccountMeta::new_readonly(sysvar::clock::id(), false),
AccountMeta::new(pool_addresses.curve_updates, false),
];
if spread_config_initialized {
let (spread_config_pda, _) =
get_spread_config_address(&pool_addresses.config, program_id);
keys.push(AccountMeta::new_readonly(spread_config_pda, false));
keys.push(AccountMeta::new_readonly(sysvar::instructions::id(), false));
}
Instruction {
program_id: *program_id,
accounts: keys,
data,
}
}
fn default_expiration() -> i64 {
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_secs() as i64
+ 3600
}