#![allow(clippy::too_many_arguments)]
use {
crate::{
find_default_deposit_account_address_and_seed, find_pool_address, find_pool_mint_address,
find_pool_mint_authority_address, find_pool_mpl_authority_address, find_pool_stake_address,
find_pool_stake_authority_address,
inline_mpl_token_metadata::{self, pda::find_metadata_account},
state::SinglePool,
},
borsh::{BorshDeserialize, BorshSerialize},
solana_program::{
instruction::{AccountMeta, Instruction},
program_pack::Pack,
pubkey::Pubkey,
rent::Rent,
stake, system_instruction, system_program, sysvar,
},
};
#[repr(C)]
#[derive(Clone, Debug, PartialEq, BorshSerialize, BorshDeserialize)]
pub enum SinglePoolInstruction {
InitializePool,
ReactivatePoolStake,
DepositStake,
WithdrawStake {
user_stake_authority: Pubkey,
token_amount: u64,
},
CreateTokenMetadata,
UpdateTokenMetadata {
name: String,
symbol: String,
uri: String,
},
}
pub fn initialize(
program_id: &Pubkey,
vote_account_address: &Pubkey,
payer: &Pubkey,
rent: &Rent,
minimum_delegation: u64,
) -> Vec<Instruction> {
let pool_address = find_pool_address(program_id, vote_account_address);
let pool_rent = rent.minimum_balance(std::mem::size_of::<SinglePool>());
let stake_address = find_pool_stake_address(program_id, &pool_address);
let stake_space = std::mem::size_of::<stake::state::StakeStateV2>();
let stake_rent_plus_minimum = rent
.minimum_balance(stake_space)
.saturating_add(minimum_delegation);
let mint_address = find_pool_mint_address(program_id, &pool_address);
let mint_rent = rent.minimum_balance(spl_token::state::Mint::LEN);
vec![
system_instruction::transfer(payer, &pool_address, pool_rent),
system_instruction::transfer(payer, &stake_address, stake_rent_plus_minimum),
system_instruction::transfer(payer, &mint_address, mint_rent),
initialize_pool(program_id, vote_account_address),
create_token_metadata(program_id, &pool_address, payer),
]
}
pub fn initialize_pool(program_id: &Pubkey, vote_account_address: &Pubkey) -> Instruction {
let pool_address = find_pool_address(program_id, vote_account_address);
let mint_address = find_pool_mint_address(program_id, &pool_address);
let data = SinglePoolInstruction::InitializePool.try_to_vec().unwrap();
let accounts = vec![
AccountMeta::new_readonly(*vote_account_address, false),
AccountMeta::new(pool_address, false),
AccountMeta::new(find_pool_stake_address(program_id, &pool_address), false),
AccountMeta::new(mint_address, false),
AccountMeta::new_readonly(
find_pool_stake_authority_address(program_id, &pool_address),
false,
),
AccountMeta::new_readonly(
find_pool_mint_authority_address(program_id, &pool_address),
false,
),
AccountMeta::new_readonly(sysvar::rent::id(), false),
AccountMeta::new_readonly(sysvar::clock::id(), false),
AccountMeta::new_readonly(sysvar::stake_history::id(), false),
#[allow(deprecated)]
AccountMeta::new_readonly(stake::config::id(), false),
AccountMeta::new_readonly(system_program::id(), false),
AccountMeta::new_readonly(spl_token::id(), false),
AccountMeta::new_readonly(stake::program::id(), false),
];
Instruction {
program_id: *program_id,
accounts,
data,
}
}
pub fn reactivate_pool_stake(program_id: &Pubkey, vote_account_address: &Pubkey) -> Instruction {
let pool_address = find_pool_address(program_id, vote_account_address);
let data = SinglePoolInstruction::ReactivatePoolStake
.try_to_vec()
.unwrap();
let accounts = vec![
AccountMeta::new_readonly(*vote_account_address, false),
AccountMeta::new_readonly(pool_address, false),
AccountMeta::new(find_pool_stake_address(program_id, &pool_address), false),
AccountMeta::new_readonly(
find_pool_stake_authority_address(program_id, &pool_address),
false,
),
AccountMeta::new_readonly(sysvar::clock::id(), false),
AccountMeta::new_readonly(sysvar::stake_history::id(), false),
#[allow(deprecated)]
AccountMeta::new_readonly(stake::config::id(), false),
AccountMeta::new_readonly(stake::program::id(), false),
];
Instruction {
program_id: *program_id,
accounts,
data,
}
}
pub fn deposit(
program_id: &Pubkey,
pool_address: &Pubkey,
user_stake_account: &Pubkey,
user_token_account: &Pubkey,
user_lamport_account: &Pubkey,
user_withdraw_authority: &Pubkey,
) -> Vec<Instruction> {
let pool_stake_authority = find_pool_stake_authority_address(program_id, pool_address);
vec![
stake::instruction::authorize(
user_stake_account,
user_withdraw_authority,
&pool_stake_authority,
stake::state::StakeAuthorize::Staker,
None,
),
stake::instruction::authorize(
user_stake_account,
user_withdraw_authority,
&pool_stake_authority,
stake::state::StakeAuthorize::Withdrawer,
None,
),
deposit_stake(
program_id,
pool_address,
user_stake_account,
user_token_account,
user_lamport_account,
),
]
}
pub fn deposit_stake(
program_id: &Pubkey,
pool_address: &Pubkey,
user_stake_account: &Pubkey,
user_token_account: &Pubkey,
user_lamport_account: &Pubkey,
) -> Instruction {
let data = SinglePoolInstruction::DepositStake.try_to_vec().unwrap();
let accounts = vec![
AccountMeta::new_readonly(*pool_address, false),
AccountMeta::new(find_pool_stake_address(program_id, pool_address), false),
AccountMeta::new(find_pool_mint_address(program_id, pool_address), false),
AccountMeta::new_readonly(
find_pool_stake_authority_address(program_id, pool_address),
false,
),
AccountMeta::new_readonly(
find_pool_mint_authority_address(program_id, pool_address),
false,
),
AccountMeta::new(*user_stake_account, false),
AccountMeta::new(*user_token_account, false),
AccountMeta::new(*user_lamport_account, false),
AccountMeta::new_readonly(sysvar::clock::id(), false),
AccountMeta::new_readonly(sysvar::stake_history::id(), false),
AccountMeta::new_readonly(spl_token::id(), false),
AccountMeta::new_readonly(stake::program::id(), false),
];
Instruction {
program_id: *program_id,
accounts,
data,
}
}
pub fn withdraw(
program_id: &Pubkey,
pool_address: &Pubkey,
user_stake_account: &Pubkey,
user_stake_authority: &Pubkey,
user_token_account: &Pubkey,
user_token_authority: &Pubkey,
token_amount: u64,
) -> Vec<Instruction> {
vec![
spl_token::instruction::approve(
&spl_token::id(),
user_token_account,
&find_pool_mint_authority_address(program_id, pool_address),
user_token_authority,
&[],
token_amount,
)
.unwrap(),
withdraw_stake(
program_id,
pool_address,
user_stake_account,
user_stake_authority,
user_token_account,
token_amount,
),
]
}
pub fn withdraw_stake(
program_id: &Pubkey,
pool_address: &Pubkey,
user_stake_account: &Pubkey,
user_stake_authority: &Pubkey,
user_token_account: &Pubkey,
token_amount: u64,
) -> Instruction {
let data = SinglePoolInstruction::WithdrawStake {
user_stake_authority: *user_stake_authority,
token_amount,
}
.try_to_vec()
.unwrap();
let accounts = vec![
AccountMeta::new_readonly(*pool_address, false),
AccountMeta::new(find_pool_stake_address(program_id, pool_address), false),
AccountMeta::new(find_pool_mint_address(program_id, pool_address), false),
AccountMeta::new_readonly(
find_pool_stake_authority_address(program_id, pool_address),
false,
),
AccountMeta::new_readonly(
find_pool_mint_authority_address(program_id, pool_address),
false,
),
AccountMeta::new(*user_stake_account, false),
AccountMeta::new(*user_token_account, false),
AccountMeta::new_readonly(sysvar::clock::id(), false),
AccountMeta::new_readonly(spl_token::id(), false),
AccountMeta::new_readonly(stake::program::id(), false),
];
Instruction {
program_id: *program_id,
accounts,
data,
}
}
pub fn create_and_delegate_user_stake(
program_id: &Pubkey,
vote_account_address: &Pubkey,
user_wallet: &Pubkey,
rent: &Rent,
stake_amount: u64,
) -> Vec<Instruction> {
let pool_address = find_pool_address(program_id, vote_account_address);
let stake_space = std::mem::size_of::<stake::state::StakeStateV2>();
let lamports = rent
.minimum_balance(stake_space)
.saturating_add(stake_amount);
let (deposit_address, deposit_seed) =
find_default_deposit_account_address_and_seed(&pool_address, user_wallet);
stake::instruction::create_account_with_seed_and_delegate_stake(
user_wallet,
&deposit_address,
user_wallet,
&deposit_seed,
vote_account_address,
&stake::state::Authorized::auto(user_wallet),
&stake::state::Lockup::default(),
lamports,
)
}
pub fn create_token_metadata(
program_id: &Pubkey,
pool_address: &Pubkey,
payer: &Pubkey,
) -> Instruction {
let pool_mint = find_pool_mint_address(program_id, pool_address);
let (token_metadata, _) = find_metadata_account(&pool_mint);
let data = SinglePoolInstruction::CreateTokenMetadata
.try_to_vec()
.unwrap();
let accounts = vec![
AccountMeta::new_readonly(*pool_address, false),
AccountMeta::new_readonly(pool_mint, false),
AccountMeta::new_readonly(
find_pool_mint_authority_address(program_id, pool_address),
false,
),
AccountMeta::new_readonly(
find_pool_mpl_authority_address(program_id, pool_address),
false,
),
AccountMeta::new(*payer, true),
AccountMeta::new(token_metadata, false),
AccountMeta::new_readonly(inline_mpl_token_metadata::id(), false),
AccountMeta::new_readonly(system_program::id(), false),
];
Instruction {
program_id: *program_id,
accounts,
data,
}
}
pub fn update_token_metadata(
program_id: &Pubkey,
vote_account_address: &Pubkey,
authorized_withdrawer: &Pubkey,
name: String,
symbol: String,
uri: String,
) -> Instruction {
let pool_address = find_pool_address(program_id, vote_account_address);
let pool_mint = find_pool_mint_address(program_id, &pool_address);
let (token_metadata, _) = find_metadata_account(&pool_mint);
let data = SinglePoolInstruction::UpdateTokenMetadata { name, symbol, uri }
.try_to_vec()
.unwrap();
let accounts = vec![
AccountMeta::new_readonly(*vote_account_address, false),
AccountMeta::new_readonly(pool_address, false),
AccountMeta::new_readonly(
find_pool_mpl_authority_address(program_id, &pool_address),
false,
),
AccountMeta::new_readonly(*authorized_withdrawer, true),
AccountMeta::new(token_metadata, false),
AccountMeta::new_readonly(inline_mpl_token_metadata::id(), false),
];
Instruction {
program_id: *program_id,
accounts,
data,
}
}