1#![allow(clippy::too_many_arguments)]
4
5use {
6 crate::{
7 find_pool_address, find_pool_mint_address, find_pool_mint_authority_address,
8 find_pool_mpl_authority_address, find_pool_onramp_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_instruction::{AccountMeta, Instruction},
15 solana_program_pack::Pack,
16 solana_pubkey::Pubkey,
17 solana_rent::Rent,
18 solana_stake_interface::{self as stake, sysvar::stake_history},
19 solana_system_interface::{instruction as system_instruction, program as system_program},
20 solana_sysvar as sysvar, spl_token_interface as spl_token,
21};
22
23#[repr(C)]
25#[derive(Clone, Debug, PartialEq, BorshSerialize, BorshDeserialize)]
26pub enum SinglePoolInstruction {
27 InitializePool,
46
47 ReplenishPool,
79
80 DepositStake,
98
99 WithdrawStake {
113 user_stake_authority: Pubkey,
115 token_amount: u64,
117 },
118
119 CreateTokenMetadata,
134
135 UpdateTokenMetadata {
145 name: String,
147 symbol: String,
149 uri: String,
151 },
152
153 InitializePoolOnRamp,
168
169 DepositSol {
192 lamports: u64,
194 },
195}
196
197pub fn initialize(
199 program_id: &Pubkey,
200 vote_account_address: &Pubkey,
201 payer: &Pubkey,
202 rent: &Rent,
203 minimum_pool_balance: u64,
204) -> Vec<Instruction> {
205 let pool_address = find_pool_address(program_id, vote_account_address);
206 let pool_rent = rent.minimum_balance(SinglePool::size_of());
207
208 let stake_address = find_pool_stake_address(program_id, &pool_address);
209 let onramp_address = find_pool_onramp_address(program_id, &pool_address);
210 let stake_space = stake::state::StakeStateV2::size_of();
211 let stake_rent = rent.minimum_balance(stake_space);
212 let stake_rent_plus_minimum = stake_rent.saturating_add(minimum_pool_balance);
213
214 let mint_address = find_pool_mint_address(program_id, &pool_address);
215 let mint_rent = rent.minimum_balance(spl_token::state::Mint::LEN);
216
217 vec![
218 system_instruction::transfer(payer, &pool_address, pool_rent),
219 system_instruction::transfer(payer, &stake_address, stake_rent_plus_minimum),
220 system_instruction::transfer(payer, &onramp_address, stake_rent),
221 system_instruction::transfer(payer, &mint_address, mint_rent),
222 initialize_pool(program_id, vote_account_address),
223 initialize_pool_onramp(program_id, &pool_address),
224 create_token_metadata(program_id, &pool_address, payer),
225 ]
226}
227
228pub fn initialize_pool(program_id: &Pubkey, vote_account_address: &Pubkey) -> Instruction {
230 let pool_address = find_pool_address(program_id, vote_account_address);
231 let mint_address = find_pool_mint_address(program_id, &pool_address);
232
233 let data = borsh::to_vec(&SinglePoolInstruction::InitializePool).unwrap();
234 let accounts = vec![
235 AccountMeta::new_readonly(*vote_account_address, false),
236 AccountMeta::new(pool_address, false),
237 AccountMeta::new(find_pool_stake_address(program_id, &pool_address), false),
238 AccountMeta::new(mint_address, false),
239 AccountMeta::new_readonly(
240 find_pool_stake_authority_address(program_id, &pool_address),
241 false,
242 ),
243 AccountMeta::new_readonly(
244 find_pool_mint_authority_address(program_id, &pool_address),
245 false,
246 ),
247 AccountMeta::new_readonly(sysvar::rent::id(), false),
248 AccountMeta::new_readonly(sysvar::clock::id(), false),
249 AccountMeta::new_readonly(stake_history::id(), false),
250 #[allow(deprecated)]
251 AccountMeta::new_readonly(stake::config::id(), false),
252 AccountMeta::new_readonly(system_program::id(), false),
253 AccountMeta::new_readonly(spl_token::id(), false),
254 AccountMeta::new_readonly(stake::program::id(), false),
255 ];
256
257 Instruction {
258 program_id: *program_id,
259 accounts,
260 data,
261 }
262}
263
264pub fn replenish_pool(program_id: &Pubkey, vote_account_address: &Pubkey) -> Instruction {
266 let pool_address = find_pool_address(program_id, vote_account_address);
267
268 let data = borsh::to_vec(&SinglePoolInstruction::ReplenishPool).unwrap();
269 let accounts = vec![
270 AccountMeta::new_readonly(*vote_account_address, false),
271 AccountMeta::new_readonly(pool_address, false),
272 AccountMeta::new(find_pool_stake_address(program_id, &pool_address), false),
273 AccountMeta::new(find_pool_onramp_address(program_id, &pool_address), false),
274 AccountMeta::new_readonly(
275 find_pool_stake_authority_address(program_id, &pool_address),
276 false,
277 ),
278 AccountMeta::new_readonly(sysvar::clock::id(), false),
279 AccountMeta::new_readonly(stake_history::id(), false),
280 #[allow(deprecated)]
281 AccountMeta::new_readonly(stake::config::id(), false),
282 AccountMeta::new_readonly(stake::program::id(), false),
283 ];
284
285 Instruction {
286 program_id: *program_id,
287 accounts,
288 data,
289 }
290}
291
292pub fn deposit(
294 program_id: &Pubkey,
295 pool_address: &Pubkey,
296 user_stake_account: &Pubkey,
297 user_token_account: &Pubkey,
298 user_lamport_account: &Pubkey,
299 user_withdraw_authority: &Pubkey,
300) -> Vec<Instruction> {
301 let pool_stake_authority = find_pool_stake_authority_address(program_id, pool_address);
302
303 vec![
304 stake::instruction::authorize(
305 user_stake_account,
306 user_withdraw_authority,
307 &pool_stake_authority,
308 stake::state::StakeAuthorize::Staker,
309 None,
310 ),
311 stake::instruction::authorize(
312 user_stake_account,
313 user_withdraw_authority,
314 &pool_stake_authority,
315 stake::state::StakeAuthorize::Withdrawer,
316 None,
317 ),
318 deposit_stake(
319 program_id,
320 pool_address,
321 user_stake_account,
322 user_token_account,
323 user_lamport_account,
324 ),
325 ]
326}
327
328pub fn deposit_stake(
330 program_id: &Pubkey,
331 pool_address: &Pubkey,
332 user_stake_account: &Pubkey,
333 user_token_account: &Pubkey,
334 user_lamport_account: &Pubkey,
335) -> Instruction {
336 let data = borsh::to_vec(&SinglePoolInstruction::DepositStake).unwrap();
337
338 let accounts = vec![
339 AccountMeta::new_readonly(*pool_address, false),
340 AccountMeta::new(find_pool_stake_address(program_id, pool_address), false),
341 AccountMeta::new_readonly(find_pool_onramp_address(program_id, pool_address), false),
342 AccountMeta::new(find_pool_mint_address(program_id, pool_address), false),
343 AccountMeta::new_readonly(
344 find_pool_stake_authority_address(program_id, pool_address),
345 false,
346 ),
347 AccountMeta::new_readonly(
348 find_pool_mint_authority_address(program_id, pool_address),
349 false,
350 ),
351 AccountMeta::new(*user_stake_account, false),
352 AccountMeta::new(*user_token_account, false),
353 AccountMeta::new(*user_lamport_account, false),
354 AccountMeta::new_readonly(sysvar::clock::id(), false),
355 AccountMeta::new_readonly(stake_history::id(), false),
356 AccountMeta::new_readonly(spl_token::id(), false),
357 AccountMeta::new_readonly(stake::program::id(), false),
358 ];
359
360 Instruction {
361 program_id: *program_id,
362 accounts,
363 data,
364 }
365}
366
367pub fn deposit_liquid(
374 program_id: &Pubkey,
375 vote_account_address: &Pubkey,
376 user_wallet: &Pubkey,
377 escrow_deposit_account: &Pubkey,
378 user_token_account: &Pubkey,
379 lamports: u64,
380) -> Vec<Instruction> {
381 vec![
382 system_instruction::transfer(user_wallet, escrow_deposit_account, lamports),
383 deposit_sol(
384 program_id,
385 vote_account_address,
386 escrow_deposit_account,
387 user_token_account,
388 lamports,
389 ),
390 ]
391}
392
393pub fn deposit_sol(
397 program_id: &Pubkey,
398 vote_account_address: &Pubkey,
399 user_deposit_account: &Pubkey,
400 user_token_account: &Pubkey,
401 lamports: u64,
402) -> Instruction {
403 let pool_address = find_pool_address(program_id, vote_account_address);
404
405 let data = borsh::to_vec(&SinglePoolInstruction::DepositSol { lamports }).unwrap();
406 let accounts = vec![
407 AccountMeta::new_readonly(*vote_account_address, false),
408 AccountMeta::new_readonly(pool_address, false),
409 AccountMeta::new(find_pool_stake_address(program_id, &pool_address), false),
410 AccountMeta::new(find_pool_onramp_address(program_id, &pool_address), false),
411 AccountMeta::new(find_pool_mint_address(program_id, &pool_address), false),
412 AccountMeta::new_readonly(
413 find_pool_stake_authority_address(program_id, &pool_address),
414 false,
415 ),
416 AccountMeta::new_readonly(
417 find_pool_mint_authority_address(program_id, &pool_address),
418 false,
419 ),
420 AccountMeta::new(*user_deposit_account, true),
421 AccountMeta::new(*user_token_account, false),
422 AccountMeta::new_readonly(sysvar::clock::id(), false),
423 AccountMeta::new_readonly(stake_history::id(), false),
424 #[allow(deprecated)]
425 AccountMeta::new_readonly(stake::config::id(), false),
426 AccountMeta::new_readonly(system_program::id(), false),
427 AccountMeta::new_readonly(spl_token::id(), false),
428 AccountMeta::new_readonly(stake::program::id(), false),
429 AccountMeta::new_readonly(*program_id, false),
430 ];
431
432 Instruction {
433 program_id: *program_id,
434 accounts,
435 data,
436 }
437}
438
439pub fn withdraw(
444 program_id: &Pubkey,
445 pool_address: &Pubkey,
446 user_stake_account: &Pubkey,
447 user_stake_authority: &Pubkey,
448 user_token_account: &Pubkey,
449 user_token_authority: &Pubkey,
450 token_amount: u64,
451) -> Vec<Instruction> {
452 vec![
453 spl_token::instruction::approve(
454 &spl_token::id(),
455 user_token_account,
456 &find_pool_mint_authority_address(program_id, pool_address),
457 user_token_authority,
458 &[],
459 token_amount,
460 )
461 .unwrap(),
462 withdraw_stake(
463 program_id,
464 pool_address,
465 user_stake_account,
466 user_stake_authority,
467 user_token_account,
468 token_amount,
469 ),
470 ]
471}
472
473pub fn withdraw_stake(
475 program_id: &Pubkey,
476 pool_address: &Pubkey,
477 user_stake_account: &Pubkey,
478 user_stake_authority: &Pubkey,
479 user_token_account: &Pubkey,
480 token_amount: u64,
481) -> Instruction {
482 let data = borsh::to_vec(&SinglePoolInstruction::WithdrawStake {
483 user_stake_authority: *user_stake_authority,
484 token_amount,
485 })
486 .unwrap();
487
488 let accounts = vec![
489 AccountMeta::new_readonly(*pool_address, false),
490 AccountMeta::new(find_pool_stake_address(program_id, pool_address), false),
491 AccountMeta::new_readonly(find_pool_onramp_address(program_id, pool_address), false),
492 AccountMeta::new(find_pool_mint_address(program_id, pool_address), false),
493 AccountMeta::new_readonly(
494 find_pool_stake_authority_address(program_id, pool_address),
495 false,
496 ),
497 AccountMeta::new_readonly(
498 find_pool_mint_authority_address(program_id, pool_address),
499 false,
500 ),
501 AccountMeta::new(*user_stake_account, false),
502 AccountMeta::new(*user_token_account, false),
503 AccountMeta::new_readonly(sysvar::clock::id(), false),
504 AccountMeta::new_readonly(spl_token::id(), false),
505 AccountMeta::new_readonly(stake::program::id(), false),
506 ];
507
508 Instruction {
509 program_id: *program_id,
510 accounts,
511 data,
512 }
513}
514
515pub fn create_token_metadata(
517 program_id: &Pubkey,
518 pool_address: &Pubkey,
519 payer: &Pubkey,
520) -> Instruction {
521 let pool_mint = find_pool_mint_address(program_id, pool_address);
522 let (token_metadata, _) = find_metadata_account(&pool_mint);
523 let data = borsh::to_vec(&SinglePoolInstruction::CreateTokenMetadata).unwrap();
524
525 let accounts = vec![
526 AccountMeta::new_readonly(*pool_address, false),
527 AccountMeta::new_readonly(pool_mint, false),
528 AccountMeta::new_readonly(
529 find_pool_mint_authority_address(program_id, pool_address),
530 false,
531 ),
532 AccountMeta::new_readonly(
533 find_pool_mpl_authority_address(program_id, pool_address),
534 false,
535 ),
536 AccountMeta::new(*payer, true),
537 AccountMeta::new(token_metadata, false),
538 AccountMeta::new_readonly(inline_mpl_token_metadata::id(), false),
539 AccountMeta::new_readonly(system_program::id(), false),
540 ];
541
542 Instruction {
543 program_id: *program_id,
544 accounts,
545 data,
546 }
547}
548
549pub fn update_token_metadata(
551 program_id: &Pubkey,
552 vote_account_address: &Pubkey,
553 authorized_withdrawer: &Pubkey,
554 name: String,
555 symbol: String,
556 uri: String,
557) -> Instruction {
558 let pool_address = find_pool_address(program_id, vote_account_address);
559 let pool_mint = find_pool_mint_address(program_id, &pool_address);
560 let (token_metadata, _) = find_metadata_account(&pool_mint);
561 let data =
562 borsh::to_vec(&SinglePoolInstruction::UpdateTokenMetadata { name, symbol, uri }).unwrap();
563
564 let accounts = vec![
565 AccountMeta::new_readonly(*vote_account_address, false),
566 AccountMeta::new_readonly(pool_address, false),
567 AccountMeta::new_readonly(
568 find_pool_mpl_authority_address(program_id, &pool_address),
569 false,
570 ),
571 AccountMeta::new_readonly(*authorized_withdrawer, true),
572 AccountMeta::new(token_metadata, false),
573 AccountMeta::new_readonly(inline_mpl_token_metadata::id(), false),
574 ];
575
576 Instruction {
577 program_id: *program_id,
578 accounts,
579 data,
580 }
581}
582
583pub fn initialize_pool_onramp(program_id: &Pubkey, pool_address: &Pubkey) -> Instruction {
585 let data = borsh::to_vec(&SinglePoolInstruction::InitializePoolOnRamp).unwrap();
586 let accounts = vec![
587 AccountMeta::new_readonly(*pool_address, false),
588 AccountMeta::new(find_pool_onramp_address(program_id, pool_address), false),
589 AccountMeta::new_readonly(
590 find_pool_stake_authority_address(program_id, pool_address),
591 false,
592 ),
593 AccountMeta::new_readonly(sysvar::rent::id(), false),
594 AccountMeta::new_readonly(system_program::id(), false),
595 AccountMeta::new_readonly(stake::program::id(), false),
596 ];
597
598 Instruction {
599 program_id: *program_id,
600 accounts,
601 data,
602 }
603}
604
605pub fn create_pool_onramp(
609 program_id: &Pubkey,
610 pool_address: &Pubkey,
611 payer: &Pubkey,
612 rent: &Rent,
613) -> Vec<Instruction> {
614 let onramp_address = find_pool_onramp_address(program_id, pool_address);
615 let stake_space = stake::state::StakeStateV2::size_of();
616 let stake_rent = rent.minimum_balance(stake_space);
617
618 vec![
619 system_instruction::transfer(payer, &onramp_address, stake_rent),
620 initialize_pool_onramp(program_id, pool_address),
621 ]
622}