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