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,
9 find_pool_onramp_address, find_pool_stake_address, 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 ReplenishPool,
79
80 DepositStake,
97
98 WithdrawStake {
111 user_stake_authority: Pubkey,
113 token_amount: u64,
115 },
116
117 CreateTokenMetadata,
132
133 UpdateTokenMetadata {
143 name: String,
145 symbol: String,
147 uri: String,
149 },
150
151 InitializePoolOnRamp,
170}
171
172pub fn initialize(
174 program_id: &Pubkey,
175 vote_account_address: &Pubkey,
176 payer: &Pubkey,
177 rent: &Rent,
178 minimum_pool_balance: u64,
179) -> Vec<Instruction> {
180 let pool_address = find_pool_address(program_id, vote_account_address);
181 let pool_rent = rent.minimum_balance(std::mem::size_of::<SinglePool>());
182
183 let stake_address = find_pool_stake_address(program_id, &pool_address);
184 let onramp_address = find_pool_onramp_address(program_id, &pool_address);
185 let stake_space = std::mem::size_of::<stake::state::StakeStateV2>();
186 let stake_rent = rent.minimum_balance(stake_space);
187 let stake_rent_plus_minimum = stake_rent.saturating_add(minimum_pool_balance);
188
189 let mint_address = find_pool_mint_address(program_id, &pool_address);
190 let mint_rent = rent.minimum_balance(spl_token::state::Mint::LEN);
191
192 vec![
193 system_instruction::transfer(payer, &pool_address, pool_rent),
194 system_instruction::transfer(payer, &stake_address, stake_rent_plus_minimum),
195 system_instruction::transfer(payer, &onramp_address, stake_rent),
196 system_instruction::transfer(payer, &mint_address, mint_rent),
197 initialize_pool(program_id, vote_account_address),
198 initialize_pool_onramp(program_id, &pool_address),
199 create_token_metadata(program_id, &pool_address, payer),
200 ]
201}
202
203pub fn initialize_pool(program_id: &Pubkey, vote_account_address: &Pubkey) -> Instruction {
205 let pool_address = find_pool_address(program_id, vote_account_address);
206 let mint_address = find_pool_mint_address(program_id, &pool_address);
207
208 let data = borsh::to_vec(&SinglePoolInstruction::InitializePool).unwrap();
209 let accounts = vec![
210 AccountMeta::new_readonly(*vote_account_address, false),
211 AccountMeta::new(pool_address, false),
212 AccountMeta::new(find_pool_stake_address(program_id, &pool_address), false),
213 AccountMeta::new(mint_address, false),
214 AccountMeta::new_readonly(
215 find_pool_stake_authority_address(program_id, &pool_address),
216 false,
217 ),
218 AccountMeta::new_readonly(
219 find_pool_mint_authority_address(program_id, &pool_address),
220 false,
221 ),
222 AccountMeta::new_readonly(sysvar::rent::id(), false),
223 AccountMeta::new_readonly(sysvar::clock::id(), false),
224 AccountMeta::new_readonly(sysvar::stake_history::id(), false),
225 #[allow(deprecated)]
226 AccountMeta::new_readonly(stake::config::id(), false),
227 AccountMeta::new_readonly(system_program::id(), false),
228 AccountMeta::new_readonly(spl_token::id(), false),
229 AccountMeta::new_readonly(stake::program::id(), false),
230 ];
231
232 Instruction {
233 program_id: *program_id,
234 accounts,
235 data,
236 }
237}
238
239pub fn replenish_pool(program_id: &Pubkey, vote_account_address: &Pubkey) -> Instruction {
241 let pool_address = find_pool_address(program_id, vote_account_address);
242
243 let data = borsh::to_vec(&SinglePoolInstruction::ReplenishPool).unwrap();
244 let accounts = vec![
245 AccountMeta::new_readonly(*vote_account_address, false),
246 AccountMeta::new_readonly(pool_address, false),
247 AccountMeta::new(find_pool_stake_address(program_id, &pool_address), false),
248 AccountMeta::new(find_pool_onramp_address(program_id, &pool_address), false),
249 AccountMeta::new_readonly(
250 find_pool_stake_authority_address(program_id, &pool_address),
251 false,
252 ),
253 AccountMeta::new_readonly(sysvar::clock::id(), false),
254 AccountMeta::new_readonly(sysvar::stake_history::id(), false),
255 #[allow(deprecated)]
256 AccountMeta::new_readonly(stake::config::id(), false),
257 AccountMeta::new_readonly(stake::program::id(), false),
258 ];
259
260 Instruction {
261 program_id: *program_id,
262 accounts,
263 data,
264 }
265}
266
267pub fn deposit(
269 program_id: &Pubkey,
270 pool_address: &Pubkey,
271 user_stake_account: &Pubkey,
272 user_token_account: &Pubkey,
273 user_lamport_account: &Pubkey,
274 user_withdraw_authority: &Pubkey,
275) -> Vec<Instruction> {
276 let pool_stake_authority = find_pool_stake_authority_address(program_id, pool_address);
277
278 vec![
279 stake::instruction::authorize(
280 user_stake_account,
281 user_withdraw_authority,
282 &pool_stake_authority,
283 stake::state::StakeAuthorize::Staker,
284 None,
285 ),
286 stake::instruction::authorize(
287 user_stake_account,
288 user_withdraw_authority,
289 &pool_stake_authority,
290 stake::state::StakeAuthorize::Withdrawer,
291 None,
292 ),
293 deposit_stake(
294 program_id,
295 pool_address,
296 user_stake_account,
297 user_token_account,
298 user_lamport_account,
299 ),
300 ]
301}
302
303pub fn deposit_stake(
305 program_id: &Pubkey,
306 pool_address: &Pubkey,
307 user_stake_account: &Pubkey,
308 user_token_account: &Pubkey,
309 user_lamport_account: &Pubkey,
310) -> Instruction {
311 let data = borsh::to_vec(&SinglePoolInstruction::DepositStake).unwrap();
312
313 let accounts = vec![
314 AccountMeta::new_readonly(*pool_address, false),
315 AccountMeta::new(find_pool_stake_address(program_id, pool_address), false),
316 AccountMeta::new(find_pool_mint_address(program_id, pool_address), false),
317 AccountMeta::new_readonly(
318 find_pool_stake_authority_address(program_id, pool_address),
319 false,
320 ),
321 AccountMeta::new_readonly(
322 find_pool_mint_authority_address(program_id, pool_address),
323 false,
324 ),
325 AccountMeta::new(*user_stake_account, false),
326 AccountMeta::new(*user_token_account, false),
327 AccountMeta::new(*user_lamport_account, false),
328 AccountMeta::new_readonly(sysvar::clock::id(), false),
329 AccountMeta::new_readonly(sysvar::stake_history::id(), false),
330 AccountMeta::new_readonly(spl_token::id(), false),
331 AccountMeta::new_readonly(stake::program::id(), false),
332 ];
333
334 Instruction {
335 program_id: *program_id,
336 accounts,
337 data,
338 }
339}
340
341pub fn withdraw(
347 program_id: &Pubkey,
348 pool_address: &Pubkey,
349 user_stake_account: &Pubkey,
350 user_stake_authority: &Pubkey,
351 user_token_account: &Pubkey,
352 user_token_authority: &Pubkey,
353 token_amount: u64,
354) -> Vec<Instruction> {
355 vec![
356 spl_token::instruction::approve(
357 &spl_token::id(),
358 user_token_account,
359 &find_pool_mint_authority_address(program_id, pool_address),
360 user_token_authority,
361 &[],
362 token_amount,
363 )
364 .unwrap(),
365 withdraw_stake(
366 program_id,
367 pool_address,
368 user_stake_account,
369 user_stake_authority,
370 user_token_account,
371 token_amount,
372 ),
373 ]
374}
375
376pub fn withdraw_stake(
378 program_id: &Pubkey,
379 pool_address: &Pubkey,
380 user_stake_account: &Pubkey,
381 user_stake_authority: &Pubkey,
382 user_token_account: &Pubkey,
383 token_amount: u64,
384) -> Instruction {
385 let data = borsh::to_vec(&SinglePoolInstruction::WithdrawStake {
386 user_stake_authority: *user_stake_authority,
387 token_amount,
388 })
389 .unwrap();
390
391 let accounts = vec![
392 AccountMeta::new_readonly(*pool_address, false),
393 AccountMeta::new(find_pool_stake_address(program_id, pool_address), false),
394 AccountMeta::new(find_pool_mint_address(program_id, pool_address), false),
395 AccountMeta::new_readonly(
396 find_pool_stake_authority_address(program_id, pool_address),
397 false,
398 ),
399 AccountMeta::new_readonly(
400 find_pool_mint_authority_address(program_id, pool_address),
401 false,
402 ),
403 AccountMeta::new(*user_stake_account, false),
404 AccountMeta::new(*user_token_account, false),
405 AccountMeta::new_readonly(sysvar::clock::id(), false),
406 AccountMeta::new_readonly(spl_token::id(), false),
407 AccountMeta::new_readonly(stake::program::id(), false),
408 ];
409
410 Instruction {
411 program_id: *program_id,
412 accounts,
413 data,
414 }
415}
416
417pub fn create_and_delegate_user_stake(
423 program_id: &Pubkey,
424 vote_account_address: &Pubkey,
425 user_wallet: &Pubkey,
426 rent: &Rent,
427 stake_amount: u64,
428) -> Vec<Instruction> {
429 let pool_address = find_pool_address(program_id, vote_account_address);
430 let stake_space = std::mem::size_of::<stake::state::StakeStateV2>();
431 let lamports = rent
432 .minimum_balance(stake_space)
433 .saturating_add(stake_amount);
434 let (deposit_address, deposit_seed) =
435 find_default_deposit_account_address_and_seed(&pool_address, user_wallet);
436
437 stake::instruction::create_account_with_seed_and_delegate_stake(
438 user_wallet,
439 &deposit_address,
440 user_wallet,
441 &deposit_seed,
442 vote_account_address,
443 &stake::state::Authorized::auto(user_wallet),
444 &stake::state::Lockup::default(),
445 lamports,
446 )
447}
448
449pub fn create_token_metadata(
451 program_id: &Pubkey,
452 pool_address: &Pubkey,
453 payer: &Pubkey,
454) -> Instruction {
455 let pool_mint = find_pool_mint_address(program_id, pool_address);
456 let (token_metadata, _) = find_metadata_account(&pool_mint);
457 let data = borsh::to_vec(&SinglePoolInstruction::CreateTokenMetadata).unwrap();
458
459 let accounts = vec![
460 AccountMeta::new_readonly(*pool_address, false),
461 AccountMeta::new_readonly(pool_mint, false),
462 AccountMeta::new_readonly(
463 find_pool_mint_authority_address(program_id, pool_address),
464 false,
465 ),
466 AccountMeta::new_readonly(
467 find_pool_mpl_authority_address(program_id, pool_address),
468 false,
469 ),
470 AccountMeta::new(*payer, true),
471 AccountMeta::new(token_metadata, false),
472 AccountMeta::new_readonly(inline_mpl_token_metadata::id(), false),
473 AccountMeta::new_readonly(system_program::id(), false),
474 ];
475
476 Instruction {
477 program_id: *program_id,
478 accounts,
479 data,
480 }
481}
482
483pub fn update_token_metadata(
485 program_id: &Pubkey,
486 vote_account_address: &Pubkey,
487 authorized_withdrawer: &Pubkey,
488 name: String,
489 symbol: String,
490 uri: String,
491) -> Instruction {
492 let pool_address = find_pool_address(program_id, vote_account_address);
493 let pool_mint = find_pool_mint_address(program_id, &pool_address);
494 let (token_metadata, _) = find_metadata_account(&pool_mint);
495 let data =
496 borsh::to_vec(&SinglePoolInstruction::UpdateTokenMetadata { name, symbol, uri }).unwrap();
497
498 let accounts = vec![
499 AccountMeta::new_readonly(*vote_account_address, false),
500 AccountMeta::new_readonly(pool_address, false),
501 AccountMeta::new_readonly(
502 find_pool_mpl_authority_address(program_id, &pool_address),
503 false,
504 ),
505 AccountMeta::new_readonly(*authorized_withdrawer, true),
506 AccountMeta::new(token_metadata, false),
507 AccountMeta::new_readonly(inline_mpl_token_metadata::id(), false),
508 ];
509
510 Instruction {
511 program_id: *program_id,
512 accounts,
513 data,
514 }
515}
516
517pub fn initialize_pool_onramp(program_id: &Pubkey, pool_address: &Pubkey) -> Instruction {
519 let data = borsh::to_vec(&SinglePoolInstruction::InitializePoolOnRamp).unwrap();
520 let accounts = vec![
521 AccountMeta::new_readonly(*pool_address, false),
522 AccountMeta::new(find_pool_onramp_address(program_id, pool_address), false),
523 AccountMeta::new_readonly(
524 find_pool_stake_authority_address(program_id, pool_address),
525 false,
526 ),
527 AccountMeta::new_readonly(sysvar::rent::id(), false),
528 AccountMeta::new_readonly(system_program::id(), false),
529 AccountMeta::new_readonly(stake::program::id(), false),
530 ];
531
532 Instruction {
533 program_id: *program_id,
534 accounts,
535 data,
536 }
537}
538
539pub fn create_pool_onramp(
543 program_id: &Pubkey,
544 pool_address: &Pubkey,
545 payer: &Pubkey,
546 rent: &Rent,
547) -> Vec<Instruction> {
548 let onramp_address = find_pool_onramp_address(program_id, pool_address);
549 let stake_space = std::mem::size_of::<stake::state::StakeStateV2>();
550 let stake_rent = rent.minimum_balance(stake_space);
551
552 vec![
553 system_instruction::transfer(payer, &onramp_address, stake_rent),
554 initialize_pool_onramp(program_id, pool_address),
555 ]
556}