1use crate::constants::{
2 ATA_PROGRAM_ID, DEX_PROGRAM_ID, LAUNCHPAD_PROGRAM_ID, NEXUS_PROGRAM_ID,
3 SYSTEM_PROGRAM_ID, TOKEN_2022_PROGRAM_ID,
4};
5use crate::transfer_hook::{HookLeg, HookLegs};
6use crate::utils::{
7 BuildError, MintInfo, TradeMints, append_hooks, check_base_mint,
8};
9use solana_address::Address;
10use solana_instruction::Instruction;
11
12#[derive(Clone, Debug)]
13pub struct BuildMigrateParams<'a> {
14 pub caller: &'a Address,
15 pub base_mint_info: &'a MintInfo,
17 pub quote_mint_info: &'a MintInfo,
19}
20
21pub fn build_migrate_instruction(
23 p: &BuildMigrateParams<'_>,
24) -> Result<Instruction, BuildError> {
25 check_base_mint(p.base_mint_info)?;
26 let (instruction, legs) = bare_migrate_instruction(p);
27 append_hooks(
28 instruction,
29 TradeMints {
30 base: p.base_mint_info,
31 quote: p.quote_mint_info,
32 },
33 &legs,
34 )
35}
36
37fn bare_migrate_instruction(
42 p: &BuildMigrateParams<'_>,
43) -> (Instruction, HookLegs) {
44 let base_mint = &p.base_mint_info.address;
45 let quote_mint = &p.quote_mint_info.address;
46 let quote_token_program = &p.quote_mint_info.token_program;
47
48 let (bonding_curve, _) =
49 super::pda::find_bonding_curve_pda(base_mint, quote_mint);
50 let (base_vault, _) = crate::utils::find_associated_token_pda(
51 &bonding_curve,
52 base_mint,
53 &TOKEN_2022_PROGRAM_ID,
54 );
55 let (quote_vault, _) = crate::utils::find_associated_token_pda(
56 &bonding_curve,
57 quote_mint,
58 quote_token_program,
59 );
60 let (creator_fee_config, _) =
61 super::pda::find_creator_fee_config_pda(base_mint, quote_mint);
62
63 let (pool, _) = crate::dex::pda::find_pool_pda(base_mint, quote_mint);
64 let (lp_mint, _) = crate::dex::pda::find_lp_mint_pda(base_mint, quote_mint);
65 let (pool_base_vault, _) = crate::utils::find_associated_token_pda(
66 &pool,
67 base_mint,
68 &TOKEN_2022_PROGRAM_ID,
69 );
70 let (pool_quote_vault, _) = crate::utils::find_associated_token_pda(
71 &pool,
72 quote_mint,
73 quote_token_program,
74 );
75 let (pool_lp_account, _) = crate::utils::find_associated_token_pda(
76 &pool,
77 &lp_mint,
78 &TOKEN_2022_PROGRAM_ID,
79 );
80 let (dex_creator_fee_config, _) =
81 crate::dex::pda::find_creator_fee_config_pda(base_mint, quote_mint);
82
83 let migrate = super::instructions::Migrate {
84 caller: *p.caller,
85 bonding_curve,
86 base_mint: *base_mint,
87 quote_mint: *quote_mint,
88 base_vault,
89 quote_vault,
90 dex_program: DEX_PROGRAM_ID,
91 migration_authority: super::pda::MIGRATION_AUTHORITY_ADDRESS,
92 pool,
93 lp_mint,
94 pool_base_vault,
95 pool_quote_vault,
96 pool_lp_account,
97 dex_event_authority: crate::dex::pda::EVENT_AUTHORITY_ADDRESS,
98 nexus_program: NEXUS_PROGRAM_ID,
99 creator_fee_config,
100 base_token_program: TOKEN_2022_PROGRAM_ID,
101 quote_token_program: *quote_token_program,
102 associated_token_program: ATA_PROGRAM_ID,
103 system_program: SYSTEM_PROGRAM_ID,
104 event_authority: super::pda::EVENT_AUTHORITY_ADDRESS,
105 program: LAUNCHPAD_PROGRAM_ID,
106 dex_creator_fee_config,
107 };
108 let from_curve = |source, mint, destination| HookLeg {
109 source,
110 mint,
111 destination,
112 authority: bonding_curve,
113 destination_owner: pool,
114 };
115 let legs = HookLegs {
116 legs: vec![
117 from_curve(quote_vault, *quote_mint, pool_quote_vault),
118 from_curve(base_vault, *base_mint, pool_base_vault),
119 ],
120 written: Vec::new(),
121 };
122 (migrate.instruction(), legs)
123}
124
125#[cfg(test)]
126mod tests {
127 use solana_address::Address;
128
129 use super::{BuildMigrateParams, build_migrate_instruction};
130 use crate::constants::{
131 DEX_PROGRAM_ID, LAUNCHPAD_PROGRAM_ID, TOKEN_2022_PROGRAM_ID,
132 TOKEN_DECIMALS, WSOL_MINT, WSOL_MINT_INFO,
133 };
134 use crate::utils::MintInfo;
135
136 const CREATOR_FEE_CONFIG_INDEX: usize = 15;
137 const DEX_CREATOR_FEE_CONFIG_INDEX: usize = 22;
138
139 const BASE_MINT: Address = Address::new_from_array([2u8; 32]);
140
141 fn migrate() -> solana_instruction::Instruction {
142 let caller = Address::new_from_array([1u8; 32]);
143 build_migrate_instruction(&BuildMigrateParams {
144 caller: &caller,
145 base_mint_info: &MintInfo::plain(
146 BASE_MINT,
147 TOKEN_2022_PROGRAM_ID,
148 TOKEN_DECIMALS,
149 ),
150 quote_mint_info: &WSOL_MINT_INFO,
151 })
152 .unwrap()
153 }
154
155 fn creator_fee_config_pda(program: &Address) -> Address {
156 Address::find_program_address(
157 &[
158 b"creator_fee_config",
159 BASE_MINT.as_ref(),
160 WSOL_MINT.as_ref(),
161 ],
162 program,
163 )
164 .0
165 }
166
167 #[test]
168 fn the_creator_fee_config_slot_is_the_curves_pda() {
169 let ix = migrate();
170
171 assert_eq!(ix.accounts.len(), 23);
172 let slot = &ix.accounts[CREATOR_FEE_CONFIG_INDEX];
173 assert_eq!(slot.pubkey, creator_fee_config_pda(&LAUNCHPAD_PROGRAM_ID));
174 assert_eq!(
175 slot.pubkey,
176 solana_address::address!(
177 "BMrF9UWCQwk4bYq9nYsfLWFCVx6UhgLXrxJCABENvd19"
178 ),
179 );
180 assert!(!slot.is_signer);
181 assert!(slot.is_writable);
182 }
183
184 #[test]
185 fn the_last_slot_is_the_pools_creator_fee_config_pda() {
186 let ix = migrate();
187
188 let slot = &ix.accounts[DEX_CREATOR_FEE_CONFIG_INDEX];
189 assert_eq!(slot.pubkey, creator_fee_config_pda(&DEX_PROGRAM_ID));
190 assert_eq!(
191 slot.pubkey,
192 solana_address::address!(
193 "DBtuuqp2KVdSJG6ffEd3NiLwhuBwybSXANx7YtjUsG6w"
194 ),
195 );
196 assert!(!slot.is_signer);
197 assert!(slot.is_writable);
198 }
199
200 #[test]
201 fn the_curves_and_the_pools_slots_differ() {
202 let ix = migrate();
203
204 assert_ne!(
205 ix.accounts[CREATOR_FEE_CONFIG_INDEX].pubkey,
206 ix.accounts[DEX_CREATOR_FEE_CONFIG_INDEX].pubkey,
207 );
208 }
209}