sendfun-sdk 3.0.0

Rust SDK for send.fun programs
Documentation
use crate::constants::{
	ATA_PROGRAM_ID, DEX_PROGRAM_ID, LAUNCHPAD_PROGRAM_ID, NEXUS_PROGRAM_ID,
	SYSTEM_PROGRAM_ID, TOKEN_2022_PROGRAM_ID,
};
use solana_address::Address;

#[derive(Clone, Debug)]
pub struct BuildMigrateParams<'a> {
	pub caller: &'a Address,
	pub base_mint: &'a Address,
	pub quote_mint: &'a Address,
	pub quote_token_program: &'a Address,
}

#[must_use]
pub fn build_migrate_instruction(
	p: &BuildMigrateParams<'_>,
) -> solana_instruction::Instruction {
	let base_mint = p.base_mint;
	let quote_mint = p.quote_mint;

	let (bonding_curve, _) =
		super::pda::find_bonding_curve_pda(base_mint, quote_mint);
	let (base_vault, _) = crate::utils::find_associated_token_pda(
		&bonding_curve,
		base_mint,
		&TOKEN_2022_PROGRAM_ID,
	);
	let (quote_vault, _) = crate::utils::find_associated_token_pda(
		&bonding_curve,
		quote_mint,
		p.quote_token_program,
	);
	let (creator_fee_config, _) =
		super::pda::find_creator_fee_config_pda(base_mint, quote_mint);

	let (pool, _) = crate::dex::pda::find_pool_pda(base_mint, quote_mint);
	let (lp_mint, _) = crate::dex::pda::find_lp_mint_pda(base_mint, quote_mint);
	let (pool_base_vault, _) = crate::utils::find_associated_token_pda(
		&pool,
		base_mint,
		&TOKEN_2022_PROGRAM_ID,
	);
	let (pool_quote_vault, _) = crate::utils::find_associated_token_pda(
		&pool,
		quote_mint,
		p.quote_token_program,
	);
	let (pool_lp_account, _) = crate::utils::find_associated_token_pda(
		&pool,
		&lp_mint,
		&TOKEN_2022_PROGRAM_ID,
	);
	let (dex_creator_fee_config, _) =
		crate::dex::pda::find_creator_fee_config_pda(base_mint, quote_mint);

	let migrate = super::instructions::Migrate {
		caller: *p.caller,
		bonding_curve,
		base_mint: *base_mint,
		quote_mint: *quote_mint,
		base_vault,
		quote_vault,
		dex_program: DEX_PROGRAM_ID,
		migration_authority: super::pda::MIGRATION_AUTHORITY_ADDRESS,
		pool,
		lp_mint,
		pool_base_vault,
		pool_quote_vault,
		pool_lp_account,
		dex_event_authority: crate::dex::pda::EVENT_AUTHORITY_ADDRESS,
		nexus_program: NEXUS_PROGRAM_ID,
		creator_fee_config,
		base_token_program: TOKEN_2022_PROGRAM_ID,
		quote_token_program: *p.quote_token_program,
		associated_token_program: ATA_PROGRAM_ID,
		system_program: SYSTEM_PROGRAM_ID,
		event_authority: super::pda::EVENT_AUTHORITY_ADDRESS,
		program: LAUNCHPAD_PROGRAM_ID,
		dex_creator_fee_config,
	};
	migrate.instruction()
}

#[cfg(test)]
mod tests {
	use solana_address::Address;

	use super::{BuildMigrateParams, build_migrate_instruction};
	use crate::constants::{
		DEX_PROGRAM_ID, LAUNCHPAD_PROGRAM_ID, TOKEN_PROGRAM_ID, WSOL_MINT,
	};

	const CREATOR_FEE_CONFIG_INDEX: usize = 15;
	const DEX_CREATOR_FEE_CONFIG_INDEX: usize = 22;

	const BASE_MINT: Address = Address::new_from_array([2u8; 32]);

	fn migrate() -> solana_instruction::Instruction {
		let caller = Address::new_from_array([1u8; 32]);
		build_migrate_instruction(&BuildMigrateParams {
			caller: &caller,
			base_mint: &BASE_MINT,
			quote_mint: &WSOL_MINT,
			quote_token_program: &TOKEN_PROGRAM_ID,
		})
	}

	fn creator_fee_config_pda(program: &Address) -> Address {
		Address::find_program_address(
			&[
				b"creator_fee_config",
				BASE_MINT.as_ref(),
				WSOL_MINT.as_ref(),
			],
			program,
		)
		.0
	}

	#[test]
	fn the_creator_fee_config_slot_is_the_curves_pda() {
		let ix = migrate();

		assert_eq!(ix.accounts.len(), 23);
		let slot = &ix.accounts[CREATOR_FEE_CONFIG_INDEX];
		assert_eq!(slot.pubkey, creator_fee_config_pda(&LAUNCHPAD_PROGRAM_ID));
		assert_eq!(
			slot.pubkey,
			solana_address::address!(
				"BMrF9UWCQwk4bYq9nYsfLWFCVx6UhgLXrxJCABENvd19"
			),
		);
		assert!(!slot.is_signer);
		assert!(slot.is_writable);
	}

	#[test]
	fn the_last_slot_is_the_pools_creator_fee_config_pda() {
		let ix = migrate();

		let slot = &ix.accounts[DEX_CREATOR_FEE_CONFIG_INDEX];
		assert_eq!(slot.pubkey, creator_fee_config_pda(&DEX_PROGRAM_ID));
		assert_eq!(
			slot.pubkey,
			solana_address::address!(
				"DBtuuqp2KVdSJG6ffEd3NiLwhuBwybSXANx7YtjUsG6w"
			),
		);
		assert!(!slot.is_signer);
		assert!(slot.is_writable);
	}

	#[test]
	fn the_curves_and_the_pools_slots_differ() {
		let ix = migrate();

		assert_ne!(
			ix.accounts[CREATOR_FEE_CONFIG_INDEX].pubkey,
			ix.accounts[DEX_CREATOR_FEE_CONFIG_INDEX].pubkey,
		);
	}
}