1#![allow(clippy::too_many_arguments)]
4
5use {
6 crate::{
7 find_default_deposit_account_address_and_seed, find_pool_address, find_pool_mint_address,
8 find_pool_mint_authority_address, find_pool_mpl_authority_address, find_pool_stake_address,
9 find_pool_stake_authority_address,
10 inline_mpl_token_metadata::{self, pda::find_metadata_account},
11 state::SinglePool,
12 },
13 borsh::{BorshDeserialize, BorshSerialize},
14 solana_program::{
15 instruction::{AccountMeta, Instruction},
16 program_pack::Pack,
17 pubkey::Pubkey,
18 rent::Rent,
19 stake, system_instruction, system_program, sysvar,
20 },
21};
22
23#[repr(C)]
25#[derive(Clone, Debug, PartialEq, BorshSerialize, BorshDeserialize)]
26pub enum SinglePoolInstruction {
27 InitializePool,
46
47 ReactivatePoolStake,
60
61 DepositStake,
78
79 WithdrawStake {
92 user_stake_authority: Pubkey,
94 token_amount: u64,
96 },
97
98 CreateTokenMetadata,
113
114 UpdateTokenMetadata {
124 name: String,
126 symbol: String,
128 uri: String,
130 },
131}
132
133pub fn initialize(
135 program_id: &Pubkey,
136 vote_account_address: &Pubkey,
137 payer: &Pubkey,
138 rent: &Rent,
139 minimum_delegation: u64,
140) -> Vec<Instruction> {
141 let pool_address = find_pool_address(program_id, vote_account_address);
142 let pool_rent = rent.minimum_balance(std::mem::size_of::<SinglePool>());
143
144 let stake_address = find_pool_stake_address(program_id, &pool_address);
145 let stake_space = std::mem::size_of::<stake::state::StakeStateV2>();
146 let stake_rent_plus_minimum = rent
147 .minimum_balance(stake_space)
148 .saturating_add(minimum_delegation);
149
150 let mint_address = find_pool_mint_address(program_id, &pool_address);
151 let mint_rent = rent.minimum_balance(spl_token::state::Mint::LEN);
152
153 vec![
154 system_instruction::transfer(payer, &pool_address, pool_rent),
155 system_instruction::transfer(payer, &stake_address, stake_rent_plus_minimum),
156 system_instruction::transfer(payer, &mint_address, mint_rent),
157 initialize_pool(program_id, vote_account_address),
158 create_token_metadata(program_id, &pool_address, payer),
159 ]
160}
161
162pub fn initialize_pool(program_id: &Pubkey, vote_account_address: &Pubkey) -> Instruction {
164 let pool_address = find_pool_address(program_id, vote_account_address);
165 let mint_address = find_pool_mint_address(program_id, &pool_address);
166
167 let data = borsh::to_vec(&SinglePoolInstruction::InitializePool).unwrap();
168 let accounts = vec![
169 AccountMeta::new_readonly(*vote_account_address, false),
170 AccountMeta::new(pool_address, false),
171 AccountMeta::new(find_pool_stake_address(program_id, &pool_address), false),
172 AccountMeta::new(mint_address, false),
173 AccountMeta::new_readonly(
174 find_pool_stake_authority_address(program_id, &pool_address),
175 false,
176 ),
177 AccountMeta::new_readonly(
178 find_pool_mint_authority_address(program_id, &pool_address),
179 false,
180 ),
181 AccountMeta::new_readonly(sysvar::rent::id(), false),
182 AccountMeta::new_readonly(sysvar::clock::id(), false),
183 AccountMeta::new_readonly(sysvar::stake_history::id(), false),
184 #[allow(deprecated)]
185 AccountMeta::new_readonly(stake::config::id(), false),
186 AccountMeta::new_readonly(system_program::id(), false),
187 AccountMeta::new_readonly(spl_token::id(), false),
188 AccountMeta::new_readonly(stake::program::id(), false),
189 ];
190
191 Instruction {
192 program_id: *program_id,
193 accounts,
194 data,
195 }
196}
197
198pub fn reactivate_pool_stake(program_id: &Pubkey, vote_account_address: &Pubkey) -> Instruction {
200 let pool_address = find_pool_address(program_id, vote_account_address);
201
202 let data = borsh::to_vec(&SinglePoolInstruction::ReactivatePoolStake).unwrap();
203 let accounts = vec![
204 AccountMeta::new_readonly(*vote_account_address, false),
205 AccountMeta::new_readonly(pool_address, false),
206 AccountMeta::new(find_pool_stake_address(program_id, &pool_address), false),
207 AccountMeta::new_readonly(
208 find_pool_stake_authority_address(program_id, &pool_address),
209 false,
210 ),
211 AccountMeta::new_readonly(sysvar::clock::id(), false),
212 AccountMeta::new_readonly(sysvar::stake_history::id(), false),
213 #[allow(deprecated)]
214 AccountMeta::new_readonly(stake::config::id(), false),
215 AccountMeta::new_readonly(stake::program::id(), false),
216 ];
217
218 Instruction {
219 program_id: *program_id,
220 accounts,
221 data,
222 }
223}
224
225pub fn deposit(
227 program_id: &Pubkey,
228 pool_address: &Pubkey,
229 user_stake_account: &Pubkey,
230 user_token_account: &Pubkey,
231 user_lamport_account: &Pubkey,
232 user_withdraw_authority: &Pubkey,
233) -> Vec<Instruction> {
234 let pool_stake_authority = find_pool_stake_authority_address(program_id, pool_address);
235
236 vec![
237 stake::instruction::authorize(
238 user_stake_account,
239 user_withdraw_authority,
240 &pool_stake_authority,
241 stake::state::StakeAuthorize::Staker,
242 None,
243 ),
244 stake::instruction::authorize(
245 user_stake_account,
246 user_withdraw_authority,
247 &pool_stake_authority,
248 stake::state::StakeAuthorize::Withdrawer,
249 None,
250 ),
251 deposit_stake(
252 program_id,
253 pool_address,
254 user_stake_account,
255 user_token_account,
256 user_lamport_account,
257 ),
258 ]
259}
260
261pub fn deposit_stake(
263 program_id: &Pubkey,
264 pool_address: &Pubkey,
265 user_stake_account: &Pubkey,
266 user_token_account: &Pubkey,
267 user_lamport_account: &Pubkey,
268) -> Instruction {
269 let data = borsh::to_vec(&SinglePoolInstruction::DepositStake).unwrap();
270
271 let accounts = vec![
272 AccountMeta::new_readonly(*pool_address, false),
273 AccountMeta::new(find_pool_stake_address(program_id, pool_address), false),
274 AccountMeta::new(find_pool_mint_address(program_id, pool_address), false),
275 AccountMeta::new_readonly(
276 find_pool_stake_authority_address(program_id, pool_address),
277 false,
278 ),
279 AccountMeta::new_readonly(
280 find_pool_mint_authority_address(program_id, pool_address),
281 false,
282 ),
283 AccountMeta::new(*user_stake_account, false),
284 AccountMeta::new(*user_token_account, false),
285 AccountMeta::new(*user_lamport_account, false),
286 AccountMeta::new_readonly(sysvar::clock::id(), false),
287 AccountMeta::new_readonly(sysvar::stake_history::id(), false),
288 AccountMeta::new_readonly(spl_token::id(), false),
289 AccountMeta::new_readonly(stake::program::id(), false),
290 ];
291
292 Instruction {
293 program_id: *program_id,
294 accounts,
295 data,
296 }
297}
298
299pub fn withdraw(
305 program_id: &Pubkey,
306 pool_address: &Pubkey,
307 user_stake_account: &Pubkey,
308 user_stake_authority: &Pubkey,
309 user_token_account: &Pubkey,
310 user_token_authority: &Pubkey,
311 token_amount: u64,
312) -> Vec<Instruction> {
313 vec![
314 spl_token::instruction::approve(
315 &spl_token::id(),
316 user_token_account,
317 &find_pool_mint_authority_address(program_id, pool_address),
318 user_token_authority,
319 &[],
320 token_amount,
321 )
322 .unwrap(),
323 withdraw_stake(
324 program_id,
325 pool_address,
326 user_stake_account,
327 user_stake_authority,
328 user_token_account,
329 token_amount,
330 ),
331 ]
332}
333
334pub fn withdraw_stake(
336 program_id: &Pubkey,
337 pool_address: &Pubkey,
338 user_stake_account: &Pubkey,
339 user_stake_authority: &Pubkey,
340 user_token_account: &Pubkey,
341 token_amount: u64,
342) -> Instruction {
343 let data = borsh::to_vec(&SinglePoolInstruction::WithdrawStake {
344 user_stake_authority: *user_stake_authority,
345 token_amount,
346 })
347 .unwrap();
348
349 let accounts = vec![
350 AccountMeta::new_readonly(*pool_address, false),
351 AccountMeta::new(find_pool_stake_address(program_id, pool_address), false),
352 AccountMeta::new(find_pool_mint_address(program_id, pool_address), false),
353 AccountMeta::new_readonly(
354 find_pool_stake_authority_address(program_id, pool_address),
355 false,
356 ),
357 AccountMeta::new_readonly(
358 find_pool_mint_authority_address(program_id, pool_address),
359 false,
360 ),
361 AccountMeta::new(*user_stake_account, false),
362 AccountMeta::new(*user_token_account, false),
363 AccountMeta::new_readonly(sysvar::clock::id(), false),
364 AccountMeta::new_readonly(spl_token::id(), false),
365 AccountMeta::new_readonly(stake::program::id(), false),
366 ];
367
368 Instruction {
369 program_id: *program_id,
370 accounts,
371 data,
372 }
373}
374
375pub fn create_and_delegate_user_stake(
381 program_id: &Pubkey,
382 vote_account_address: &Pubkey,
383 user_wallet: &Pubkey,
384 rent: &Rent,
385 stake_amount: u64,
386) -> Vec<Instruction> {
387 let pool_address = find_pool_address(program_id, vote_account_address);
388 let stake_space = std::mem::size_of::<stake::state::StakeStateV2>();
389 let lamports = rent
390 .minimum_balance(stake_space)
391 .saturating_add(stake_amount);
392 let (deposit_address, deposit_seed) =
393 find_default_deposit_account_address_and_seed(&pool_address, user_wallet);
394
395 stake::instruction::create_account_with_seed_and_delegate_stake(
396 user_wallet,
397 &deposit_address,
398 user_wallet,
399 &deposit_seed,
400 vote_account_address,
401 &stake::state::Authorized::auto(user_wallet),
402 &stake::state::Lockup::default(),
403 lamports,
404 )
405}
406
407pub fn create_token_metadata(
409 program_id: &Pubkey,
410 pool_address: &Pubkey,
411 payer: &Pubkey,
412) -> Instruction {
413 let pool_mint = find_pool_mint_address(program_id, pool_address);
414 let (token_metadata, _) = find_metadata_account(&pool_mint);
415 let data = borsh::to_vec(&SinglePoolInstruction::CreateTokenMetadata).unwrap();
416
417 let accounts = vec![
418 AccountMeta::new_readonly(*pool_address, false),
419 AccountMeta::new_readonly(pool_mint, false),
420 AccountMeta::new_readonly(
421 find_pool_mint_authority_address(program_id, pool_address),
422 false,
423 ),
424 AccountMeta::new_readonly(
425 find_pool_mpl_authority_address(program_id, pool_address),
426 false,
427 ),
428 AccountMeta::new(*payer, true),
429 AccountMeta::new(token_metadata, false),
430 AccountMeta::new_readonly(inline_mpl_token_metadata::id(), false),
431 AccountMeta::new_readonly(system_program::id(), false),
432 ];
433
434 Instruction {
435 program_id: *program_id,
436 accounts,
437 data,
438 }
439}
440
441pub fn update_token_metadata(
443 program_id: &Pubkey,
444 vote_account_address: &Pubkey,
445 authorized_withdrawer: &Pubkey,
446 name: String,
447 symbol: String,
448 uri: String,
449) -> Instruction {
450 let pool_address = find_pool_address(program_id, vote_account_address);
451 let pool_mint = find_pool_mint_address(program_id, &pool_address);
452 let (token_metadata, _) = find_metadata_account(&pool_mint);
453 let data =
454 borsh::to_vec(&SinglePoolInstruction::UpdateTokenMetadata { name, symbol, uri }).unwrap();
455
456 let accounts = vec![
457 AccountMeta::new_readonly(*vote_account_address, false),
458 AccountMeta::new_readonly(pool_address, false),
459 AccountMeta::new_readonly(
460 find_pool_mpl_authority_address(program_id, &pool_address),
461 false,
462 ),
463 AccountMeta::new_readonly(*authorized_withdrawer, true),
464 AccountMeta::new(token_metadata, false),
465 AccountMeta::new_readonly(inline_mpl_token_metadata::id(), false),
466 ];
467
468 Instruction {
469 program_id: *program_id,
470 accounts,
471 data,
472 }
473}