#![allow(clippy::too_many_arguments)]
use {
crate::{
find_pool_address, find_pool_mint_address, find_pool_mint_authority_address,
find_pool_mpl_authority_address, find_pool_onramp_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_instruction::{AccountMeta, Instruction},
solana_program_pack::Pack,
solana_pubkey::Pubkey,
solana_rent::Rent,
solana_stake_interface::{self as stake, sysvar::stake_history},
solana_system_interface::{instruction as system_instruction, program as system_program},
solana_sysvar as sysvar, spl_token_interface as spl_token,
};
#[repr(C)]
#[derive(Clone, Debug, PartialEq, BorshSerialize, BorshDeserialize)]
pub enum SinglePoolInstruction {
InitializePool,
ReplenishPool,
DepositStake,
WithdrawStake {
user_stake_authority: Pubkey,
token_amount: u64,
},
CreateTokenMetadata,
UpdateTokenMetadata {
name: String,
symbol: String,
uri: String,
},
InitializePoolOnRamp,
DepositSol {
lamports: u64,
},
}
pub fn initialize(
program_id: &Pubkey,
vote_account_address: &Pubkey,
payer: &Pubkey,
rent: &Rent,
minimum_pool_balance: u64,
) -> Vec<Instruction> {
let pool_address = find_pool_address(program_id, vote_account_address);
let pool_rent = rent.minimum_balance(SinglePool::size_of());
let stake_address = find_pool_stake_address(program_id, &pool_address);
let onramp_address = find_pool_onramp_address(program_id, &pool_address);
let stake_space = stake::state::StakeStateV2::size_of();
let stake_rent = rent.minimum_balance(stake_space);
let stake_rent_plus_minimum = stake_rent.saturating_add(minimum_pool_balance);
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, &onramp_address, stake_rent),
system_instruction::transfer(payer, &mint_address, mint_rent),
initialize_pool(program_id, vote_account_address),
initialize_pool_onramp(program_id, &pool_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 = borsh::to_vec(&SinglePoolInstruction::InitializePool).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(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 replenish_pool(program_id: &Pubkey, vote_account_address: &Pubkey) -> Instruction {
let pool_address = find_pool_address(program_id, vote_account_address);
let data = borsh::to_vec(&SinglePoolInstruction::ReplenishPool).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(find_pool_onramp_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(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 = borsh::to_vec(&SinglePoolInstruction::DepositStake).unwrap();
let accounts = vec![
AccountMeta::new_readonly(*pool_address, false),
AccountMeta::new(find_pool_stake_address(program_id, pool_address), false),
AccountMeta::new_readonly(find_pool_onramp_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(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 = borsh::to_vec(&SinglePoolInstruction::WithdrawStake {
user_stake_authority: *user_stake_authority,
token_amount,
})
.unwrap();
let accounts = vec![
AccountMeta::new_readonly(*pool_address, false),
AccountMeta::new(find_pool_stake_address(program_id, pool_address), false),
AccountMeta::new_readonly(find_pool_onramp_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_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 = borsh::to_vec(&SinglePoolInstruction::CreateTokenMetadata).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 =
borsh::to_vec(&SinglePoolInstruction::UpdateTokenMetadata { name, symbol, uri }).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,
}
}
pub fn initialize_pool_onramp(program_id: &Pubkey, pool_address: &Pubkey) -> Instruction {
let data = borsh::to_vec(&SinglePoolInstruction::InitializePoolOnRamp).unwrap();
let accounts = vec![
AccountMeta::new_readonly(*pool_address, false),
AccountMeta::new(find_pool_onramp_address(program_id, pool_address), false),
AccountMeta::new_readonly(
find_pool_stake_authority_address(program_id, pool_address),
false,
),
AccountMeta::new_readonly(sysvar::rent::id(), false),
AccountMeta::new_readonly(system_program::id(), false),
AccountMeta::new_readonly(stake::program::id(), false),
];
Instruction {
program_id: *program_id,
accounts,
data,
}
}
pub fn create_pool_onramp(
program_id: &Pubkey,
pool_address: &Pubkey,
payer: &Pubkey,
rent: &Rent,
) -> Vec<Instruction> {
let onramp_address = find_pool_onramp_address(program_id, pool_address);
let stake_space = stake::state::StakeStateV2::size_of();
let stake_rent = rent.minimum_balance(stake_space);
vec![
system_instruction::transfer(payer, &onramp_address, stake_rent),
initialize_pool_onramp(program_id, pool_address),
]
}