Stake pool/liquid deposits (rebased on new master) (#2141)
* rename staking instructions * modify DepositStake te to include manager fees and referrer, continue _stake refactor, referral fees WIP * initialize with fees, fee application, checks * inline functions * temporarily substitute u8 for bool until borsh gets it's * straight * set deposit fee * apply deposit and referral fee to liquid deposit_sol too * add set-deposit-fee, unify cli interface * set-referral-fee * full feature set for liquid deposits (?) * add tests/set_referral_fee.rs * fix missing serialization in process_set_referral_fee * remove duplicated test case in tests/set_withdrawal_fee.rs * tests WIP, numbers dont add up after non-zero deposit fee * fix error, fix tests * deposit_sol tests. Requires additional changes to work properly * simplify deposit_sol tests, add referral fee tests for deposit and deposit_sol * add `sol_deposit_authority`. * split deposit_sol() & deposit_sol_with_authority(), cli sol_deposit --from, minor cleanup * cli deposit-sol from argument should take keypair instead * commands: set-sol-deposit-authority, show * cli: pretty print stake pool struct * chore: comments/naming * fmt, clippy * add args for `create-pool` * mistake in the cli * `system_prog` is `read_only`, require sig from `stake_deposit_auth` * change deposit-sol-authority arg to optional acount, rename instruction::withdraw->withdraw_stake, remove unnecesary sys_prog arg for withdraw_stake * resolve simple nits and suggestions * cli: change default referrer to user's token receiver account instead of pool manager * cli: remove show command, add fees to list command, rename pool -> epoch * update tests for removed unnecessary referral fee account owner check and deposit sol * cli changes: create ephemeral account for deposit_sol * specify pool token type for account info name * add check for manager fee acc in deposit_sol * Apply suggestions from code review Co-authored-by: Jon Cinque <jon.cinque@gmail.com> * fix non-rebased bug * SetDepositStakeAuthority * refactor + tests + cli * fmt * clippy * remove unnecessary comment * ASK keyword * unset deposit auth * combine set fee instructions * clippy * applying suggestions * apply out-of-date check only for FeeTypes that need it * add fee + user = new tokens check * Fix test * Unify `SetDepositAuthority` instruction * fmt Co-authored-by: dhy1996 <dhy1996@live.com.sg> Co-authored-by: Jesse Y. Cho <f8122dac91@gmail.com> Co-authored-by: Jon Cinque <jon.cinque@gmail.com>
This commit is contained in:
parent
0d8a248786
commit
87d88bd078
|
@ -47,7 +47,7 @@ deposit_stakes () {
|
||||||
for validator in $(cat $validator_list)
|
for validator in $(cat $validator_list)
|
||||||
do
|
do
|
||||||
stake=$(solana-keygen pubkey $keys_dir/stake_$validator.json)
|
stake=$(solana-keygen pubkey $keys_dir/stake_$validator.json)
|
||||||
$spl_stake_pool deposit $stake_pool_pubkey $stake
|
$spl_stake_pool deposit-stake $stake_pool_pubkey $stake
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,7 +57,7 @@ withdraw_stakes () {
|
||||||
pool_amount=$3
|
pool_amount=$3
|
||||||
for validator in $(cat $validator_list)
|
for validator in $(cat $validator_list)
|
||||||
do
|
do
|
||||||
$spl_stake_pool withdraw $stake_pool_pubkey $pool_amount --vote-account $validator
|
$spl_stake_pool withdraw-stake $stake_pool_pubkey $pool_amount --vote-account $validator
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@ use {
|
||||||
input_parsers::{keypair_of, pubkey_of},
|
input_parsers::{keypair_of, pubkey_of},
|
||||||
input_validators::{
|
input_validators::{
|
||||||
is_amount, is_keypair, is_keypair_or_ask_keyword, is_parsable, is_pubkey, is_url,
|
is_amount, is_keypair, is_keypair_or_ask_keyword, is_parsable, is_pubkey, is_url,
|
||||||
|
is_valid_percentage,
|
||||||
},
|
},
|
||||||
keypair::signer_from_path,
|
keypair::signer_from_path,
|
||||||
},
|
},
|
||||||
|
@ -38,7 +39,7 @@ use {
|
||||||
find_withdraw_authority_program_address,
|
find_withdraw_authority_program_address,
|
||||||
instruction::PreferredValidatorType,
|
instruction::PreferredValidatorType,
|
||||||
stake_program::{self, StakeState},
|
stake_program::{self, StakeState},
|
||||||
state::{Fee, StakePool, ValidatorList},
|
state::{Fee, FeeType, StakePool, ValidatorList},
|
||||||
},
|
},
|
||||||
std::{process::exit, sync::Arc},
|
std::{process::exit, sync::Arc},
|
||||||
};
|
};
|
||||||
|
@ -49,6 +50,7 @@ struct Config {
|
||||||
manager: Box<dyn Signer>,
|
manager: Box<dyn Signer>,
|
||||||
staker: Box<dyn Signer>,
|
staker: Box<dyn Signer>,
|
||||||
depositor: Option<Box<dyn Signer>>,
|
depositor: Option<Box<dyn Signer>>,
|
||||||
|
sol_depositor: Option<Box<dyn Signer>>,
|
||||||
token_owner: Box<dyn Signer>,
|
token_owner: Box<dyn Signer>,
|
||||||
fee_payer: Box<dyn Signer>,
|
fee_payer: Box<dyn Signer>,
|
||||||
dry_run: bool,
|
dry_run: bool,
|
||||||
|
@ -153,9 +155,11 @@ fn checked_transaction_with_signers<T: Signers>(
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn command_create_pool(
|
fn command_create_pool(
|
||||||
config: &Config,
|
config: &Config,
|
||||||
deposit_authority: Option<Pubkey>,
|
stake_deposit_authority: Option<Keypair>,
|
||||||
fee: Fee,
|
fee: Fee,
|
||||||
withdrawal_fee: Fee,
|
withdrawal_fee: Fee,
|
||||||
|
stake_deposit_fee: Fee,
|
||||||
|
stake_referral_fee: u8,
|
||||||
max_validators: u32,
|
max_validators: u32,
|
||||||
stake_pool_keypair: Option<Keypair>,
|
stake_pool_keypair: Option<Keypair>,
|
||||||
mint_keypair: Option<Keypair>,
|
mint_keypair: Option<Keypair>,
|
||||||
|
@ -283,9 +287,11 @@ fn command_create_pool(
|
||||||
&mint_keypair.pubkey(),
|
&mint_keypair.pubkey(),
|
||||||
&pool_fee_account,
|
&pool_fee_account,
|
||||||
&spl_token::id(),
|
&spl_token::id(),
|
||||||
deposit_authority,
|
stake_deposit_authority.as_ref().map(|x| x.pubkey()),
|
||||||
fee,
|
fee,
|
||||||
withdrawal_fee,
|
withdrawal_fee,
|
||||||
|
stake_deposit_fee,
|
||||||
|
stake_referral_fee,
|
||||||
max_validators,
|
max_validators,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -311,8 +317,15 @@ fn command_create_pool(
|
||||||
&validator_list,
|
&validator_list,
|
||||||
config.manager.as_ref(),
|
config.manager.as_ref(),
|
||||||
];
|
];
|
||||||
|
if let Some(stake_deposit_authority) = stake_deposit_authority {
|
||||||
|
let mut initialize_signers = initialize_signers.clone();
|
||||||
|
initialize_signers.push(&stake_deposit_authority);
|
||||||
unique_signers!(initialize_signers);
|
unique_signers!(initialize_signers);
|
||||||
initialize_transaction.sign(&initialize_signers, recent_blockhash);
|
initialize_transaction.sign(&initialize_signers, recent_blockhash);
|
||||||
|
} else {
|
||||||
|
unique_signers!(initialize_signers);
|
||||||
|
initialize_transaction.sign(&initialize_signers, recent_blockhash);
|
||||||
|
}
|
||||||
send_transaction(config, initialize_transaction)?;
|
send_transaction(config, initialize_transaction)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -567,11 +580,12 @@ fn add_associated_token_account(
|
||||||
account
|
account
|
||||||
}
|
}
|
||||||
|
|
||||||
fn command_deposit(
|
fn command_deposit_stake(
|
||||||
config: &Config,
|
config: &Config,
|
||||||
stake_pool_address: &Pubkey,
|
stake_pool_address: &Pubkey,
|
||||||
stake: &Pubkey,
|
stake: &Pubkey,
|
||||||
token_receiver: &Option<Pubkey>,
|
pool_token_receiver_account: &Option<Pubkey>,
|
||||||
|
referrer_token_account: &Option<Pubkey>,
|
||||||
) -> CommandResult {
|
) -> CommandResult {
|
||||||
if !config.no_update {
|
if !config.no_update {
|
||||||
command_update(config, stake_pool_address, false, false)?;
|
command_update(config, stake_pool_address, false, false)?;
|
||||||
|
@ -613,7 +627,8 @@ fn command_deposit(
|
||||||
let mut total_rent_free_balances: u64 = 0;
|
let mut total_rent_free_balances: u64 = 0;
|
||||||
|
|
||||||
// Create token account if not specified
|
// Create token account if not specified
|
||||||
let token_receiver = token_receiver.unwrap_or(add_associated_token_account(
|
let pool_token_receiver_account =
|
||||||
|
pool_token_receiver_account.unwrap_or(add_associated_token_account(
|
||||||
config,
|
config,
|
||||||
&stake_pool.pool_mint,
|
&stake_pool.pool_mint,
|
||||||
&config.token_owner.pubkey(),
|
&config.token_owner.pubkey(),
|
||||||
|
@ -621,36 +636,41 @@ fn command_deposit(
|
||||||
&mut total_rent_free_balances,
|
&mut total_rent_free_balances,
|
||||||
));
|
));
|
||||||
|
|
||||||
|
let referrer_token_account = referrer_token_account.unwrap_or(pool_token_receiver_account);
|
||||||
|
|
||||||
let pool_withdraw_authority =
|
let pool_withdraw_authority =
|
||||||
find_withdraw_authority_program_address(&spl_stake_pool::id(), stake_pool_address).0;
|
find_withdraw_authority_program_address(&spl_stake_pool::id(), stake_pool_address).0;
|
||||||
|
|
||||||
let mut deposit_instructions = if let Some(deposit_authority) = config.depositor.as_ref() {
|
let mut deposit_instructions = if let Some(stake_deposit_authority) = config.depositor.as_ref()
|
||||||
signers.push(deposit_authority.as_ref());
|
{
|
||||||
if deposit_authority.pubkey() != stake_pool.deposit_authority {
|
signers.push(stake_deposit_authority.as_ref());
|
||||||
|
if stake_deposit_authority.pubkey() != stake_pool.stake_deposit_authority {
|
||||||
let error = format!(
|
let error = format!(
|
||||||
"Invalid deposit authority specified, expected {}, received {}",
|
"Invalid deposit authority specified, expected {}, received {}",
|
||||||
stake_pool.deposit_authority,
|
stake_pool.stake_deposit_authority,
|
||||||
deposit_authority.pubkey()
|
stake_deposit_authority.pubkey()
|
||||||
);
|
);
|
||||||
return Err(error.into());
|
return Err(error.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
spl_stake_pool::instruction::deposit_with_authority(
|
spl_stake_pool::instruction::deposit_stake_with_authority(
|
||||||
&spl_stake_pool::id(),
|
&spl_stake_pool::id(),
|
||||||
stake_pool_address,
|
stake_pool_address,
|
||||||
&stake_pool.validator_list,
|
&stake_pool.validator_list,
|
||||||
&deposit_authority.pubkey(),
|
&stake_deposit_authority.pubkey(),
|
||||||
&pool_withdraw_authority,
|
&pool_withdraw_authority,
|
||||||
stake,
|
stake,
|
||||||
&config.staker.pubkey(),
|
&config.staker.pubkey(),
|
||||||
&validator_stake_account,
|
&validator_stake_account,
|
||||||
&stake_pool.reserve_stake,
|
&stake_pool.reserve_stake,
|
||||||
&token_receiver,
|
&pool_token_receiver_account,
|
||||||
|
&stake_pool.manager_fee_account,
|
||||||
|
&referrer_token_account,
|
||||||
&stake_pool.pool_mint,
|
&stake_pool.pool_mint,
|
||||||
&spl_token::id(),
|
&spl_token::id(),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
spl_stake_pool::instruction::deposit(
|
spl_stake_pool::instruction::deposit_stake(
|
||||||
&spl_stake_pool::id(),
|
&spl_stake_pool::id(),
|
||||||
stake_pool_address,
|
stake_pool_address,
|
||||||
&stake_pool.validator_list,
|
&stake_pool.validator_list,
|
||||||
|
@ -659,7 +679,9 @@ fn command_deposit(
|
||||||
&config.staker.pubkey(),
|
&config.staker.pubkey(),
|
||||||
&validator_stake_account,
|
&validator_stake_account,
|
||||||
&stake_pool.reserve_stake,
|
&stake_pool.reserve_stake,
|
||||||
&token_receiver,
|
&pool_token_receiver_account,
|
||||||
|
&stake_pool.manager_fee_account,
|
||||||
|
&referrer_token_account,
|
||||||
&stake_pool.pool_mint,
|
&stake_pool.pool_mint,
|
||||||
&spl_token::id(),
|
&spl_token::id(),
|
||||||
)
|
)
|
||||||
|
@ -681,6 +703,136 @@ fn command_deposit(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn command_deposit_sol(
|
||||||
|
config: &Config,
|
||||||
|
stake_pool_address: &Pubkey,
|
||||||
|
from: &Option<Keypair>,
|
||||||
|
pool_token_receiver_account: &Option<Pubkey>,
|
||||||
|
referrer_token_account: &Option<Pubkey>,
|
||||||
|
amount: f64,
|
||||||
|
) -> CommandResult {
|
||||||
|
if !config.no_update {
|
||||||
|
command_update(config, stake_pool_address, false, false)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let amount = native_token::sol_to_lamports(amount);
|
||||||
|
|
||||||
|
// Check withdraw_from balance
|
||||||
|
let from_pubkey = from.as_ref().map_or_else(
|
||||||
|
|| config.fee_payer.try_pubkey().unwrap(),
|
||||||
|
|keypair| keypair.try_pubkey().unwrap(),
|
||||||
|
);
|
||||||
|
let from_balance = config.rpc_client.get_balance(&from_pubkey)?;
|
||||||
|
if from_balance < amount {
|
||||||
|
return Err(format!(
|
||||||
|
"Not enough SOL to deposit into pool: {}.\nMaximum deposit amount is {} SOL.",
|
||||||
|
Sol(amount),
|
||||||
|
Sol(from_balance)
|
||||||
|
)
|
||||||
|
.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
let stake_pool = get_stake_pool(&config.rpc_client, stake_pool_address)?;
|
||||||
|
|
||||||
|
let mut instructions: Vec<Instruction> = vec![];
|
||||||
|
|
||||||
|
// ephemeral SOL account just to do the transfer
|
||||||
|
let user_sol_transfer = Keypair::new();
|
||||||
|
let mut signers = vec![
|
||||||
|
config.fee_payer.as_ref(),
|
||||||
|
config.staker.as_ref(),
|
||||||
|
&user_sol_transfer,
|
||||||
|
];
|
||||||
|
if let Some(keypair) = from.as_ref() {
|
||||||
|
signers.push(keypair)
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut total_rent_free_balances: u64 = 0;
|
||||||
|
|
||||||
|
// Create the ephemeral SOL account
|
||||||
|
instructions.push(system_instruction::transfer(
|
||||||
|
&from_pubkey,
|
||||||
|
&user_sol_transfer.pubkey(),
|
||||||
|
amount,
|
||||||
|
));
|
||||||
|
|
||||||
|
// Create token account if not specified
|
||||||
|
let pool_token_receiver_account =
|
||||||
|
pool_token_receiver_account.unwrap_or(add_associated_token_account(
|
||||||
|
config,
|
||||||
|
&stake_pool.pool_mint,
|
||||||
|
&config.token_owner.pubkey(),
|
||||||
|
&mut instructions,
|
||||||
|
&mut total_rent_free_balances,
|
||||||
|
));
|
||||||
|
|
||||||
|
let referrer_token_account = referrer_token_account.unwrap_or(pool_token_receiver_account);
|
||||||
|
|
||||||
|
let pool_withdraw_authority =
|
||||||
|
find_withdraw_authority_program_address(&spl_stake_pool::id(), stake_pool_address).0;
|
||||||
|
|
||||||
|
let mut deposit_instructions = if let Some(sol_deposit_authority) =
|
||||||
|
config.sol_depositor.as_ref()
|
||||||
|
{
|
||||||
|
let expected_sol_deposit_authority = stake_pool.sol_deposit_authority.ok_or_else(|| {
|
||||||
|
"SOL deposit authority specified in arguments but stake pool has none".to_string()
|
||||||
|
})?;
|
||||||
|
signers.push(sol_deposit_authority.as_ref());
|
||||||
|
if sol_deposit_authority.pubkey() != expected_sol_deposit_authority {
|
||||||
|
let error = format!(
|
||||||
|
"Invalid deposit authority specified, expected {}, received {}",
|
||||||
|
expected_sol_deposit_authority,
|
||||||
|
sol_deposit_authority.pubkey()
|
||||||
|
);
|
||||||
|
return Err(error.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
spl_stake_pool::instruction::deposit_sol_with_authority(
|
||||||
|
&spl_stake_pool::id(),
|
||||||
|
stake_pool_address,
|
||||||
|
&sol_deposit_authority.pubkey(),
|
||||||
|
&pool_withdraw_authority,
|
||||||
|
&stake_pool.reserve_stake,
|
||||||
|
&user_sol_transfer.pubkey(),
|
||||||
|
&pool_token_receiver_account,
|
||||||
|
&stake_pool.manager_fee_account,
|
||||||
|
&referrer_token_account,
|
||||||
|
&stake_pool.pool_mint,
|
||||||
|
&spl_token::id(),
|
||||||
|
amount,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
spl_stake_pool::instruction::deposit_sol(
|
||||||
|
&spl_stake_pool::id(),
|
||||||
|
stake_pool_address,
|
||||||
|
&pool_withdraw_authority,
|
||||||
|
&stake_pool.reserve_stake,
|
||||||
|
&user_sol_transfer.pubkey(),
|
||||||
|
&pool_token_receiver_account,
|
||||||
|
&stake_pool.manager_fee_account,
|
||||||
|
&referrer_token_account,
|
||||||
|
&stake_pool.pool_mint,
|
||||||
|
&spl_token::id(),
|
||||||
|
amount,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
instructions.append(&mut deposit_instructions);
|
||||||
|
|
||||||
|
let mut transaction =
|
||||||
|
Transaction::new_with_payer(&instructions, Some(&config.fee_payer.pubkey()));
|
||||||
|
|
||||||
|
let (recent_blockhash, fee_calculator) = config.rpc_client.get_recent_blockhash()?;
|
||||||
|
check_fee_payer_balance(
|
||||||
|
config,
|
||||||
|
total_rent_free_balances + fee_calculator.calculate_fee(transaction.message()),
|
||||||
|
)?;
|
||||||
|
unique_signers!(signers);
|
||||||
|
transaction.sign(&signers, recent_blockhash);
|
||||||
|
send_transaction(config, transaction)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn command_list(config: &Config, stake_pool_address: &Pubkey) -> CommandResult {
|
fn command_list(config: &Config, stake_pool_address: &Pubkey) -> CommandResult {
|
||||||
let stake_pool = get_stake_pool(&config.rpc_client, stake_pool_address)?;
|
let stake_pool = get_stake_pool(&config.rpc_client, stake_pool_address)?;
|
||||||
let validator_list = get_validator_list(&config.rpc_client, &stake_pool.validator_list)?;
|
let validator_list = get_validator_list(&config.rpc_client, &stake_pool.validator_list)?;
|
||||||
|
@ -688,6 +840,9 @@ fn command_list(config: &Config, stake_pool_address: &Pubkey) -> CommandResult {
|
||||||
let epoch_info = config.rpc_client.get_epoch_info()?;
|
let epoch_info = config.rpc_client.get_epoch_info()?;
|
||||||
let pool_withdraw_authority =
|
let pool_withdraw_authority =
|
||||||
find_withdraw_authority_program_address(&spl_stake_pool::id(), stake_pool_address).0;
|
find_withdraw_authority_program_address(&spl_stake_pool::id(), stake_pool_address).0;
|
||||||
|
let sol_deposit_authority = stake_pool
|
||||||
|
.sol_deposit_authority
|
||||||
|
.map_or("None".into(), |pubkey| pubkey.to_string());
|
||||||
|
|
||||||
if config.verbose {
|
if config.verbose {
|
||||||
println!("Stake Pool Info");
|
println!("Stake Pool Info");
|
||||||
|
@ -696,7 +851,8 @@ fn command_list(config: &Config, stake_pool_address: &Pubkey) -> CommandResult {
|
||||||
println!("Validator List: {}", stake_pool.validator_list);
|
println!("Validator List: {}", stake_pool.validator_list);
|
||||||
println!("Manager: {}", stake_pool.manager);
|
println!("Manager: {}", stake_pool.manager);
|
||||||
println!("Staker: {}", stake_pool.staker);
|
println!("Staker: {}", stake_pool.staker);
|
||||||
println!("Depositor: {}", stake_pool.deposit_authority);
|
println!("Depositor: {}", stake_pool.stake_deposit_authority);
|
||||||
|
println!("SOL Deposit Authority: {}", sol_deposit_authority);
|
||||||
println!("Withdraw Authority: {}", pool_withdraw_authority);
|
println!("Withdraw Authority: {}", pool_withdraw_authority);
|
||||||
println!("Pool Token Mint: {}", stake_pool.pool_mint);
|
println!("Pool Token Mint: {}", stake_pool.pool_mint);
|
||||||
println!("Fee Account: {}", stake_pool.manager_fee_account);
|
println!("Fee Account: {}", stake_pool.manager_fee_account);
|
||||||
|
@ -718,13 +874,52 @@ fn command_list(config: &Config, stake_pool_address: &Pubkey) -> CommandResult {
|
||||||
preferred_withdraw_validator
|
preferred_withdraw_validator
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if stake_pool.fee.numerator > 0 {
|
|
||||||
|
// Display fees information
|
||||||
|
if stake_pool.fee.numerator > 0 && stake_pool.fee.denominator > 0 {
|
||||||
|
println!("Epoch Fee: {} of epoch rewards", stake_pool.fee);
|
||||||
|
} else {
|
||||||
|
println!("Epoch Fee: none");
|
||||||
|
}
|
||||||
|
if stake_pool.withdrawal_fee.numerator > 0 && stake_pool.withdrawal_fee.denominator > 0 {
|
||||||
println!(
|
println!(
|
||||||
"Fee: {}/{} of epoch rewards",
|
"Withdrawal Fee: {} of withdrawal amount",
|
||||||
stake_pool.fee.numerator, stake_pool.fee.denominator
|
stake_pool.withdrawal_fee
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
println!("Fee: none");
|
println!("Withdrawal Fee: none");
|
||||||
|
}
|
||||||
|
if stake_pool.stake_deposit_fee.numerator > 0 && stake_pool.stake_deposit_fee.denominator > 0 {
|
||||||
|
println!(
|
||||||
|
"Stake Deposit Fee: {} of staked amount",
|
||||||
|
stake_pool.stake_deposit_fee
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
println!("Stake Deposit Fee: none");
|
||||||
|
}
|
||||||
|
if stake_pool.sol_deposit_fee.numerator > 0 && stake_pool.sol_deposit_fee.denominator > 0 {
|
||||||
|
println!(
|
||||||
|
"SOL Deposit Fee: {} of deposit amount",
|
||||||
|
stake_pool.sol_deposit_fee
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
println!("SOL Deposit Fee: none");
|
||||||
|
}
|
||||||
|
if stake_pool.sol_referral_fee > 0 {
|
||||||
|
println!(
|
||||||
|
"SOL Deposit Referral Fee: {}% of SOL Deposit Fee",
|
||||||
|
stake_pool.sol_referral_fee
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
println!("SOL Deposit Referral Fee: none");
|
||||||
|
}
|
||||||
|
if stake_pool.stake_referral_fee > 0 {
|
||||||
|
println!(
|
||||||
|
"Stake Deposit Referral Fee: {}% of Stake Deposit Fee",
|
||||||
|
stake_pool.stake_referral_fee
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
println!("Stake Deposit Referral Fee: none");
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.verbose {
|
if config.verbose {
|
||||||
|
@ -1079,7 +1274,7 @@ fn command_withdraw(
|
||||||
stake_receiver_pubkey
|
stake_receiver_pubkey
|
||||||
});
|
});
|
||||||
|
|
||||||
instructions.push(spl_stake_pool::instruction::withdraw(
|
instructions.push(spl_stake_pool::instruction::withdraw_stake(
|
||||||
&spl_stake_pool::id(),
|
&spl_stake_pool::id(),
|
||||||
stake_pool_address,
|
stake_pool_address,
|
||||||
&stake_pool.validator_list,
|
&stake_pool.validator_list,
|
||||||
|
@ -1179,16 +1374,22 @@ fn command_set_staker(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn command_set_fee(config: &Config, stake_pool_address: &Pubkey, new_fee: Fee) -> CommandResult {
|
fn command_set_deposit_authority(
|
||||||
|
config: &Config,
|
||||||
|
stake_pool_address: &Pubkey,
|
||||||
|
new_sol_deposit_authority: Option<Pubkey>,
|
||||||
|
for_stake_deposit: bool,
|
||||||
|
) -> CommandResult {
|
||||||
let mut signers = vec![config.fee_payer.as_ref(), config.manager.as_ref()];
|
let mut signers = vec![config.fee_payer.as_ref(), config.manager.as_ref()];
|
||||||
unique_signers!(signers);
|
unique_signers!(signers);
|
||||||
let transaction = checked_transaction_with_signers(
|
let transaction = checked_transaction_with_signers(
|
||||||
config,
|
config,
|
||||||
&[spl_stake_pool::instruction::set_fee(
|
&[spl_stake_pool::instruction::set_deposit_authority(
|
||||||
&spl_stake_pool::id(),
|
&spl_stake_pool::id(),
|
||||||
stake_pool_address,
|
stake_pool_address,
|
||||||
&config.manager.pubkey(),
|
&config.manager.pubkey(),
|
||||||
new_fee,
|
new_sol_deposit_authority.as_ref(),
|
||||||
|
for_stake_deposit,
|
||||||
)],
|
)],
|
||||||
&signers,
|
&signers,
|
||||||
)?;
|
)?;
|
||||||
|
@ -1196,16 +1397,16 @@ fn command_set_fee(config: &Config, stake_pool_address: &Pubkey, new_fee: Fee) -
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn command_set_withdrawal_fee(
|
fn command_set_fee(
|
||||||
config: &Config,
|
config: &Config,
|
||||||
stake_pool_address: &Pubkey,
|
stake_pool_address: &Pubkey,
|
||||||
new_fee: Fee,
|
new_fee: FeeType,
|
||||||
) -> CommandResult {
|
) -> CommandResult {
|
||||||
let mut signers = vec![config.fee_payer.as_ref(), config.manager.as_ref()];
|
let mut signers = vec![config.fee_payer.as_ref(), config.manager.as_ref()];
|
||||||
unique_signers!(signers);
|
unique_signers!(signers);
|
||||||
let transaction = checked_transaction_with_signers(
|
let transaction = checked_transaction_with_signers(
|
||||||
config,
|
config,
|
||||||
&[spl_stake_pool::instruction::set_withdrawal_fee(
|
&[spl_stake_pool::instruction::set_fee(
|
||||||
&spl_stake_pool::id(),
|
&spl_stake_pool::id(),
|
||||||
stake_pool_address,
|
stake_pool_address,
|
||||||
&config.manager.pubkey(),
|
&config.manager.pubkey(),
|
||||||
|
@ -1366,6 +1567,31 @@ fn main() {
|
||||||
.requires("withdrawal_fee_numerator")
|
.requires("withdrawal_fee_numerator")
|
||||||
.help("Withdrawal fee denominator, fee amount is numerator divided by denominator [default: 0]"),
|
.help("Withdrawal fee denominator, fee amount is numerator divided by denominator [default: 0]"),
|
||||||
)
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("deposit_fee_numerator")
|
||||||
|
.long("deposit-fee-numerator")
|
||||||
|
.validator(is_parsable::<u64>)
|
||||||
|
.value_name("NUMERATOR")
|
||||||
|
.takes_value(true)
|
||||||
|
.requires("deposit_fee_denominator")
|
||||||
|
.help("Deposit fee numerator, fee amount is numerator divided by denominator [default: 0]"),
|
||||||
|
).arg(
|
||||||
|
Arg::with_name("deposit_fee_denominator")
|
||||||
|
.long("deposit-fee-denominator")
|
||||||
|
.validator(is_parsable::<u64>)
|
||||||
|
.value_name("DENOMINATOR")
|
||||||
|
.takes_value(true)
|
||||||
|
.requires("deposit_fee_numerator")
|
||||||
|
.help("Deposit fee denominator, fee amount is numerator divided by denominator [default: 0]"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("referral_fee")
|
||||||
|
.long("referral-fee")
|
||||||
|
.validator(is_valid_percentage)
|
||||||
|
.value_name("FEE_PERCENTAGE")
|
||||||
|
.takes_value(true)
|
||||||
|
.help("Referral fee percentage, maximum 100"),
|
||||||
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name("max_validators")
|
Arg::with_name("max_validators")
|
||||||
.long("max-validators")
|
.long("max-validators")
|
||||||
|
@ -1377,11 +1603,11 @@ fn main() {
|
||||||
.help("Max number of validators included in the stake pool"),
|
.help("Max number of validators included in the stake pool"),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name("deposit_authority")
|
Arg::with_name("stake_deposit_authority")
|
||||||
.long("deposit-authority")
|
.long("stake-deposit-authority")
|
||||||
.short("a")
|
.short("a")
|
||||||
.validator(is_pubkey)
|
.validator(is_keypair_or_ask_keyword)
|
||||||
.value_name("DEPOSIT_AUTHORITY_ADDRESS")
|
.value_name("STAKE_DEPOSIT_AUTHORITY_KEYPAIR")
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
.help("Deposit authority required to sign all deposits into the stake pool"),
|
.help("Deposit authority required to sign all deposits into the stake pool"),
|
||||||
)
|
)
|
||||||
|
@ -1581,8 +1807,8 @@ fn main() {
|
||||||
.required(true)
|
.required(true)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.subcommand(SubCommand::with_name("deposit")
|
.subcommand(SubCommand::with_name("deposit-stake")
|
||||||
.about("Add stake account to the stake pool")
|
.about("Deposit active stake account into the stake pool in exchange for pool tokens")
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name("pool")
|
Arg::with_name("pool")
|
||||||
.index(1)
|
.index(1)
|
||||||
|
@ -1611,6 +1837,61 @@ fn main() {
|
||||||
Defaults to the token-owner's associated pool token account. \
|
Defaults to the token-owner's associated pool token account. \
|
||||||
Creates the account if it does not exist."),
|
Creates the account if it does not exist."),
|
||||||
)
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("referrer")
|
||||||
|
.validator(is_pubkey)
|
||||||
|
.value_name("ADDRESS")
|
||||||
|
.takes_value(true)
|
||||||
|
.help("Pool token account to receive the referral fees for deposits. \
|
||||||
|
Defaults to the token receiver."),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.subcommand(SubCommand::with_name("deposit-sol")
|
||||||
|
.about("Deposit SOL into the stake pool in exchange for pool tokens")
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("pool")
|
||||||
|
.index(1)
|
||||||
|
.validator(is_pubkey)
|
||||||
|
.value_name("POOL_ADDRESS")
|
||||||
|
.takes_value(true)
|
||||||
|
.required(true)
|
||||||
|
.help("Stake pool address"),
|
||||||
|
).arg(
|
||||||
|
Arg::with_name("amount")
|
||||||
|
.index(2)
|
||||||
|
.validator(is_amount)
|
||||||
|
.value_name("AMOUNT")
|
||||||
|
.takes_value(true)
|
||||||
|
.help("Amount in SOL to deposit into the stake pool reserve account."),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("from")
|
||||||
|
.long("from")
|
||||||
|
.validator(is_keypair_or_ask_keyword)
|
||||||
|
.value_name("KEYPAIR")
|
||||||
|
.takes_value(true)
|
||||||
|
.help("Source account of funds. \
|
||||||
|
Defaults to the fee payer."),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("token_receiver")
|
||||||
|
.long("token-receiver")
|
||||||
|
.validator(is_pubkey)
|
||||||
|
.value_name("POOL_TOKEN_RECEIVER_ADDRESS")
|
||||||
|
.takes_value(true)
|
||||||
|
.help("Account to receive the minted pool tokens. \
|
||||||
|
Defaults to the token-owner's associated pool token account. \
|
||||||
|
Creates the account if it does not exist."),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("referrer")
|
||||||
|
.long("referrer")
|
||||||
|
.validator(is_pubkey)
|
||||||
|
.value_name("REFERRER_TOKEN_ADDRESS")
|
||||||
|
.takes_value(true)
|
||||||
|
.help("Account to receive the referral fees for deposits. \
|
||||||
|
Defaults to the token receiver."),
|
||||||
|
)
|
||||||
)
|
)
|
||||||
.subcommand(SubCommand::with_name("list")
|
.subcommand(SubCommand::with_name("list")
|
||||||
.about("List stake accounts managed by this pool")
|
.about("List stake accounts managed by this pool")
|
||||||
|
@ -1648,7 +1929,7 @@ fn main() {
|
||||||
.help("Do not automatically merge transient stakes. Useful if the stake pool is in an expected state, but the balances still need to be updated."),
|
.help("Do not automatically merge transient stakes. Useful if the stake pool is in an expected state, but the balances still need to be updated."),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.subcommand(SubCommand::with_name("withdraw")
|
.subcommand(SubCommand::with_name("withdraw-stake")
|
||||||
.about("Withdraw amount from the stake pool")
|
.about("Withdraw amount from the stake pool")
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name("pool")
|
Arg::with_name("pool")
|
||||||
|
@ -1758,8 +2039,108 @@ fn main() {
|
||||||
.help("Public key for the new stake pool staker."),
|
.help("Public key for the new stake pool staker."),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
.subcommand(SubCommand::with_name("set-sol-deposit-authority")
|
||||||
|
.about("Change sol deposit authority account for the stake pool. Must be signed by the manager.")
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("pool")
|
||||||
|
.index(1)
|
||||||
|
.validator(is_pubkey)
|
||||||
|
.value_name("POOL_ADDRESS")
|
||||||
|
.takes_value(true)
|
||||||
|
.required(true)
|
||||||
|
.help("Stake pool address."),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("new_sol_deposit_authority")
|
||||||
|
.index(2)
|
||||||
|
.validator(is_pubkey)
|
||||||
|
.value_name("ADDRESS")
|
||||||
|
.takes_value(true)
|
||||||
|
.help("The public key for the new stake pool sol deposit authority."),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("unset")
|
||||||
|
.long("unset")
|
||||||
|
.takes_value(false)
|
||||||
|
.help("Unset the sol deposit authority.")
|
||||||
|
)
|
||||||
|
.group(ArgGroup::with_name("validator")
|
||||||
|
.arg("new_sol_deposit_authority")
|
||||||
|
.arg("unset")
|
||||||
|
.required(true)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.subcommand(SubCommand::with_name("set-stake-deposit-authority")
|
||||||
|
.about("Change stake deposit authority account for the stake pool. Must be signed by the manager.")
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("pool")
|
||||||
|
.index(1)
|
||||||
|
.validator(is_pubkey)
|
||||||
|
.value_name("POOL_ADDRESS")
|
||||||
|
.takes_value(true)
|
||||||
|
.required(true)
|
||||||
|
.help("Stake pool address."),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("new_stake_deposit_authority")
|
||||||
|
.index(2)
|
||||||
|
.validator(is_pubkey)
|
||||||
|
.value_name("ADDRESS_OR_NONE")
|
||||||
|
.takes_value(true)
|
||||||
|
.help("'none', or a public key for the new stake pool sol deposit authority."),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("unset")
|
||||||
|
.long("unset")
|
||||||
|
.takes_value(false)
|
||||||
|
.help("Unset the stake deposit authority. The program will use a program derived address.")
|
||||||
|
)
|
||||||
|
.group(ArgGroup::with_name("validator")
|
||||||
|
.arg("new_stake_deposit_authority")
|
||||||
|
.arg("unset")
|
||||||
|
.required(true)
|
||||||
|
)
|
||||||
|
)
|
||||||
.subcommand(SubCommand::with_name("set-fee")
|
.subcommand(SubCommand::with_name("set-fee")
|
||||||
.about("Change the fee assessed by the stake pool. Must be signed by the manager.")
|
.about("Change the [management/withdrawal/stake deposit/sol deposit] fee assessed by the stake pool. Must be signed by the manager.")
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("pool")
|
||||||
|
.index(1)
|
||||||
|
.validator(is_pubkey)
|
||||||
|
.value_name("POOL_ADDRESS")
|
||||||
|
.takes_value(true)
|
||||||
|
.required(true)
|
||||||
|
.help("Stake pool address."),
|
||||||
|
)
|
||||||
|
.arg(Arg::with_name("fee_type")
|
||||||
|
.index(2)
|
||||||
|
.value_name("FEE_TYPE")
|
||||||
|
.possible_values(&["epoch", "stake-deposit", "sol-deposit", "withdrawal"]) // PreferredValidatorType enum
|
||||||
|
.takes_value(true)
|
||||||
|
.required(true)
|
||||||
|
.help("Fee type to be updated."),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("fee_numerator")
|
||||||
|
.index(3)
|
||||||
|
.validator(is_parsable::<u64>)
|
||||||
|
.value_name("NUMERATOR")
|
||||||
|
.takes_value(true)
|
||||||
|
.required(true)
|
||||||
|
.help("Fee numerator, fee amount is numerator divided by denominator."),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("fee_denominator")
|
||||||
|
.index(4)
|
||||||
|
.validator(is_parsable::<u64>)
|
||||||
|
.value_name("DENOMINATOR")
|
||||||
|
.takes_value(true)
|
||||||
|
.required(true)
|
||||||
|
.help("Fee denominator, fee amount is numerator divided by denominator."),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.subcommand(SubCommand::with_name("set-stake-referral-fee")
|
||||||
|
.about("Change the referral fee assessed by the stake pool for stake deposits. Must be signed by the manager.")
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name("pool")
|
Arg::with_name("pool")
|
||||||
.index(1)
|
.index(1)
|
||||||
|
@ -1770,26 +2151,16 @@ fn main() {
|
||||||
.help("Stake pool address."),
|
.help("Stake pool address."),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name("fee_numerator")
|
Arg::with_name("fee")
|
||||||
.index(2)
|
.index(2)
|
||||||
.validator(is_parsable::<u64>)
|
.validator(is_valid_percentage)
|
||||||
.value_name("NUMERATOR")
|
.value_name("FEE_PERCENTAGE")
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
.required(true)
|
.required(true)
|
||||||
.help("Fee numerator, fee amount is numerator divided by denominator."),
|
.help("Fee percentage, maximum 100"),
|
||||||
)
|
)
|
||||||
.arg(
|
).subcommand(SubCommand::with_name("set-sol-referral-fee")
|
||||||
Arg::with_name("fee_denominator")
|
.about("Change the referral fee assessed by the stake pool for SOL deposits. Must be signed by the manager.")
|
||||||
.index(3)
|
|
||||||
.validator(is_parsable::<u64>)
|
|
||||||
.value_name("DENOMINATOR")
|
|
||||||
.takes_value(true)
|
|
||||||
.required(true)
|
|
||||||
.help("Fee denominator, fee amount is numerator divided by denominator."),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.subcommand(SubCommand::with_name("set-withdrawal-fee")
|
|
||||||
.about("Change the withdrawal fee assessed by the stake pool. Must be signed by the manager.")
|
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name("pool")
|
Arg::with_name("pool")
|
||||||
.index(1)
|
.index(1)
|
||||||
|
@ -1800,22 +2171,13 @@ fn main() {
|
||||||
.help("Stake pool address."),
|
.help("Stake pool address."),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name("fee_numerator")
|
Arg::with_name("fee")
|
||||||
.index(2)
|
.index(2)
|
||||||
.validator(is_parsable::<u64>)
|
.validator(is_valid_percentage)
|
||||||
.value_name("NUMERATOR")
|
.value_name("FEE_PERCENTAGE")
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
.required(true)
|
.required(true)
|
||||||
.help("Fee numerator, fee amount is numerator divided by denominator."),
|
.help("Fee percentage, maximum 100"),
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name("fee_denominator")
|
|
||||||
.index(3)
|
|
||||||
.validator(is_parsable::<u64>)
|
|
||||||
.value_name("DENOMINATOR")
|
|
||||||
.takes_value(true)
|
|
||||||
.required(true)
|
|
||||||
.help("Fee denominator, fee amount is numerator divided by denominator."),
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.get_matches();
|
.get_matches();
|
||||||
|
@ -1847,6 +2209,16 @@ fn main() {
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
let sol_depositor = if matches.is_present("sol_depositor") {
|
||||||
|
Some(get_signer(
|
||||||
|
&matches,
|
||||||
|
"sol_depositor",
|
||||||
|
&cli_config.keypair_path,
|
||||||
|
&mut wallet_manager,
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
let manager = get_signer(
|
let manager = get_signer(
|
||||||
&matches,
|
&matches,
|
||||||
"manager",
|
"manager",
|
||||||
|
@ -1875,6 +2247,7 @@ fn main() {
|
||||||
manager,
|
manager,
|
||||||
staker,
|
staker,
|
||||||
depositor,
|
depositor,
|
||||||
|
sol_depositor,
|
||||||
token_owner,
|
token_owner,
|
||||||
fee_payer,
|
fee_payer,
|
||||||
dry_run,
|
dry_run,
|
||||||
|
@ -1884,18 +2257,21 @@ fn main() {
|
||||||
|
|
||||||
let _ = match matches.subcommand() {
|
let _ = match matches.subcommand() {
|
||||||
("create-pool", Some(arg_matches)) => {
|
("create-pool", Some(arg_matches)) => {
|
||||||
let deposit_authority = pubkey_of(arg_matches, "deposit_authority");
|
let stake_deposit_authority = keypair_of(arg_matches, "stake_deposit_authority");
|
||||||
let numerator = value_t_or_exit!(arg_matches, "fee_numerator", u64);
|
let numerator = value_t_or_exit!(arg_matches, "fee_numerator", u64);
|
||||||
let denominator = value_t_or_exit!(arg_matches, "fee_denominator", u64);
|
let denominator = value_t_or_exit!(arg_matches, "fee_denominator", u64);
|
||||||
let w_numerator = value_t!(arg_matches, "withdrawal_fee_numerator", u64);
|
let w_numerator = value_t!(arg_matches, "withdrawal_fee_numerator", u64);
|
||||||
let w_denominator = value_t!(arg_matches, "withdrawal_fee_denominator", u64);
|
let w_denominator = value_t!(arg_matches, "withdrawal_fee_denominator", u64);
|
||||||
|
let d_numerator = value_t!(arg_matches, "deposit_fee_numerator", u64);
|
||||||
|
let d_denominator = value_t!(arg_matches, "deposit_fee_denominator", u64);
|
||||||
|
let referral_fee = value_t!(arg_matches, "referral_fee", u8);
|
||||||
let max_validators = value_t_or_exit!(arg_matches, "max_validators", u32);
|
let max_validators = value_t_or_exit!(arg_matches, "max_validators", u32);
|
||||||
let pool_keypair = keypair_of(arg_matches, "pool_keypair");
|
let pool_keypair = keypair_of(arg_matches, "pool_keypair");
|
||||||
let mint_keypair = keypair_of(arg_matches, "mint_keypair");
|
let mint_keypair = keypair_of(arg_matches, "mint_keypair");
|
||||||
let reserve_keypair = keypair_of(arg_matches, "reserve_keypair");
|
let reserve_keypair = keypair_of(arg_matches, "reserve_keypair");
|
||||||
command_create_pool(
|
command_create_pool(
|
||||||
&config,
|
&config,
|
||||||
deposit_authority,
|
stake_deposit_authority,
|
||||||
Fee {
|
Fee {
|
||||||
denominator,
|
denominator,
|
||||||
numerator,
|
numerator,
|
||||||
|
@ -1904,6 +2280,11 @@ fn main() {
|
||||||
numerator: w_numerator.unwrap_or(0),
|
numerator: w_numerator.unwrap_or(0),
|
||||||
denominator: w_denominator.unwrap_or(0),
|
denominator: w_denominator.unwrap_or(0),
|
||||||
},
|
},
|
||||||
|
Fee {
|
||||||
|
numerator: d_numerator.unwrap_or(0),
|
||||||
|
denominator: d_denominator.unwrap_or(0),
|
||||||
|
},
|
||||||
|
referral_fee.unwrap_or(0),
|
||||||
max_validators,
|
max_validators,
|
||||||
pool_keypair,
|
pool_keypair,
|
||||||
mint_keypair,
|
mint_keypair,
|
||||||
|
@ -1956,15 +2337,32 @@ fn main() {
|
||||||
vote_account,
|
vote_account,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
("deposit", Some(arg_matches)) => {
|
("deposit-stake", Some(arg_matches)) => {
|
||||||
let stake_pool_address = pubkey_of(arg_matches, "pool").unwrap();
|
let stake_pool_address = pubkey_of(arg_matches, "pool").unwrap();
|
||||||
let stake_account = pubkey_of(arg_matches, "stake_account").unwrap();
|
let stake_account = pubkey_of(arg_matches, "stake_account").unwrap();
|
||||||
let token_receiver: Option<Pubkey> = pubkey_of(arg_matches, "token_receiver");
|
let token_receiver: Option<Pubkey> = pubkey_of(arg_matches, "token_receiver");
|
||||||
command_deposit(
|
let referrer: Option<Pubkey> = pubkey_of(arg_matches, "referrer");
|
||||||
|
command_deposit_stake(
|
||||||
&config,
|
&config,
|
||||||
&stake_pool_address,
|
&stake_pool_address,
|
||||||
&stake_account,
|
&stake_account,
|
||||||
&token_receiver,
|
&token_receiver,
|
||||||
|
&referrer,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
("deposit-sol", Some(arg_matches)) => {
|
||||||
|
let stake_pool_address = pubkey_of(arg_matches, "pool").unwrap();
|
||||||
|
let token_receiver: Option<Pubkey> = pubkey_of(arg_matches, "token_receiver");
|
||||||
|
let referrer: Option<Pubkey> = pubkey_of(arg_matches, "referrer");
|
||||||
|
let from = keypair_of(arg_matches, "from");
|
||||||
|
let amount = value_t_or_exit!(arg_matches, "amount", f64);
|
||||||
|
command_deposit_sol(
|
||||||
|
&config,
|
||||||
|
&stake_pool_address,
|
||||||
|
&from,
|
||||||
|
&token_receiver,
|
||||||
|
&referrer,
|
||||||
|
amount,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
("list", Some(arg_matches)) => {
|
("list", Some(arg_matches)) => {
|
||||||
|
@ -1977,7 +2375,7 @@ fn main() {
|
||||||
let force = arg_matches.is_present("force");
|
let force = arg_matches.is_present("force");
|
||||||
command_update(&config, &stake_pool_address, force, no_merge)
|
command_update(&config, &stake_pool_address, force, no_merge)
|
||||||
}
|
}
|
||||||
("withdraw", Some(arg_matches)) => {
|
("withdraw-stake", Some(arg_matches)) => {
|
||||||
let stake_pool_address = pubkey_of(arg_matches, "pool").unwrap();
|
let stake_pool_address = pubkey_of(arg_matches, "pool").unwrap();
|
||||||
let vote_account = pubkey_of(arg_matches, "vote_account");
|
let vote_account = pubkey_of(arg_matches, "vote_account");
|
||||||
let pool_account = pubkey_of(arg_matches, "pool_account");
|
let pool_account = pubkey_of(arg_matches, "pool_account");
|
||||||
|
@ -2010,6 +2408,28 @@ fn main() {
|
||||||
let new_staker = pubkey_of(arg_matches, "new_staker").unwrap();
|
let new_staker = pubkey_of(arg_matches, "new_staker").unwrap();
|
||||||
command_set_staker(&config, &stake_pool_address, &new_staker)
|
command_set_staker(&config, &stake_pool_address, &new_staker)
|
||||||
}
|
}
|
||||||
|
("set-stake-deposit-authority", Some(arg_matches)) => {
|
||||||
|
let stake_pool_address = pubkey_of(arg_matches, "pool").unwrap();
|
||||||
|
let new_stake_deposit_authority = pubkey_of(arg_matches, "new_stake_deposit_authority");
|
||||||
|
let _unset = arg_matches.is_present("unset");
|
||||||
|
command_set_deposit_authority(
|
||||||
|
&config,
|
||||||
|
&stake_pool_address,
|
||||||
|
new_stake_deposit_authority,
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
("set-sol-deposit-authority", Some(arg_matches)) => {
|
||||||
|
let stake_pool_address = pubkey_of(arg_matches, "pool").unwrap();
|
||||||
|
let new_sol_deposit_authority = pubkey_of(arg_matches, "new_sol_deposit_authority");
|
||||||
|
let _unset = arg_matches.is_present("unset");
|
||||||
|
command_set_deposit_authority(
|
||||||
|
&config,
|
||||||
|
&stake_pool_address,
|
||||||
|
new_sol_deposit_authority,
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
}
|
||||||
("set-fee", Some(arg_matches)) => {
|
("set-fee", Some(arg_matches)) => {
|
||||||
let stake_pool_address = pubkey_of(arg_matches, "pool").unwrap();
|
let stake_pool_address = pubkey_of(arg_matches, "pool").unwrap();
|
||||||
let numerator = value_t_or_exit!(arg_matches, "fee_numerator", u64);
|
let numerator = value_t_or_exit!(arg_matches, "fee_numerator", u64);
|
||||||
|
@ -2018,17 +2438,35 @@ fn main() {
|
||||||
denominator,
|
denominator,
|
||||||
numerator,
|
numerator,
|
||||||
};
|
};
|
||||||
command_set_fee(&config, &stake_pool_address, new_fee)
|
match arg_matches.value_of("fee_type").unwrap() {
|
||||||
|
"epoch" => command_set_fee(&config, &stake_pool_address, FeeType::Epoch(new_fee)),
|
||||||
|
"stake-deposit" => {
|
||||||
|
command_set_fee(&config, &stake_pool_address, FeeType::StakeDeposit(new_fee))
|
||||||
}
|
}
|
||||||
("set-withdrawal-fee", Some(arg_matches)) => {
|
"sol-deposit" => {
|
||||||
|
command_set_fee(&config, &stake_pool_address, FeeType::SolDeposit(new_fee))
|
||||||
|
}
|
||||||
|
"withdrawal" => {
|
||||||
|
command_set_fee(&config, &stake_pool_address, FeeType::Withdrawal(new_fee))
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
("set-stake-referral-fee", Some(arg_matches)) => {
|
||||||
let stake_pool_address = pubkey_of(arg_matches, "pool").unwrap();
|
let stake_pool_address = pubkey_of(arg_matches, "pool").unwrap();
|
||||||
let numerator = value_t_or_exit!(arg_matches, "fee_numerator", u64);
|
let fee = value_t_or_exit!(arg_matches, "fee", u8);
|
||||||
let denominator = value_t_or_exit!(arg_matches, "fee_denominator", u64);
|
if fee > 100u8 {
|
||||||
let new_fee = Fee {
|
panic!("Invalid fee {}%. Fee needs to be in range [0-100]", fee);
|
||||||
denominator,
|
}
|
||||||
numerator,
|
command_set_fee(&config, &stake_pool_address, FeeType::StakeReferral(fee))
|
||||||
};
|
}
|
||||||
command_set_withdrawal_fee(&config, &stake_pool_address, new_fee)
|
("set-sol-referral-fee", Some(arg_matches)) => {
|
||||||
|
let stake_pool_address = pubkey_of(arg_matches, "pool").unwrap();
|
||||||
|
let fee = value_t_or_exit!(arg_matches, "fee", u8);
|
||||||
|
if fee > 100u8 {
|
||||||
|
panic!("Invalid fee {}%. Fee needs to be in range [0-100]", fee);
|
||||||
|
}
|
||||||
|
command_set_fee(&config, &stake_pool_address, FeeType::SolReferral(fee))
|
||||||
}
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
|
|
|
@ -108,6 +108,14 @@ pub enum StakePoolError {
|
||||||
/// Not enough lamports provided for deposit to result in one pool token
|
/// Not enough lamports provided for deposit to result in one pool token
|
||||||
#[error("DepositTooSmall")]
|
#[error("DepositTooSmall")]
|
||||||
DepositTooSmall,
|
DepositTooSmall,
|
||||||
|
|
||||||
|
// 30.
|
||||||
|
/// Provided stake deposit authority does not match the program's
|
||||||
|
#[error("FeeIncreaseTooHigh")]
|
||||||
|
InvalidStakeDepositAuthority,
|
||||||
|
/// Provided sol deposit authority does not match the program's
|
||||||
|
#[error("InvalidSolDepositAuthority")]
|
||||||
|
InvalidSolDepositAuthority,
|
||||||
}
|
}
|
||||||
impl From<StakePoolError> for ProgramError {
|
impl From<StakePoolError> for ProgramError {
|
||||||
fn from(e: StakePoolError) -> Self {
|
fn from(e: StakePoolError) -> Self {
|
||||||
|
|
|
@ -6,7 +6,7 @@ use {
|
||||||
find_deposit_authority_program_address, find_stake_program_address,
|
find_deposit_authority_program_address, find_stake_program_address,
|
||||||
find_transient_stake_program_address, find_withdraw_authority_program_address,
|
find_transient_stake_program_address, find_withdraw_authority_program_address,
|
||||||
stake_program,
|
stake_program,
|
||||||
state::{Fee, StakePool, ValidatorList},
|
state::{Fee, FeeType, StakePool, ValidatorList},
|
||||||
MAX_VALIDATORS_TO_UPDATE,
|
MAX_VALIDATORS_TO_UPDATE,
|
||||||
},
|
},
|
||||||
borsh::{BorshDeserialize, BorshSchema, BorshSerialize},
|
borsh::{BorshDeserialize, BorshSchema, BorshSerialize},
|
||||||
|
@ -28,6 +28,17 @@ pub enum PreferredValidatorType {
|
||||||
Withdraw,
|
Withdraw,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Defines which validator vote account is set during the
|
||||||
|
/// `SetPreferredValidator` instruction
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Debug, PartialEq, BorshSerialize, BorshDeserialize, BorshSchema)]
|
||||||
|
pub enum DepositType {
|
||||||
|
/// Set preferred validator for deposits
|
||||||
|
Stake,
|
||||||
|
/// Set preferred validator for withdraws
|
||||||
|
Sol,
|
||||||
|
}
|
||||||
|
|
||||||
/// Instructions supported by the StakePool program.
|
/// Instructions supported by the StakePool program.
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Clone, Debug, PartialEq, BorshSerialize, BorshDeserialize, BorshSchema)]
|
#[derive(Clone, Debug, PartialEq, BorshSerialize, BorshDeserialize, BorshSchema)]
|
||||||
|
@ -55,6 +66,12 @@ pub enum StakePoolInstruction {
|
||||||
/// Fee charged per withdrawal as percentage of withdrawal
|
/// Fee charged per withdrawal as percentage of withdrawal
|
||||||
#[allow(dead_code)] // but it's not
|
#[allow(dead_code)] // but it's not
|
||||||
withdrawal_fee: Fee,
|
withdrawal_fee: Fee,
|
||||||
|
/// Fee charged per deposit as percentage of deposit
|
||||||
|
#[allow(dead_code)] // but it's not
|
||||||
|
deposit_fee: Fee,
|
||||||
|
/// Percentage [0-100] of deposit_fee that goes to referrer
|
||||||
|
#[allow(dead_code)] // but it's not
|
||||||
|
referral_fee: u8,
|
||||||
/// Maximum expected number of validators
|
/// Maximum expected number of validators
|
||||||
#[allow(dead_code)] // but it's not
|
#[allow(dead_code)] // but it's not
|
||||||
max_validators: u32,
|
max_validators: u32,
|
||||||
|
@ -244,18 +261,20 @@ pub enum StakePoolInstruction {
|
||||||
///
|
///
|
||||||
/// 0. `[w]` Stake pool
|
/// 0. `[w]` Stake pool
|
||||||
/// 1. `[w]` Validator stake list storage account
|
/// 1. `[w]` Validator stake list storage account
|
||||||
/// 2. `[]` Stake pool deposit authority
|
/// 2. `[s]/[]` Stake pool deposit authority
|
||||||
/// 3. `[]` Stake pool withdraw authority
|
/// 3. `[]` Stake pool withdraw authority
|
||||||
/// 4. `[w]` Stake account to join the pool (withdraw authority for the stake account should be first set to the stake pool deposit authority)
|
/// 4. `[w]` Stake account to join the pool (withdraw authority for the stake account should be first set to the stake pool deposit authority)
|
||||||
/// 5. `[w]` Validator stake account for the stake account to be merged with
|
/// 5. `[w]` Validator stake account for the stake account to be merged with
|
||||||
/// 6. `[w]` Reserve stake account, to withdraw rent exempt reserve
|
/// 6. `[w]` Reserve stake account, to withdraw rent exempt reserve
|
||||||
/// 7. `[w]` User account to receive pool tokens
|
/// 7. `[w]` User account to receive pool tokens
|
||||||
/// 8. `[w]` Pool token mint account
|
/// 8. `[w]` Account to receive pool fee tokens
|
||||||
/// 9. '[]' Sysvar clock account
|
/// 9. `[w]` Account to receive a portion of pool fee tokens as referral fees
|
||||||
/// 10. '[]' Sysvar stake history account
|
/// 10. `[w]` Pool token mint account
|
||||||
/// 11. `[]` Pool token program id,
|
/// 11. '[]' Sysvar clock account
|
||||||
/// 12. `[]` Stake program id,
|
/// 12. '[]' Sysvar stake history account
|
||||||
Deposit,
|
/// 13. `[]` Pool token program id,
|
||||||
|
/// 14. `[]` Stake program id,
|
||||||
|
DepositStake,
|
||||||
|
|
||||||
/// Withdraw the token from the pool at the current ratio.
|
/// Withdraw the token from the pool at the current ratio.
|
||||||
///
|
///
|
||||||
|
@ -281,7 +300,7 @@ pub enum StakePoolInstruction {
|
||||||
/// 11. `[]` Pool token program id
|
/// 11. `[]` Pool token program id
|
||||||
/// 12. `[]` Stake program id,
|
/// 12. `[]` Stake program id,
|
||||||
/// userdata: amount of pool tokens to withdraw
|
/// userdata: amount of pool tokens to withdraw
|
||||||
Withdraw(u64),
|
WithdrawStake(u64),
|
||||||
|
|
||||||
/// (Manager only) Update manager
|
/// (Manager only) Update manager
|
||||||
///
|
///
|
||||||
|
@ -297,9 +316,9 @@ pub enum StakePoolInstruction {
|
||||||
/// 1. `[s]` Manager
|
/// 1. `[s]` Manager
|
||||||
/// 2. `[]` Sysvar clock
|
/// 2. `[]` Sysvar clock
|
||||||
SetFee {
|
SetFee {
|
||||||
/// Fee assessed as percentage of perceived rewards
|
/// Type of fee to update and value to update it to
|
||||||
#[allow(dead_code)] // but it's not
|
#[allow(dead_code)] // but it's not
|
||||||
fee: Fee,
|
fee: FeeType,
|
||||||
},
|
},
|
||||||
|
|
||||||
/// (Manager or staker only) Update staker
|
/// (Manager or staker only) Update staker
|
||||||
|
@ -309,16 +328,29 @@ pub enum StakePoolInstruction {
|
||||||
/// 2. '[]` New staker pubkey
|
/// 2. '[]` New staker pubkey
|
||||||
SetStaker,
|
SetStaker,
|
||||||
|
|
||||||
/// (Manager only) Update Withdrawal fee for next epoch
|
/// Deposit SOL directly into the pool's reserve account. The output is a "pool" token
|
||||||
|
/// representing ownership into the pool. Inputs are converted to the current ratio.
|
||||||
|
///
|
||||||
|
/// 0. `[w]` Stake pool
|
||||||
|
/// 1. `[s]/[]` Stake pool sol deposit authority.
|
||||||
|
/// 2. `[]` Stake pool withdraw authority
|
||||||
|
/// 3. `[w]` Reserve stake account, to withdraw rent exempt reserve
|
||||||
|
/// 4. `[s]` Account providing the lamports to be deposited into the pool
|
||||||
|
/// 5. `[w]` User account to receive pool tokens
|
||||||
|
/// 6. `[w]` Account to receive pool fee tokens
|
||||||
|
/// 7. `[w]` Account to receive a portion of pool fee tokens as referral fees
|
||||||
|
/// 8. `[w]` Pool token mint account
|
||||||
|
/// 9. '[]' Sysvar clock account
|
||||||
|
/// 10 `[]` System program account
|
||||||
|
/// 11. `[]` Pool token program id,
|
||||||
|
DepositSol(u64),
|
||||||
|
|
||||||
|
/// (Manager only) Update SOL deposit authority
|
||||||
///
|
///
|
||||||
/// 0. `[w]` StakePool
|
/// 0. `[w]` StakePool
|
||||||
/// 1. `[s]` Manager
|
/// 1. `[s]` Manager
|
||||||
/// 2. `[]` Sysvar clock
|
/// 2. '[]` New sol_deposit_authority pubkey or none
|
||||||
SetWithdrawalFee {
|
SetDepositAuthority(DepositType),
|
||||||
/// Fee assessed as percentage of perceived rewards
|
|
||||||
#[allow(dead_code)] // but it's not
|
|
||||||
fee: Fee,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates an 'initialize' instruction.
|
/// Creates an 'initialize' instruction.
|
||||||
|
@ -335,11 +367,15 @@ pub fn initialize(
|
||||||
deposit_authority: Option<Pubkey>,
|
deposit_authority: Option<Pubkey>,
|
||||||
fee: Fee,
|
fee: Fee,
|
||||||
withdrawal_fee: Fee,
|
withdrawal_fee: Fee,
|
||||||
|
deposit_fee: Fee,
|
||||||
|
referral_fee: u8,
|
||||||
max_validators: u32,
|
max_validators: u32,
|
||||||
) -> Instruction {
|
) -> Instruction {
|
||||||
let init_data = StakePoolInstruction::Initialize {
|
let init_data = StakePoolInstruction::Initialize {
|
||||||
fee,
|
fee,
|
||||||
withdrawal_fee,
|
withdrawal_fee,
|
||||||
|
deposit_fee,
|
||||||
|
referral_fee,
|
||||||
max_validators,
|
max_validators,
|
||||||
};
|
};
|
||||||
let data = init_data.try_to_vec().unwrap();
|
let data = init_data.try_to_vec().unwrap();
|
||||||
|
@ -826,7 +862,7 @@ pub fn update_stake_pool(
|
||||||
|
|
||||||
/// Creates instructions required to deposit into a stake pool, given a stake
|
/// Creates instructions required to deposit into a stake pool, given a stake
|
||||||
/// account owned by the user.
|
/// account owned by the user.
|
||||||
pub fn deposit(
|
pub fn deposit_stake(
|
||||||
program_id: &Pubkey,
|
program_id: &Pubkey,
|
||||||
stake_pool: &Pubkey,
|
stake_pool: &Pubkey,
|
||||||
validator_list_storage: &Pubkey,
|
validator_list_storage: &Pubkey,
|
||||||
|
@ -836,6 +872,8 @@ pub fn deposit(
|
||||||
validator_stake_account: &Pubkey,
|
validator_stake_account: &Pubkey,
|
||||||
reserve_stake_account: &Pubkey,
|
reserve_stake_account: &Pubkey,
|
||||||
pool_tokens_to: &Pubkey,
|
pool_tokens_to: &Pubkey,
|
||||||
|
manager_fee_account: &Pubkey,
|
||||||
|
referrer_pool_tokens_account: &Pubkey,
|
||||||
pool_mint: &Pubkey,
|
pool_mint: &Pubkey,
|
||||||
token_program_id: &Pubkey,
|
token_program_id: &Pubkey,
|
||||||
) -> Vec<Instruction> {
|
) -> Vec<Instruction> {
|
||||||
|
@ -850,6 +888,8 @@ pub fn deposit(
|
||||||
AccountMeta::new(*validator_stake_account, false),
|
AccountMeta::new(*validator_stake_account, false),
|
||||||
AccountMeta::new(*reserve_stake_account, false),
|
AccountMeta::new(*reserve_stake_account, false),
|
||||||
AccountMeta::new(*pool_tokens_to, false),
|
AccountMeta::new(*pool_tokens_to, false),
|
||||||
|
AccountMeta::new(*manager_fee_account, false),
|
||||||
|
AccountMeta::new(*referrer_pool_tokens_account, false),
|
||||||
AccountMeta::new(*pool_mint, false),
|
AccountMeta::new(*pool_mint, false),
|
||||||
AccountMeta::new_readonly(sysvar::clock::id(), false),
|
AccountMeta::new_readonly(sysvar::clock::id(), false),
|
||||||
AccountMeta::new_readonly(sysvar::stake_history::id(), false),
|
AccountMeta::new_readonly(sysvar::stake_history::id(), false),
|
||||||
|
@ -872,7 +912,7 @@ pub fn deposit(
|
||||||
Instruction {
|
Instruction {
|
||||||
program_id: *program_id,
|
program_id: *program_id,
|
||||||
accounts,
|
accounts,
|
||||||
data: StakePoolInstruction::Deposit.try_to_vec().unwrap(),
|
data: StakePoolInstruction::DepositStake.try_to_vec().unwrap(),
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -880,7 +920,7 @@ pub fn deposit(
|
||||||
/// Creates instructions required to deposit into a stake pool, given a stake
|
/// Creates instructions required to deposit into a stake pool, given a stake
|
||||||
/// account owned by the user. The difference with `deposit()` is that a deposit
|
/// account owned by the user. The difference with `deposit()` is that a deposit
|
||||||
/// authority must sign this instruction, which is required for private pools.
|
/// authority must sign this instruction, which is required for private pools.
|
||||||
pub fn deposit_with_authority(
|
pub fn deposit_stake_with_authority(
|
||||||
program_id: &Pubkey,
|
program_id: &Pubkey,
|
||||||
stake_pool: &Pubkey,
|
stake_pool: &Pubkey,
|
||||||
validator_list_storage: &Pubkey,
|
validator_list_storage: &Pubkey,
|
||||||
|
@ -891,6 +931,8 @@ pub fn deposit_with_authority(
|
||||||
validator_stake_account: &Pubkey,
|
validator_stake_account: &Pubkey,
|
||||||
reserve_stake_account: &Pubkey,
|
reserve_stake_account: &Pubkey,
|
||||||
pool_tokens_to: &Pubkey,
|
pool_tokens_to: &Pubkey,
|
||||||
|
manager_fee_account: &Pubkey,
|
||||||
|
referrer_pool_tokens_account: &Pubkey,
|
||||||
pool_mint: &Pubkey,
|
pool_mint: &Pubkey,
|
||||||
token_program_id: &Pubkey,
|
token_program_id: &Pubkey,
|
||||||
) -> Vec<Instruction> {
|
) -> Vec<Instruction> {
|
||||||
|
@ -903,6 +945,8 @@ pub fn deposit_with_authority(
|
||||||
AccountMeta::new(*validator_stake_account, false),
|
AccountMeta::new(*validator_stake_account, false),
|
||||||
AccountMeta::new(*reserve_stake_account, false),
|
AccountMeta::new(*reserve_stake_account, false),
|
||||||
AccountMeta::new(*pool_tokens_to, false),
|
AccountMeta::new(*pool_tokens_to, false),
|
||||||
|
AccountMeta::new(*manager_fee_account, false),
|
||||||
|
AccountMeta::new(*referrer_pool_tokens_account, false),
|
||||||
AccountMeta::new(*pool_mint, false),
|
AccountMeta::new(*pool_mint, false),
|
||||||
AccountMeta::new_readonly(sysvar::clock::id(), false),
|
AccountMeta::new_readonly(sysvar::clock::id(), false),
|
||||||
AccountMeta::new_readonly(sysvar::stake_history::id(), false),
|
AccountMeta::new_readonly(sysvar::stake_history::id(), false),
|
||||||
|
@ -925,13 +969,91 @@ pub fn deposit_with_authority(
|
||||||
Instruction {
|
Instruction {
|
||||||
program_id: *program_id,
|
program_id: *program_id,
|
||||||
accounts,
|
accounts,
|
||||||
data: StakePoolInstruction::Deposit.try_to_vec().unwrap(),
|
data: StakePoolInstruction::DepositStake.try_to_vec().unwrap(),
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a 'withdraw' instruction.
|
/// Creates instructions required to deposit SOL directly into a stake pool.
|
||||||
pub fn withdraw(
|
pub fn deposit_sol(
|
||||||
|
program_id: &Pubkey,
|
||||||
|
stake_pool: &Pubkey,
|
||||||
|
stake_pool_withdraw_authority: &Pubkey,
|
||||||
|
reserve_stake_account: &Pubkey,
|
||||||
|
lamports_from: &Pubkey,
|
||||||
|
pool_tokens_to: &Pubkey,
|
||||||
|
manager_fee_account: &Pubkey,
|
||||||
|
referrer_pool_tokens_account: &Pubkey,
|
||||||
|
pool_mint: &Pubkey,
|
||||||
|
token_program_id: &Pubkey,
|
||||||
|
amount: u64,
|
||||||
|
) -> Vec<Instruction> {
|
||||||
|
let accounts = vec![
|
||||||
|
AccountMeta::new(*stake_pool, false),
|
||||||
|
AccountMeta::new_readonly(*stake_pool_withdraw_authority, false),
|
||||||
|
AccountMeta::new(*reserve_stake_account, false),
|
||||||
|
AccountMeta::new(*lamports_from, true),
|
||||||
|
AccountMeta::new(*pool_tokens_to, false),
|
||||||
|
AccountMeta::new(*manager_fee_account, false),
|
||||||
|
AccountMeta::new(*referrer_pool_tokens_account, false),
|
||||||
|
AccountMeta::new(*pool_mint, false),
|
||||||
|
AccountMeta::new_readonly(sysvar::clock::id(), false),
|
||||||
|
AccountMeta::new_readonly(system_program::id(), false),
|
||||||
|
AccountMeta::new_readonly(*token_program_id, false),
|
||||||
|
];
|
||||||
|
vec![Instruction {
|
||||||
|
program_id: *program_id,
|
||||||
|
accounts,
|
||||||
|
data: StakePoolInstruction::DepositSol(amount)
|
||||||
|
.try_to_vec()
|
||||||
|
.unwrap(),
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates instructions required to deposit SOL directly into a stake pool.
|
||||||
|
/// The difference with `deposit_sol()` is that a deposit
|
||||||
|
/// authority must sign this instruction, which is required for private pools.
|
||||||
|
/// `require_deposit_authority` should be false only if
|
||||||
|
/// `sol_deposit_authority == None`
|
||||||
|
pub fn deposit_sol_with_authority(
|
||||||
|
program_id: &Pubkey,
|
||||||
|
stake_pool: &Pubkey,
|
||||||
|
sol_deposit_authority: &Pubkey,
|
||||||
|
stake_pool_withdraw_authority: &Pubkey,
|
||||||
|
reserve_stake_account: &Pubkey,
|
||||||
|
lamports_from: &Pubkey,
|
||||||
|
pool_tokens_to: &Pubkey,
|
||||||
|
manager_fee_account: &Pubkey,
|
||||||
|
referrer_pool_tokens_account: &Pubkey,
|
||||||
|
pool_mint: &Pubkey,
|
||||||
|
token_program_id: &Pubkey,
|
||||||
|
amount: u64,
|
||||||
|
) -> Vec<Instruction> {
|
||||||
|
let accounts = vec![
|
||||||
|
AccountMeta::new(*stake_pool, false),
|
||||||
|
AccountMeta::new_readonly(*stake_pool_withdraw_authority, false),
|
||||||
|
AccountMeta::new(*reserve_stake_account, false),
|
||||||
|
AccountMeta::new(*lamports_from, true),
|
||||||
|
AccountMeta::new(*pool_tokens_to, false),
|
||||||
|
AccountMeta::new(*manager_fee_account, false),
|
||||||
|
AccountMeta::new(*referrer_pool_tokens_account, false),
|
||||||
|
AccountMeta::new(*pool_mint, false),
|
||||||
|
AccountMeta::new_readonly(sysvar::clock::id(), false),
|
||||||
|
AccountMeta::new_readonly(system_program::id(), false),
|
||||||
|
AccountMeta::new_readonly(*token_program_id, false),
|
||||||
|
AccountMeta::new_readonly(*sol_deposit_authority, true),
|
||||||
|
];
|
||||||
|
vec![Instruction {
|
||||||
|
program_id: *program_id,
|
||||||
|
accounts,
|
||||||
|
data: StakePoolInstruction::DepositSol(amount)
|
||||||
|
.try_to_vec()
|
||||||
|
.unwrap(),
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a 'WithdrawStake' instruction.
|
||||||
|
pub fn withdraw_stake(
|
||||||
program_id: &Pubkey,
|
program_id: &Pubkey,
|
||||||
stake_pool: &Pubkey,
|
stake_pool: &Pubkey,
|
||||||
validator_list_storage: &Pubkey,
|
validator_list_storage: &Pubkey,
|
||||||
|
@ -964,7 +1086,9 @@ pub fn withdraw(
|
||||||
Instruction {
|
Instruction {
|
||||||
program_id: *program_id,
|
program_id: *program_id,
|
||||||
accounts,
|
accounts,
|
||||||
data: StakePoolInstruction::Withdraw(amount).try_to_vec().unwrap(),
|
data: StakePoolInstruction::WithdrawStake(amount)
|
||||||
|
.try_to_vec()
|
||||||
|
.unwrap(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -994,7 +1118,7 @@ pub fn set_fee(
|
||||||
program_id: &Pubkey,
|
program_id: &Pubkey,
|
||||||
stake_pool: &Pubkey,
|
stake_pool: &Pubkey,
|
||||||
manager: &Pubkey,
|
manager: &Pubkey,
|
||||||
fee: Fee,
|
fee: FeeType,
|
||||||
) -> Instruction {
|
) -> Instruction {
|
||||||
let accounts = vec![
|
let accounts = vec![
|
||||||
AccountMeta::new(*stake_pool, false),
|
AccountMeta::new(*stake_pool, false),
|
||||||
|
@ -1008,25 +1132,64 @@ pub fn set_fee(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a 'set fee' instruction.
|
/// Creates a 'set fee' instruction for setting the epoch fee
|
||||||
|
pub fn set_epoch_fee(
|
||||||
|
program_id: &Pubkey,
|
||||||
|
stake_pool: &Pubkey,
|
||||||
|
manager: &Pubkey,
|
||||||
|
fee: Fee,
|
||||||
|
) -> Instruction {
|
||||||
|
set_fee(program_id, stake_pool, manager, FeeType::Epoch(fee))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a 'set fee' instruction for setting withdrawal fee
|
||||||
pub fn set_withdrawal_fee(
|
pub fn set_withdrawal_fee(
|
||||||
program_id: &Pubkey,
|
program_id: &Pubkey,
|
||||||
stake_pool: &Pubkey,
|
stake_pool: &Pubkey,
|
||||||
manager: &Pubkey,
|
manager: &Pubkey,
|
||||||
fee: Fee,
|
fee: Fee,
|
||||||
) -> Instruction {
|
) -> Instruction {
|
||||||
let accounts = vec![
|
set_fee(program_id, stake_pool, manager, FeeType::Withdrawal(fee))
|
||||||
AccountMeta::new(*stake_pool, false),
|
}
|
||||||
AccountMeta::new_readonly(*manager, true),
|
|
||||||
AccountMeta::new_readonly(sysvar::clock::id(), false),
|
/// Creates a 'set fee' instruction for setting SOL deposit fee
|
||||||
];
|
pub fn set_sol_deposit_fee(
|
||||||
Instruction {
|
program_id: &Pubkey,
|
||||||
program_id: *program_id,
|
stake_pool: &Pubkey,
|
||||||
accounts,
|
manager: &Pubkey,
|
||||||
data: StakePoolInstruction::SetWithdrawalFee { fee }
|
fee: Fee,
|
||||||
.try_to_vec()
|
) -> Instruction {
|
||||||
.unwrap(),
|
set_fee(program_id, stake_pool, manager, FeeType::SolDeposit(fee))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a 'set fee' instruction for setting stake deposit fee
|
||||||
|
pub fn set_stake_deposit_fee(
|
||||||
|
program_id: &Pubkey,
|
||||||
|
stake_pool: &Pubkey,
|
||||||
|
manager: &Pubkey,
|
||||||
|
fee: Fee,
|
||||||
|
) -> Instruction {
|
||||||
|
set_fee(program_id, stake_pool, manager, FeeType::StakeDeposit(fee))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a 'set fee' instruction for setting SOL referral fee
|
||||||
|
pub fn set_sol_referral_fee(
|
||||||
|
program_id: &Pubkey,
|
||||||
|
stake_pool: &Pubkey,
|
||||||
|
manager: &Pubkey,
|
||||||
|
fee: u8,
|
||||||
|
) -> Instruction {
|
||||||
|
set_fee(program_id, stake_pool, manager, FeeType::SolReferral(fee))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a 'set fee' instruction for setting stake referral fee
|
||||||
|
pub fn set_stake_referral_fee(
|
||||||
|
program_id: &Pubkey,
|
||||||
|
stake_pool: &Pubkey,
|
||||||
|
manager: &Pubkey,
|
||||||
|
fee: u8,
|
||||||
|
) -> Instruction {
|
||||||
|
set_fee(program_id, stake_pool, manager, FeeType::StakeReferral(fee))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a 'set staker' instruction.
|
/// Creates a 'set staker' instruction.
|
||||||
|
@ -1047,3 +1210,79 @@ pub fn set_staker(
|
||||||
data: StakePoolInstruction::SetStaker.try_to_vec().unwrap(),
|
data: StakePoolInstruction::SetStaker.try_to_vec().unwrap(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a 'set sol deposit authority' instruction.
|
||||||
|
pub fn set_deposit_authority(
|
||||||
|
program_id: &Pubkey,
|
||||||
|
stake_pool: &Pubkey,
|
||||||
|
manager: &Pubkey,
|
||||||
|
new_sol_deposit_authority: Option<&Pubkey>,
|
||||||
|
for_stake_deposit: bool,
|
||||||
|
) -> Instruction {
|
||||||
|
let mut accounts = vec![
|
||||||
|
AccountMeta::new(*stake_pool, false),
|
||||||
|
AccountMeta::new_readonly(*manager, true),
|
||||||
|
];
|
||||||
|
if let Some(auth) = new_sol_deposit_authority {
|
||||||
|
accounts.push(AccountMeta::new_readonly(*auth, false))
|
||||||
|
}
|
||||||
|
Instruction {
|
||||||
|
program_id: *program_id,
|
||||||
|
accounts,
|
||||||
|
data: if for_stake_deposit {
|
||||||
|
StakePoolInstruction::SetDepositAuthority(DepositType::Stake)
|
||||||
|
.try_to_vec()
|
||||||
|
.unwrap()
|
||||||
|
} else {
|
||||||
|
StakePoolInstruction::SetDepositAuthority(DepositType::Sol)
|
||||||
|
.try_to_vec()
|
||||||
|
.unwrap()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a 'set stake deposit authority' instruction.
|
||||||
|
pub fn set_stake_deposit_authority(
|
||||||
|
program_id: &Pubkey,
|
||||||
|
stake_pool: &Pubkey,
|
||||||
|
manager: &Pubkey,
|
||||||
|
new_stake_deposit_authority: Option<&Pubkey>,
|
||||||
|
) -> Instruction {
|
||||||
|
let mut accounts = vec![
|
||||||
|
AccountMeta::new(*stake_pool, false),
|
||||||
|
AccountMeta::new_readonly(*manager, true),
|
||||||
|
];
|
||||||
|
if let Some(auth) = new_stake_deposit_authority {
|
||||||
|
accounts.push(AccountMeta::new_readonly(*auth, false))
|
||||||
|
}
|
||||||
|
Instruction {
|
||||||
|
program_id: *program_id,
|
||||||
|
accounts,
|
||||||
|
data: StakePoolInstruction::SetDepositAuthority(DepositType::Stake)
|
||||||
|
.try_to_vec()
|
||||||
|
.unwrap(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a 'set stake deposit authority' instruction.
|
||||||
|
pub fn set_sol_deposit_authority(
|
||||||
|
program_id: &Pubkey,
|
||||||
|
stake_pool: &Pubkey,
|
||||||
|
manager: &Pubkey,
|
||||||
|
new_stake_deposit_authority: Option<&Pubkey>,
|
||||||
|
) -> Instruction {
|
||||||
|
let mut accounts = vec![
|
||||||
|
AccountMeta::new(*stake_pool, false),
|
||||||
|
AccountMeta::new_readonly(*manager, true),
|
||||||
|
];
|
||||||
|
if let Some(auth) = new_stake_deposit_authority {
|
||||||
|
accounts.push(AccountMeta::new_readonly(*auth, false))
|
||||||
|
}
|
||||||
|
Instruction {
|
||||||
|
program_id: *program_id,
|
||||||
|
accounts,
|
||||||
|
data: StakePoolInstruction::SetDepositAuthority(DepositType::Sol)
|
||||||
|
.try_to_vec()
|
||||||
|
.unwrap(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -51,6 +51,7 @@ pub const WITHDRAWAL_BASELINE_FEE: Fee = Fee {
|
||||||
|
|
||||||
/// Get the stake amount under consideration when calculating pool token
|
/// Get the stake amount under consideration when calculating pool token
|
||||||
/// conversions
|
/// conversions
|
||||||
|
#[inline]
|
||||||
pub fn minimum_stake_lamports(meta: &Meta) -> u64 {
|
pub fn minimum_stake_lamports(meta: &Meta) -> u64 {
|
||||||
meta.rent_exempt_reserve
|
meta.rent_exempt_reserve
|
||||||
.saturating_add(MINIMUM_ACTIVE_STAKE)
|
.saturating_add(MINIMUM_ACTIVE_STAKE)
|
||||||
|
@ -58,6 +59,7 @@ pub fn minimum_stake_lamports(meta: &Meta) -> u64 {
|
||||||
|
|
||||||
/// Get the stake amount under consideration when calculating pool token
|
/// Get the stake amount under consideration when calculating pool token
|
||||||
/// conversions
|
/// conversions
|
||||||
|
#[inline]
|
||||||
pub fn minimum_reserve_lamports(meta: &Meta) -> u64 {
|
pub fn minimum_reserve_lamports(meta: &Meta) -> u64 {
|
||||||
meta.rent_exempt_reserve.saturating_add(1)
|
meta.rent_exempt_reserve.saturating_add(1)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
//! Program state processor
|
//! Program state processor
|
||||||
|
|
||||||
|
use crate::instruction::DepositType;
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
error::StakePoolError,
|
error::StakePoolError,
|
||||||
|
@ -7,11 +8,10 @@ use {
|
||||||
instruction::{PreferredValidatorType, StakePoolInstruction},
|
instruction::{PreferredValidatorType, StakePoolInstruction},
|
||||||
minimum_reserve_lamports, minimum_stake_lamports, stake_program,
|
minimum_reserve_lamports, minimum_stake_lamports, stake_program,
|
||||||
state::{
|
state::{
|
||||||
AccountType, Fee, StakePool, StakeStatus, ValidatorList, ValidatorListHeader,
|
AccountType, Fee, FeeType, StakePool, StakeStatus, ValidatorList, ValidatorListHeader,
|
||||||
ValidatorStakeInfo,
|
ValidatorStakeInfo,
|
||||||
},
|
},
|
||||||
AUTHORITY_DEPOSIT, AUTHORITY_WITHDRAW, MAX_WITHDRAWAL_FEE_INCREASE, MINIMUM_ACTIVE_STAKE,
|
AUTHORITY_DEPOSIT, AUTHORITY_WITHDRAW, MINIMUM_ACTIVE_STAKE, TRANSIENT_STAKE_SEED,
|
||||||
TRANSIENT_STAKE_SEED, WITHDRAWAL_BASELINE_FEE,
|
|
||||||
},
|
},
|
||||||
borsh::{BorshDeserialize, BorshSerialize},
|
borsh::{BorshDeserialize, BorshSerialize},
|
||||||
num_traits::FromPrimitive,
|
num_traits::FromPrimitive,
|
||||||
|
@ -468,12 +468,24 @@ impl Processor {
|
||||||
invoke(&ix, &[source, destination, authority, token_program])
|
invoke(&ix, &[source, destination, authority, token_program])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn sol_transfer<'a>(
|
||||||
|
source: AccountInfo<'a>,
|
||||||
|
destination: AccountInfo<'a>,
|
||||||
|
system_program: AccountInfo<'a>,
|
||||||
|
amount: u64,
|
||||||
|
) -> Result<(), ProgramError> {
|
||||||
|
let ix = solana_program::system_instruction::transfer(source.key, destination.key, amount);
|
||||||
|
invoke(&ix, &[source, destination, system_program])
|
||||||
|
}
|
||||||
|
|
||||||
/// Processes `Initialize` instruction.
|
/// Processes `Initialize` instruction.
|
||||||
fn process_initialize(
|
fn process_initialize(
|
||||||
program_id: &Pubkey,
|
program_id: &Pubkey,
|
||||||
accounts: &[AccountInfo],
|
accounts: &[AccountInfo],
|
||||||
fee: Fee,
|
fee: Fee,
|
||||||
withdrawal_fee: Fee,
|
withdrawal_fee: Fee,
|
||||||
|
stake_deposit_fee: Fee,
|
||||||
|
stake_referral_fee: u8,
|
||||||
max_validators: u32,
|
max_validators: u32,
|
||||||
) -> ProgramResult {
|
) -> ProgramResult {
|
||||||
let account_info_iter = &mut accounts.iter();
|
let account_info_iter = &mut accounts.iter();
|
||||||
|
@ -538,7 +550,11 @@ impl Processor {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Numerator should be smaller than or equal to denominator (fee <= 1)
|
// Numerator should be smaller than or equal to denominator (fee <= 1)
|
||||||
if fee.numerator > fee.denominator {
|
if fee.numerator > fee.denominator
|
||||||
|
|| withdrawal_fee.numerator > withdrawal_fee.denominator
|
||||||
|
|| stake_deposit_fee.numerator > stake_deposit_fee.denominator
|
||||||
|
|| stake_referral_fee > 100u8
|
||||||
|
{
|
||||||
return Err(StakePoolError::FeeTooHigh.into());
|
return Err(StakePoolError::FeeTooHigh.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -556,11 +572,11 @@ impl Processor {
|
||||||
return Err(StakePoolError::WrongAccountMint.into());
|
return Err(StakePoolError::WrongAccountMint.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
let deposit_authority = match next_account_info(account_info_iter) {
|
let stake_deposit_authority = match next_account_info(account_info_iter) {
|
||||||
Ok(deposit_authority_info) => *deposit_authority_info.key,
|
Ok(stake_deposit_authority_info) => *stake_deposit_authority_info.key,
|
||||||
Err(_) => find_deposit_authority_program_address(program_id, stake_pool_info.key).0,
|
Err(_) => find_deposit_authority_program_address(program_id, stake_pool_info.key).0,
|
||||||
};
|
};
|
||||||
let (withdraw_authority_key, withdraw_bump_seed) =
|
let (withdraw_authority_key, stake_withdraw_bump_seed) =
|
||||||
crate::find_withdraw_authority_program_address(program_id, stake_pool_info.key);
|
crate::find_withdraw_authority_program_address(program_id, stake_pool_info.key);
|
||||||
|
|
||||||
let pool_mint = Mint::unpack_from_slice(&pool_mint_info.data.borrow())?;
|
let pool_mint = Mint::unpack_from_slice(&pool_mint_info.data.borrow())?;
|
||||||
|
@ -616,10 +632,6 @@ impl Processor {
|
||||||
msg!("Reserve stake account not in intialized state");
|
msg!("Reserve stake account not in intialized state");
|
||||||
return Err(StakePoolError::WrongStakeState.into());
|
return Err(StakePoolError::WrongStakeState.into());
|
||||||
};
|
};
|
||||||
// Numerator should be smaller than or equal to denominator (fee <= 1)
|
|
||||||
if withdrawal_fee.numerator > withdrawal_fee.denominator {
|
|
||||||
return Err(StakePoolError::FeeTooHigh.into());
|
|
||||||
}
|
|
||||||
|
|
||||||
validator_list.serialize(&mut *validator_list_info.data.borrow_mut())?;
|
validator_list.serialize(&mut *validator_list_info.data.borrow_mut())?;
|
||||||
|
|
||||||
|
@ -627,8 +639,8 @@ impl Processor {
|
||||||
stake_pool.manager = *manager_info.key;
|
stake_pool.manager = *manager_info.key;
|
||||||
stake_pool.staker = *staker_info.key;
|
stake_pool.staker = *staker_info.key;
|
||||||
stake_pool.reserve_stake = *reserve_stake_info.key;
|
stake_pool.reserve_stake = *reserve_stake_info.key;
|
||||||
stake_pool.deposit_authority = deposit_authority;
|
stake_pool.stake_deposit_authority = stake_deposit_authority;
|
||||||
stake_pool.withdraw_bump_seed = withdraw_bump_seed;
|
stake_pool.stake_withdraw_bump_seed = stake_withdraw_bump_seed;
|
||||||
stake_pool.validator_list = *validator_list_info.key;
|
stake_pool.validator_list = *validator_list_info.key;
|
||||||
stake_pool.pool_mint = *pool_mint_info.key;
|
stake_pool.pool_mint = *pool_mint_info.key;
|
||||||
stake_pool.manager_fee_account = *manager_fee_info.key;
|
stake_pool.manager_fee_account = *manager_fee_info.key;
|
||||||
|
@ -639,9 +651,11 @@ impl Processor {
|
||||||
stake_pool.next_epoch_fee = None;
|
stake_pool.next_epoch_fee = None;
|
||||||
stake_pool.preferred_deposit_validator_vote_address = None;
|
stake_pool.preferred_deposit_validator_vote_address = None;
|
||||||
stake_pool.preferred_withdraw_validator_vote_address = None;
|
stake_pool.preferred_withdraw_validator_vote_address = None;
|
||||||
stake_pool.deposit_fee = Fee::default();
|
stake_pool.stake_deposit_fee = stake_deposit_fee;
|
||||||
stake_pool.withdrawal_fee = withdrawal_fee;
|
stake_pool.withdrawal_fee = withdrawal_fee;
|
||||||
stake_pool.next_withdrawal_fee = None;
|
stake_pool.next_withdrawal_fee = None;
|
||||||
|
stake_pool.stake_referral_fee = stake_referral_fee;
|
||||||
|
stake_pool.sol_deposit_authority = None;
|
||||||
|
|
||||||
stake_pool
|
stake_pool
|
||||||
.serialize(&mut *stake_pool_info.data.borrow_mut())
|
.serialize(&mut *stake_pool_info.data.borrow_mut())
|
||||||
|
@ -798,7 +812,7 @@ impl Processor {
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
if meta.lockup != stake_program::Lockup::default() {
|
if meta.lockup != stake_program::Lockup::default() {
|
||||||
msg!("Validator stake account has a lockup");
|
msg!("Stake account has a lockup");
|
||||||
return Err(StakePoolError::WrongStakeState.into());
|
return Err(StakePoolError::WrongStakeState.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -813,20 +827,13 @@ impl Processor {
|
||||||
// Check amount of lamports
|
// Check amount of lamports
|
||||||
let stake_lamports = **stake_account_info.lamports.borrow();
|
let stake_lamports = **stake_account_info.lamports.borrow();
|
||||||
let minimum_lamport_amount = minimum_stake_lamports(&meta);
|
let minimum_lamport_amount = minimum_stake_lamports(&meta);
|
||||||
if stake_lamports != minimum_lamport_amount {
|
if stake_lamports != minimum_lamport_amount
|
||||||
|
|| stake.delegation.stake != MINIMUM_ACTIVE_STAKE
|
||||||
|
{
|
||||||
msg!(
|
msg!(
|
||||||
"Error: attempting to add stake with {} lamports, must have {} lamports",
|
"Error: attempting to add (stake: {}, delegation: {}), below minimum",
|
||||||
stake_lamports,
|
stake_lamports,
|
||||||
minimum_lamport_amount
|
|
||||||
);
|
|
||||||
return Err(StakePoolError::StakeLamportsNotEqualToMinimum.into());
|
|
||||||
}
|
|
||||||
|
|
||||||
if stake.delegation.stake != MINIMUM_ACTIVE_STAKE {
|
|
||||||
msg!(
|
|
||||||
"Error: attempting to add stake with delegation of {} lamports, must have {} lamports",
|
|
||||||
stake.delegation.stake,
|
stake.delegation.stake,
|
||||||
MINIMUM_ACTIVE_STAKE
|
|
||||||
);
|
);
|
||||||
return Err(StakePoolError::StakeLamportsNotEqualToMinimum.into());
|
return Err(StakePoolError::StakeLamportsNotEqualToMinimum.into());
|
||||||
}
|
}
|
||||||
|
@ -970,7 +977,7 @@ impl Processor {
|
||||||
stake_account_info.clone(),
|
stake_account_info.clone(),
|
||||||
withdraw_authority_info.clone(),
|
withdraw_authority_info.clone(),
|
||||||
AUTHORITY_WITHDRAW,
|
AUTHORITY_WITHDRAW,
|
||||||
stake_pool.withdraw_bump_seed,
|
stake_pool.stake_withdraw_bump_seed,
|
||||||
new_stake_authority_info.key,
|
new_stake_authority_info.key,
|
||||||
clock_info.clone(),
|
clock_info.clone(),
|
||||||
stake_program_info.clone(),
|
stake_program_info.clone(),
|
||||||
|
@ -1097,7 +1104,7 @@ impl Processor {
|
||||||
validator_stake_account_info.clone(),
|
validator_stake_account_info.clone(),
|
||||||
withdraw_authority_info.clone(),
|
withdraw_authority_info.clone(),
|
||||||
AUTHORITY_WITHDRAW,
|
AUTHORITY_WITHDRAW,
|
||||||
stake_pool.withdraw_bump_seed,
|
stake_pool.stake_withdraw_bump_seed,
|
||||||
lamports,
|
lamports,
|
||||||
transient_stake_account_info.clone(),
|
transient_stake_account_info.clone(),
|
||||||
)?;
|
)?;
|
||||||
|
@ -1109,7 +1116,7 @@ impl Processor {
|
||||||
withdraw_authority_info.clone(),
|
withdraw_authority_info.clone(),
|
||||||
stake_pool_info.key,
|
stake_pool_info.key,
|
||||||
AUTHORITY_WITHDRAW,
|
AUTHORITY_WITHDRAW,
|
||||||
stake_pool.withdraw_bump_seed,
|
stake_pool.stake_withdraw_bump_seed,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
validator_stake_info.active_stake_lamports = validator_stake_info
|
validator_stake_info.active_stake_lamports = validator_stake_info
|
||||||
|
@ -1249,7 +1256,7 @@ impl Processor {
|
||||||
reserve_stake_account_info.clone(),
|
reserve_stake_account_info.clone(),
|
||||||
withdraw_authority_info.clone(),
|
withdraw_authority_info.clone(),
|
||||||
AUTHORITY_WITHDRAW,
|
AUTHORITY_WITHDRAW,
|
||||||
stake_pool.withdraw_bump_seed,
|
stake_pool.stake_withdraw_bump_seed,
|
||||||
total_lamports,
|
total_lamports,
|
||||||
transient_stake_account_info.clone(),
|
transient_stake_account_info.clone(),
|
||||||
)?;
|
)?;
|
||||||
|
@ -1264,7 +1271,7 @@ impl Processor {
|
||||||
withdraw_authority_info.clone(),
|
withdraw_authority_info.clone(),
|
||||||
stake_pool_info.key,
|
stake_pool_info.key,
|
||||||
AUTHORITY_WITHDRAW,
|
AUTHORITY_WITHDRAW,
|
||||||
stake_pool.withdraw_bump_seed,
|
stake_pool.stake_withdraw_bump_seed,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
validator_stake_info.transient_stake_lamports = total_lamports;
|
validator_stake_info.transient_stake_lamports = total_lamports;
|
||||||
|
@ -1427,7 +1434,7 @@ impl Processor {
|
||||||
transient_stake_info.clone(),
|
transient_stake_info.clone(),
|
||||||
withdraw_authority_info.clone(),
|
withdraw_authority_info.clone(),
|
||||||
AUTHORITY_WITHDRAW,
|
AUTHORITY_WITHDRAW,
|
||||||
stake_pool.withdraw_bump_seed,
|
stake_pool.stake_withdraw_bump_seed,
|
||||||
reserve_stake_info.clone(),
|
reserve_stake_info.clone(),
|
||||||
clock_info.clone(),
|
clock_info.clone(),
|
||||||
stake_history_info.clone(),
|
stake_history_info.clone(),
|
||||||
|
@ -1453,7 +1460,7 @@ impl Processor {
|
||||||
transient_stake_info.clone(),
|
transient_stake_info.clone(),
|
||||||
withdraw_authority_info.clone(),
|
withdraw_authority_info.clone(),
|
||||||
AUTHORITY_WITHDRAW,
|
AUTHORITY_WITHDRAW,
|
||||||
stake_pool.withdraw_bump_seed,
|
stake_pool.stake_withdraw_bump_seed,
|
||||||
reserve_stake_info.clone(),
|
reserve_stake_info.clone(),
|
||||||
clock_info.clone(),
|
clock_info.clone(),
|
||||||
stake_history_info.clone(),
|
stake_history_info.clone(),
|
||||||
|
@ -1479,7 +1486,7 @@ impl Processor {
|
||||||
transient_stake_info.clone(),
|
transient_stake_info.clone(),
|
||||||
withdraw_authority_info.clone(),
|
withdraw_authority_info.clone(),
|
||||||
AUTHORITY_WITHDRAW,
|
AUTHORITY_WITHDRAW,
|
||||||
stake_pool.withdraw_bump_seed,
|
stake_pool.stake_withdraw_bump_seed,
|
||||||
validator_stake_info.clone(),
|
validator_stake_info.clone(),
|
||||||
clock_info.clone(),
|
clock_info.clone(),
|
||||||
stake_history_info.clone(),
|
stake_history_info.clone(),
|
||||||
|
@ -1494,7 +1501,7 @@ impl Processor {
|
||||||
validator_stake_info.clone(),
|
validator_stake_info.clone(),
|
||||||
withdraw_authority_info.clone(),
|
withdraw_authority_info.clone(),
|
||||||
AUTHORITY_WITHDRAW,
|
AUTHORITY_WITHDRAW,
|
||||||
stake_pool.withdraw_bump_seed,
|
stake_pool.stake_withdraw_bump_seed,
|
||||||
reserve_stake_info.clone(),
|
reserve_stake_info.clone(),
|
||||||
clock_info.clone(),
|
clock_info.clone(),
|
||||||
stake_history_info.clone(),
|
stake_history_info.clone(),
|
||||||
|
@ -1623,7 +1630,7 @@ impl Processor {
|
||||||
|
|
||||||
let reward_lamports = total_stake_lamports.saturating_sub(previous_lamports);
|
let reward_lamports = total_stake_lamports.saturating_sub(previous_lamports);
|
||||||
let fee = stake_pool
|
let fee = stake_pool
|
||||||
.calc_fee_amount(reward_lamports)
|
.calc_epoch_fee_amount(reward_lamports)
|
||||||
.ok_or(StakePoolError::CalculationFailure)?;
|
.ok_or(StakePoolError::CalculationFailure)?;
|
||||||
|
|
||||||
if fee > 0 {
|
if fee > 0 {
|
||||||
|
@ -1634,7 +1641,7 @@ impl Processor {
|
||||||
manager_fee_info.clone(),
|
manager_fee_info.clone(),
|
||||||
withdraw_info.clone(),
|
withdraw_info.clone(),
|
||||||
AUTHORITY_WITHDRAW,
|
AUTHORITY_WITHDRAW,
|
||||||
stake_pool.withdraw_bump_seed,
|
stake_pool.stake_withdraw_bump_seed,
|
||||||
fee,
|
fee,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
@ -1718,17 +1725,19 @@ impl Processor {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Processes [Deposit](enum.Instruction.html).
|
/// Processes [DepositStake](enum.Instruction.html).
|
||||||
fn process_deposit(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult {
|
fn process_deposit_stake(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult {
|
||||||
let account_info_iter = &mut accounts.iter();
|
let account_info_iter = &mut accounts.iter();
|
||||||
let stake_pool_info = next_account_info(account_info_iter)?;
|
let stake_pool_info = next_account_info(account_info_iter)?;
|
||||||
let validator_list_info = next_account_info(account_info_iter)?;
|
let validator_list_info = next_account_info(account_info_iter)?;
|
||||||
let deposit_authority_info = next_account_info(account_info_iter)?;
|
let stake_deposit_authority_info = next_account_info(account_info_iter)?;
|
||||||
let withdraw_authority_info = next_account_info(account_info_iter)?;
|
let withdraw_authority_info = next_account_info(account_info_iter)?;
|
||||||
let stake_info = next_account_info(account_info_iter)?;
|
let stake_info = next_account_info(account_info_iter)?;
|
||||||
let validator_stake_account_info = next_account_info(account_info_iter)?;
|
let validator_stake_account_info = next_account_info(account_info_iter)?;
|
||||||
let reserve_stake_account_info = next_account_info(account_info_iter)?;
|
let reserve_stake_account_info = next_account_info(account_info_iter)?;
|
||||||
let dest_user_info = next_account_info(account_info_iter)?;
|
let dest_user_pool_info = next_account_info(account_info_iter)?;
|
||||||
|
let manager_fee_info = next_account_info(account_info_iter)?;
|
||||||
|
let referrer_fee_info = next_account_info(account_info_iter)?;
|
||||||
let pool_mint_info = next_account_info(account_info_iter)?;
|
let pool_mint_info = next_account_info(account_info_iter)?;
|
||||||
let clock_info = next_account_info(account_info_iter)?;
|
let clock_info = next_account_info(account_info_iter)?;
|
||||||
let clock = &Clock::from_account_info(clock_info)?;
|
let clock = &Clock::from_account_info(clock_info)?;
|
||||||
|
@ -1753,7 +1762,7 @@ impl Processor {
|
||||||
program_id,
|
program_id,
|
||||||
stake_pool_info.key,
|
stake_pool_info.key,
|
||||||
)?;
|
)?;
|
||||||
stake_pool.check_deposit_authority(deposit_authority_info.key)?;
|
stake_pool.check_stake_deposit_authority(stake_deposit_authority_info.key)?;
|
||||||
stake_pool.check_mint(pool_mint_info)?;
|
stake_pool.check_mint(pool_mint_info)?;
|
||||||
stake_pool.check_validator_list(validator_list_info)?;
|
stake_pool.check_validator_list(validator_list_info)?;
|
||||||
stake_pool.check_reserve_stake(reserve_stake_account_info)?;
|
stake_pool.check_reserve_stake(reserve_stake_account_info)?;
|
||||||
|
@ -1762,6 +1771,10 @@ impl Processor {
|
||||||
return Err(ProgramError::IncorrectProgramId);
|
return Err(ProgramError::IncorrectProgramId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if stake_pool.manager_fee_account != *manager_fee_info.key {
|
||||||
|
return Err(StakePoolError::InvalidFeeAccount.into());
|
||||||
|
}
|
||||||
|
|
||||||
if stake_pool.last_update_epoch < clock.epoch {
|
if stake_pool.last_update_epoch < clock.epoch {
|
||||||
return Err(StakePoolError::StakeListAndPoolOutOfDate.into());
|
return Err(StakePoolError::StakeListAndPoolOutOfDate.into());
|
||||||
}
|
}
|
||||||
|
@ -1808,13 +1821,13 @@ impl Processor {
|
||||||
|
|
||||||
msg!("Stake pre merge {}", validator_stake.delegation.stake);
|
msg!("Stake pre merge {}", validator_stake.delegation.stake);
|
||||||
|
|
||||||
let (deposit_authority_program_address, deposit_bump_seed) =
|
let (stake_deposit_authority_program_address, deposit_bump_seed) =
|
||||||
find_deposit_authority_program_address(program_id, stake_pool_info.key);
|
find_deposit_authority_program_address(program_id, stake_pool_info.key);
|
||||||
if *deposit_authority_info.key == deposit_authority_program_address {
|
if *stake_deposit_authority_info.key == stake_deposit_authority_program_address {
|
||||||
Self::stake_authorize_signed(
|
Self::stake_authorize_signed(
|
||||||
stake_pool_info.key,
|
stake_pool_info.key,
|
||||||
stake_info.clone(),
|
stake_info.clone(),
|
||||||
deposit_authority_info.clone(),
|
stake_deposit_authority_info.clone(),
|
||||||
AUTHORITY_DEPOSIT,
|
AUTHORITY_DEPOSIT,
|
||||||
deposit_bump_seed,
|
deposit_bump_seed,
|
||||||
withdraw_authority_info.key,
|
withdraw_authority_info.key,
|
||||||
|
@ -1824,7 +1837,7 @@ impl Processor {
|
||||||
} else {
|
} else {
|
||||||
Self::stake_authorize(
|
Self::stake_authorize(
|
||||||
stake_info.clone(),
|
stake_info.clone(),
|
||||||
deposit_authority_info.clone(),
|
stake_deposit_authority_info.clone(),
|
||||||
withdraw_authority_info.key,
|
withdraw_authority_info.key,
|
||||||
clock_info.clone(),
|
clock_info.clone(),
|
||||||
stake_program_info.clone(),
|
stake_program_info.clone(),
|
||||||
|
@ -1836,7 +1849,7 @@ impl Processor {
|
||||||
stake_info.clone(),
|
stake_info.clone(),
|
||||||
withdraw_authority_info.clone(),
|
withdraw_authority_info.clone(),
|
||||||
AUTHORITY_WITHDRAW,
|
AUTHORITY_WITHDRAW,
|
||||||
stake_pool.withdraw_bump_seed,
|
stake_pool.stake_withdraw_bump_seed,
|
||||||
validator_stake_account_info.clone(),
|
validator_stake_account_info.clone(),
|
||||||
clock_info.clone(),
|
clock_info.clone(),
|
||||||
stake_history_info.clone(),
|
stake_history_info.clone(),
|
||||||
|
@ -1854,24 +1867,71 @@ impl Processor {
|
||||||
.stake
|
.stake
|
||||||
.checked_sub(validator_stake.delegation.stake)
|
.checked_sub(validator_stake.delegation.stake)
|
||||||
.ok_or(StakePoolError::CalculationFailure)?;
|
.ok_or(StakePoolError::CalculationFailure)?;
|
||||||
|
|
||||||
let new_pool_tokens = stake_pool
|
let new_pool_tokens = stake_pool
|
||||||
.calc_pool_tokens_for_deposit(all_deposit_lamports)
|
.calc_pool_tokens_for_deposit(all_deposit_lamports)
|
||||||
.ok_or(StakePoolError::CalculationFailure)?;
|
.ok_or(StakePoolError::CalculationFailure)?;
|
||||||
|
|
||||||
|
let pool_tokens_stake_deposit_fee = stake_pool
|
||||||
|
.calc_pool_tokens_stake_deposit_fee(new_pool_tokens)
|
||||||
|
.ok_or(StakePoolError::CalculationFailure)?;
|
||||||
|
let pool_tokens_user = new_pool_tokens
|
||||||
|
.checked_sub(pool_tokens_stake_deposit_fee)
|
||||||
|
.ok_or(StakePoolError::CalculationFailure)?;
|
||||||
|
|
||||||
|
let pool_tokens_referral_fee = stake_pool
|
||||||
|
.calc_pool_tokens_stake_referral_fee(pool_tokens_stake_deposit_fee)
|
||||||
|
.ok_or(StakePoolError::CalculationFailure)?;
|
||||||
|
let pool_tokens_manager_deposit_fee = pool_tokens_stake_deposit_fee
|
||||||
|
.checked_sub(pool_tokens_referral_fee)
|
||||||
|
.ok_or(StakePoolError::CalculationFailure)?;
|
||||||
|
|
||||||
|
if pool_tokens_user + pool_tokens_manager_deposit_fee + pool_tokens_referral_fee
|
||||||
|
!= new_pool_tokens
|
||||||
|
{
|
||||||
|
return Err(StakePoolError::CalculationFailure.into());
|
||||||
|
}
|
||||||
|
|
||||||
if new_pool_tokens == 0 {
|
if new_pool_tokens == 0 {
|
||||||
return Err(StakePoolError::DepositTooSmall.into());
|
return Err(StakePoolError::DepositTooSmall.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if pool_tokens_user > 0 {
|
||||||
Self::token_mint_to(
|
Self::token_mint_to(
|
||||||
stake_pool_info.key,
|
stake_pool_info.key,
|
||||||
token_program_info.clone(),
|
token_program_info.clone(),
|
||||||
pool_mint_info.clone(),
|
pool_mint_info.clone(),
|
||||||
dest_user_info.clone(),
|
dest_user_pool_info.clone(),
|
||||||
withdraw_authority_info.clone(),
|
withdraw_authority_info.clone(),
|
||||||
AUTHORITY_WITHDRAW,
|
AUTHORITY_WITHDRAW,
|
||||||
stake_pool.withdraw_bump_seed,
|
stake_pool.stake_withdraw_bump_seed,
|
||||||
new_pool_tokens,
|
pool_tokens_user,
|
||||||
)?;
|
)?;
|
||||||
|
}
|
||||||
|
if pool_tokens_manager_deposit_fee > 0 {
|
||||||
|
Self::token_mint_to(
|
||||||
|
stake_pool_info.key,
|
||||||
|
token_program_info.clone(),
|
||||||
|
pool_mint_info.clone(),
|
||||||
|
manager_fee_info.clone(),
|
||||||
|
withdraw_authority_info.clone(),
|
||||||
|
AUTHORITY_WITHDRAW,
|
||||||
|
stake_pool.stake_withdraw_bump_seed,
|
||||||
|
pool_tokens_manager_deposit_fee,
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
if pool_tokens_referral_fee > 0 {
|
||||||
|
Self::token_mint_to(
|
||||||
|
stake_pool_info.key,
|
||||||
|
token_program_info.clone(),
|
||||||
|
pool_mint_info.clone(),
|
||||||
|
referrer_fee_info.clone(),
|
||||||
|
withdraw_authority_info.clone(),
|
||||||
|
AUTHORITY_WITHDRAW,
|
||||||
|
stake_pool.stake_withdraw_bump_seed,
|
||||||
|
pool_tokens_referral_fee,
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
// withdraw additional lamports to the reserve
|
// withdraw additional lamports to the reserve
|
||||||
let additional_lamports = all_deposit_lamports
|
let additional_lamports = all_deposit_lamports
|
||||||
|
@ -1883,7 +1943,7 @@ impl Processor {
|
||||||
validator_stake_account_info.clone(),
|
validator_stake_account_info.clone(),
|
||||||
withdraw_authority_info.clone(),
|
withdraw_authority_info.clone(),
|
||||||
AUTHORITY_WITHDRAW,
|
AUTHORITY_WITHDRAW,
|
||||||
stake_pool.withdraw_bump_seed,
|
stake_pool.stake_withdraw_bump_seed,
|
||||||
reserve_stake_account_info.clone(),
|
reserve_stake_account_info.clone(),
|
||||||
clock_info.clone(),
|
clock_info.clone(),
|
||||||
stake_history_info.clone(),
|
stake_history_info.clone(),
|
||||||
|
@ -1911,8 +1971,146 @@ impl Processor {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Processes [Withdraw](enum.Instruction.html).
|
/// Processes [DepositStake](enum.Instruction.html).
|
||||||
fn process_withdraw(
|
fn process_deposit_sol(
|
||||||
|
program_id: &Pubkey,
|
||||||
|
accounts: &[AccountInfo],
|
||||||
|
deposit_lamports: u64,
|
||||||
|
) -> ProgramResult {
|
||||||
|
let account_info_iter = &mut accounts.iter();
|
||||||
|
let stake_pool_info = next_account_info(account_info_iter)?;
|
||||||
|
let withdraw_authority_info = next_account_info(account_info_iter)?;
|
||||||
|
let reserve_stake_account_info = next_account_info(account_info_iter)?;
|
||||||
|
let from_user_lamports_info = next_account_info(account_info_iter)?;
|
||||||
|
let dest_user_pool_info = next_account_info(account_info_iter)?;
|
||||||
|
let manager_fee_info = next_account_info(account_info_iter)?;
|
||||||
|
let referrer_fee_info = next_account_info(account_info_iter)?;
|
||||||
|
let pool_mint_info = next_account_info(account_info_iter)?;
|
||||||
|
let clock_info = next_account_info(account_info_iter)?;
|
||||||
|
let clock = &Clock::from_account_info(clock_info)?;
|
||||||
|
let system_program_info = next_account_info(account_info_iter)?;
|
||||||
|
let token_program_info = next_account_info(account_info_iter)?;
|
||||||
|
let sol_deposit_authority_info = next_account_info(account_info_iter).ok();
|
||||||
|
|
||||||
|
check_account_owner(stake_pool_info, program_id)?;
|
||||||
|
let mut stake_pool = try_from_slice_unchecked::<StakePool>(&stake_pool_info.data.borrow())?;
|
||||||
|
if !stake_pool.is_valid() {
|
||||||
|
return Err(StakePoolError::InvalidState.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Self::check_stake_activation(stake_info, clock, stake_history)?;
|
||||||
|
|
||||||
|
stake_pool.check_authority_withdraw(
|
||||||
|
withdraw_authority_info.key,
|
||||||
|
program_id,
|
||||||
|
stake_pool_info.key,
|
||||||
|
)?;
|
||||||
|
if let Some(sol_deposit_authority) = sol_deposit_authority_info {
|
||||||
|
stake_pool.check_sol_deposit_authority(sol_deposit_authority)?;
|
||||||
|
}
|
||||||
|
stake_pool.check_mint(pool_mint_info)?;
|
||||||
|
stake_pool.check_reserve_stake(reserve_stake_account_info)?;
|
||||||
|
|
||||||
|
if stake_pool.token_program_id != *token_program_info.key {
|
||||||
|
return Err(ProgramError::IncorrectProgramId);
|
||||||
|
}
|
||||||
|
check_system_program(system_program_info.key)?;
|
||||||
|
|
||||||
|
if stake_pool.manager_fee_account != *manager_fee_info.key {
|
||||||
|
return Err(StakePoolError::InvalidFeeAccount.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
// We want this to hold to ensure that deposit_sol mints pool tokens
|
||||||
|
// at the right price
|
||||||
|
if stake_pool.last_update_epoch < clock.epoch {
|
||||||
|
return Err(StakePoolError::StakeListAndPoolOutOfDate.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
let new_pool_tokens = stake_pool
|
||||||
|
.calc_pool_tokens_for_deposit(deposit_lamports)
|
||||||
|
.ok_or(StakePoolError::CalculationFailure)?;
|
||||||
|
|
||||||
|
let pool_tokens_sol_deposit_fee = stake_pool
|
||||||
|
.calc_pool_tokens_sol_deposit_fee(new_pool_tokens)
|
||||||
|
.ok_or(StakePoolError::CalculationFailure)?;
|
||||||
|
let pool_tokens_user = new_pool_tokens
|
||||||
|
.checked_sub(pool_tokens_sol_deposit_fee)
|
||||||
|
.ok_or(StakePoolError::CalculationFailure)?;
|
||||||
|
|
||||||
|
let pool_tokens_referral_fee = stake_pool
|
||||||
|
.calc_pool_tokens_sol_referral_fee(pool_tokens_sol_deposit_fee)
|
||||||
|
.ok_or(StakePoolError::CalculationFailure)?;
|
||||||
|
let pool_tokens_manager_deposit_fee = pool_tokens_sol_deposit_fee
|
||||||
|
.checked_sub(pool_tokens_referral_fee)
|
||||||
|
.ok_or(StakePoolError::CalculationFailure)?;
|
||||||
|
|
||||||
|
if pool_tokens_user + pool_tokens_manager_deposit_fee + pool_tokens_referral_fee
|
||||||
|
!= new_pool_tokens
|
||||||
|
{
|
||||||
|
return Err(StakePoolError::CalculationFailure.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
Self::sol_transfer(
|
||||||
|
from_user_lamports_info.clone(),
|
||||||
|
reserve_stake_account_info.clone(),
|
||||||
|
system_program_info.clone(),
|
||||||
|
deposit_lamports,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
if pool_tokens_user > 0 {
|
||||||
|
Self::token_mint_to(
|
||||||
|
stake_pool_info.key,
|
||||||
|
token_program_info.clone(),
|
||||||
|
pool_mint_info.clone(),
|
||||||
|
dest_user_pool_info.clone(),
|
||||||
|
withdraw_authority_info.clone(),
|
||||||
|
AUTHORITY_WITHDRAW,
|
||||||
|
stake_pool.stake_withdraw_bump_seed,
|
||||||
|
pool_tokens_user,
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if pool_tokens_manager_deposit_fee > 0 {
|
||||||
|
Self::token_mint_to(
|
||||||
|
stake_pool_info.key,
|
||||||
|
token_program_info.clone(),
|
||||||
|
pool_mint_info.clone(),
|
||||||
|
manager_fee_info.clone(),
|
||||||
|
withdraw_authority_info.clone(),
|
||||||
|
AUTHORITY_WITHDRAW,
|
||||||
|
stake_pool.stake_withdraw_bump_seed,
|
||||||
|
pool_tokens_manager_deposit_fee,
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if pool_tokens_referral_fee > 0 {
|
||||||
|
Self::token_mint_to(
|
||||||
|
stake_pool_info.key,
|
||||||
|
token_program_info.clone(),
|
||||||
|
pool_mint_info.clone(),
|
||||||
|
referrer_fee_info.clone(),
|
||||||
|
withdraw_authority_info.clone(),
|
||||||
|
AUTHORITY_WITHDRAW,
|
||||||
|
stake_pool.stake_withdraw_bump_seed,
|
||||||
|
pool_tokens_referral_fee,
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
stake_pool.pool_token_supply = stake_pool
|
||||||
|
.pool_token_supply
|
||||||
|
.checked_add(new_pool_tokens)
|
||||||
|
.ok_or(StakePoolError::CalculationFailure)?;
|
||||||
|
stake_pool.total_stake_lamports = stake_pool
|
||||||
|
.total_stake_lamports
|
||||||
|
.checked_add(deposit_lamports)
|
||||||
|
.ok_or(StakePoolError::CalculationFailure)?;
|
||||||
|
stake_pool.serialize(&mut *stake_pool_info.data.borrow_mut())?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Processes [WithdrawStake](enum.Instruction.html).
|
||||||
|
fn process_withdraw_stake(
|
||||||
program_id: &Pubkey,
|
program_id: &Pubkey,
|
||||||
accounts: &[AccountInfo],
|
accounts: &[AccountInfo],
|
||||||
pool_tokens: u64,
|
pool_tokens: u64,
|
||||||
|
@ -1925,7 +2123,7 @@ impl Processor {
|
||||||
let stake_split_to = next_account_info(account_info_iter)?;
|
let stake_split_to = next_account_info(account_info_iter)?;
|
||||||
let user_stake_authority_info = next_account_info(account_info_iter)?;
|
let user_stake_authority_info = next_account_info(account_info_iter)?;
|
||||||
let user_transfer_authority_info = next_account_info(account_info_iter)?;
|
let user_transfer_authority_info = next_account_info(account_info_iter)?;
|
||||||
let burn_from_info = next_account_info(account_info_iter)?;
|
let burn_from_pool_info = next_account_info(account_info_iter)?;
|
||||||
let manager_fee_info = next_account_info(account_info_iter)?;
|
let manager_fee_info = next_account_info(account_info_iter)?;
|
||||||
let pool_mint_info = next_account_info(account_info_iter)?;
|
let pool_mint_info = next_account_info(account_info_iter)?;
|
||||||
let clock_info = next_account_info(account_info_iter)?;
|
let clock_info = next_account_info(account_info_iter)?;
|
||||||
|
@ -1967,7 +2165,7 @@ impl Processor {
|
||||||
return Err(StakePoolError::InvalidState.into());
|
return Err(StakePoolError::InvalidState.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
let pool_tokens_fee = if stake_pool.manager_fee_account == *burn_from_info.key {
|
let pool_tokens_fee = if stake_pool.manager_fee_account == *burn_from_pool_info.key {
|
||||||
0
|
0
|
||||||
} else {
|
} else {
|
||||||
stake_pool
|
stake_pool
|
||||||
|
@ -2079,7 +2277,7 @@ impl Processor {
|
||||||
|
|
||||||
Self::token_burn(
|
Self::token_burn(
|
||||||
token_program_info.clone(),
|
token_program_info.clone(),
|
||||||
burn_from_info.clone(),
|
burn_from_pool_info.clone(),
|
||||||
pool_mint_info.clone(),
|
pool_mint_info.clone(),
|
||||||
user_transfer_authority_info.clone(),
|
user_transfer_authority_info.clone(),
|
||||||
pool_tokens_burnt,
|
pool_tokens_burnt,
|
||||||
|
@ -2090,7 +2288,7 @@ impl Processor {
|
||||||
stake_split_from.clone(),
|
stake_split_from.clone(),
|
||||||
withdraw_authority_info.clone(),
|
withdraw_authority_info.clone(),
|
||||||
AUTHORITY_WITHDRAW,
|
AUTHORITY_WITHDRAW,
|
||||||
stake_pool.withdraw_bump_seed,
|
stake_pool.stake_withdraw_bump_seed,
|
||||||
withdraw_lamports,
|
withdraw_lamports,
|
||||||
stake_split_to.clone(),
|
stake_split_to.clone(),
|
||||||
)?;
|
)?;
|
||||||
|
@ -2100,7 +2298,7 @@ impl Processor {
|
||||||
stake_split_to.clone(),
|
stake_split_to.clone(),
|
||||||
withdraw_authority_info.clone(),
|
withdraw_authority_info.clone(),
|
||||||
AUTHORITY_WITHDRAW,
|
AUTHORITY_WITHDRAW,
|
||||||
stake_pool.withdraw_bump_seed,
|
stake_pool.stake_withdraw_bump_seed,
|
||||||
user_stake_authority_info.key,
|
user_stake_authority_info.key,
|
||||||
clock_info.clone(),
|
clock_info.clone(),
|
||||||
stake_program_info.clone(),
|
stake_program_info.clone(),
|
||||||
|
@ -2109,7 +2307,7 @@ impl Processor {
|
||||||
if pool_tokens_fee > 0 {
|
if pool_tokens_fee > 0 {
|
||||||
Self::token_transfer(
|
Self::token_transfer(
|
||||||
token_program_info.clone(),
|
token_program_info.clone(),
|
||||||
burn_from_info.clone(),
|
burn_from_pool_info.clone(),
|
||||||
manager_fee_info.clone(),
|
manager_fee_info.clone(),
|
||||||
user_transfer_authority_info.clone(),
|
user_transfer_authority_info.clone(),
|
||||||
pool_tokens_fee,
|
pool_tokens_fee,
|
||||||
|
@ -2175,7 +2373,11 @@ impl Processor {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Processes [SetFee](enum.Instruction.html).
|
/// Processes [SetFee](enum.Instruction.html).
|
||||||
fn process_set_fee(program_id: &Pubkey, accounts: &[AccountInfo], fee: Fee) -> ProgramResult {
|
fn process_set_fee(
|
||||||
|
program_id: &Pubkey,
|
||||||
|
accounts: &[AccountInfo],
|
||||||
|
fee: FeeType,
|
||||||
|
) -> ProgramResult {
|
||||||
let account_info_iter = &mut accounts.iter();
|
let account_info_iter = &mut accounts.iter();
|
||||||
let stake_pool_info = next_account_info(account_info_iter)?;
|
let stake_pool_info = next_account_info(account_info_iter)?;
|
||||||
let manager_info = next_account_info(account_info_iter)?;
|
let manager_info = next_account_info(account_info_iter)?;
|
||||||
|
@ -2190,21 +2392,14 @@ impl Processor {
|
||||||
|
|
||||||
stake_pool.check_manager(manager_info)?;
|
stake_pool.check_manager(manager_info)?;
|
||||||
|
|
||||||
if stake_pool.last_update_epoch < clock.epoch {
|
if fee.can_only_change_next_epoch() && stake_pool.last_update_epoch < clock.epoch {
|
||||||
return Err(StakePoolError::StakeListAndPoolOutOfDate.into());
|
return Err(StakePoolError::StakeListAndPoolOutOfDate.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Numerator should be smaller than or equal to denominator (fee <= 1)
|
fee.check_too_high()?;
|
||||||
if fee.numerator > fee.denominator {
|
fee.check_withdrawal(&stake_pool.withdrawal_fee)?;
|
||||||
msg!(
|
|
||||||
"Fee greater than 100%, numerator {}, denominator {}",
|
|
||||||
fee.numerator,
|
|
||||||
fee.denominator
|
|
||||||
);
|
|
||||||
return Err(StakePoolError::FeeTooHigh.into());
|
|
||||||
}
|
|
||||||
|
|
||||||
stake_pool.next_epoch_fee = Some(fee);
|
stake_pool.update_fee(&fee);
|
||||||
stake_pool.serialize(&mut *stake_pool_info.data.borrow_mut())?;
|
stake_pool.serialize(&mut *stake_pool_info.data.borrow_mut())?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -2232,76 +2427,34 @@ impl Processor {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Processes [SetWithdrawalFee](enum.Instruction.html).
|
/// Processes [SetStakeDepositAuthority/SetSolDepositAuthority](enum.Instruction.html).
|
||||||
fn process_set_withdrawal_fee(
|
fn process_set_deposit_authority(
|
||||||
program_id: &Pubkey,
|
program_id: &Pubkey,
|
||||||
accounts: &[AccountInfo],
|
accounts: &[AccountInfo],
|
||||||
fee: Fee,
|
deposit_type: DepositType,
|
||||||
) -> ProgramResult {
|
) -> ProgramResult {
|
||||||
let account_info_iter = &mut accounts.iter();
|
let account_info_iter = &mut accounts.iter();
|
||||||
let stake_pool_info = next_account_info(account_info_iter)?;
|
let stake_pool_info = next_account_info(account_info_iter)?;
|
||||||
let manager_info = next_account_info(account_info_iter)?;
|
let manager_info = next_account_info(account_info_iter)?;
|
||||||
let clock_info = next_account_info(account_info_iter)?;
|
|
||||||
let clock = &Clock::from_account_info(clock_info)?;
|
let new_sol_deposit_authority = next_account_info(account_info_iter).ok().map(
|
||||||
|
|new_sol_deposit_authority_account_info| *new_sol_deposit_authority_account_info.key,
|
||||||
|
);
|
||||||
|
|
||||||
check_account_owner(stake_pool_info, program_id)?;
|
check_account_owner(stake_pool_info, program_id)?;
|
||||||
let mut stake_pool = try_from_slice_unchecked::<StakePool>(&stake_pool_info.data.borrow())?;
|
let mut stake_pool = try_from_slice_unchecked::<StakePool>(&stake_pool_info.data.borrow())?;
|
||||||
if !stake_pool.is_valid() {
|
if !stake_pool.is_valid() {
|
||||||
return Err(StakePoolError::InvalidState.into());
|
return Err(StakePoolError::InvalidState.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
stake_pool.check_manager(manager_info)?;
|
stake_pool.check_manager(manager_info)?;
|
||||||
|
match deposit_type {
|
||||||
if stake_pool.last_update_epoch < clock.epoch {
|
DepositType::Stake => {
|
||||||
return Err(StakePoolError::StakeListAndPoolOutOfDate.into());
|
stake_pool.stake_deposit_authority = new_sol_deposit_authority.unwrap_or(
|
||||||
}
|
find_deposit_authority_program_address(program_id, stake_pool_info.key).0,
|
||||||
|
|
||||||
// Numerator should be smaller than or equal to denominator (fee <= 1)
|
|
||||||
if fee.numerator > fee.denominator {
|
|
||||||
msg!(
|
|
||||||
"Fee greater than 100%, numerator {}, denominator {}",
|
|
||||||
fee.numerator,
|
|
||||||
fee.denominator
|
|
||||||
);
|
);
|
||||||
return Err(StakePoolError::FeeTooHigh.into());
|
|
||||||
}
|
}
|
||||||
|
DepositType::Sol => stake_pool.sol_deposit_authority = new_sol_deposit_authority,
|
||||||
// If the previous withdrawal fee was 0, we allow the fee to be set to a
|
|
||||||
// maximum of (WITHDRAWAL_BASELINE_FEE * MAX_WITHDRAWAL_FEE_INCREASE)
|
|
||||||
let (old_num, old_denom) = if stake_pool.withdrawal_fee.denominator == 0
|
|
||||||
|| stake_pool.withdrawal_fee.numerator == 0
|
|
||||||
{
|
|
||||||
(
|
|
||||||
WITHDRAWAL_BASELINE_FEE.numerator,
|
|
||||||
WITHDRAWAL_BASELINE_FEE.denominator,
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
(
|
|
||||||
stake_pool.withdrawal_fee.numerator,
|
|
||||||
stake_pool.withdrawal_fee.denominator,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
// Check that new_fee / old_fee <= MAX_WITHDRAWAL_FEE_INCREASE
|
|
||||||
// Program fails if provided numerator or denominator is too large, resulting in overflow
|
|
||||||
if (old_num as u128)
|
|
||||||
.checked_mul(fee.denominator as u128)
|
|
||||||
.map(|x| x.checked_mul(MAX_WITHDRAWAL_FEE_INCREASE.numerator as u128))
|
|
||||||
.ok_or(StakePoolError::CalculationFailure)?
|
|
||||||
< (fee.numerator as u128)
|
|
||||||
.checked_mul(old_denom as u128)
|
|
||||||
.map(|x| x.checked_mul(MAX_WITHDRAWAL_FEE_INCREASE.denominator as u128))
|
|
||||||
.ok_or(StakePoolError::CalculationFailure)?
|
|
||||||
{
|
|
||||||
msg!(
|
|
||||||
"Fee increase exceeds maximum allowed, proposed increase factor ({} / {})",
|
|
||||||
fee.numerator * old_denom,
|
|
||||||
old_num * fee.denominator,
|
|
||||||
);
|
|
||||||
return Err(StakePoolError::FeeIncreaseTooHigh.into());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
stake_pool.next_withdrawal_fee = Some(fee);
|
|
||||||
stake_pool.serialize(&mut *stake_pool_info.data.borrow_mut())?;
|
stake_pool.serialize(&mut *stake_pool_info.data.borrow_mut())?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -2313,10 +2466,20 @@ impl Processor {
|
||||||
StakePoolInstruction::Initialize {
|
StakePoolInstruction::Initialize {
|
||||||
fee,
|
fee,
|
||||||
withdrawal_fee,
|
withdrawal_fee,
|
||||||
|
deposit_fee,
|
||||||
|
referral_fee,
|
||||||
max_validators,
|
max_validators,
|
||||||
} => {
|
} => {
|
||||||
msg!("Instruction: Initialize stake pool");
|
msg!("Instruction: Initialize stake pool");
|
||||||
Self::process_initialize(program_id, accounts, fee, withdrawal_fee, max_validators)
|
Self::process_initialize(
|
||||||
|
program_id,
|
||||||
|
accounts,
|
||||||
|
fee,
|
||||||
|
withdrawal_fee,
|
||||||
|
deposit_fee,
|
||||||
|
referral_fee,
|
||||||
|
max_validators,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
StakePoolInstruction::CreateValidatorStakeAccount => {
|
StakePoolInstruction::CreateValidatorStakeAccount => {
|
||||||
msg!("Instruction: CreateValidatorStakeAccount");
|
msg!("Instruction: CreateValidatorStakeAccount");
|
||||||
|
@ -2370,13 +2533,13 @@ impl Processor {
|
||||||
msg!("Instruction: CleanupRemovedValidatorEntries");
|
msg!("Instruction: CleanupRemovedValidatorEntries");
|
||||||
Self::process_cleanup_removed_validator_entries(program_id, accounts)
|
Self::process_cleanup_removed_validator_entries(program_id, accounts)
|
||||||
}
|
}
|
||||||
StakePoolInstruction::Deposit => {
|
StakePoolInstruction::DepositStake => {
|
||||||
msg!("Instruction: Deposit");
|
msg!("Instruction: DepositStake");
|
||||||
Self::process_deposit(program_id, accounts)
|
Self::process_deposit_stake(program_id, accounts)
|
||||||
}
|
}
|
||||||
StakePoolInstruction::Withdraw(amount) => {
|
StakePoolInstruction::WithdrawStake(amount) => {
|
||||||
msg!("Instruction: Withdraw");
|
msg!("Instruction: WithdrawStake");
|
||||||
Self::process_withdraw(program_id, accounts, amount)
|
Self::process_withdraw_stake(program_id, accounts, amount)
|
||||||
}
|
}
|
||||||
StakePoolInstruction::SetManager => {
|
StakePoolInstruction::SetManager => {
|
||||||
msg!("Instruction: SetManager");
|
msg!("Instruction: SetManager");
|
||||||
|
@ -2390,9 +2553,13 @@ impl Processor {
|
||||||
msg!("Instruction: SetStaker");
|
msg!("Instruction: SetStaker");
|
||||||
Self::process_set_staker(program_id, accounts)
|
Self::process_set_staker(program_id, accounts)
|
||||||
}
|
}
|
||||||
StakePoolInstruction::SetWithdrawalFee { fee } => {
|
StakePoolInstruction::DepositSol(lamports) => {
|
||||||
msg!("Instruction: SetWithdrawalFee");
|
msg!("Instruction: DepositSol");
|
||||||
Self::process_set_withdrawal_fee(program_id, accounts, fee)
|
Self::process_deposit_sol(program_id, accounts, lamports)
|
||||||
|
}
|
||||||
|
StakePoolInstruction::SetDepositAuthority(deposit_type) => {
|
||||||
|
msg!("Instruction: SetDepositAuthority");
|
||||||
|
Self::process_set_deposit_authority(program_id, accounts, deposit_type)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2436,6 +2603,8 @@ impl PrintProgramError for StakePoolError {
|
||||||
StakePoolError::FeeIncreaseTooHigh => msg!("Error: The fee cannot increase by a factor exceeding the stipulated ratio"),
|
StakePoolError::FeeIncreaseTooHigh => msg!("Error: The fee cannot increase by a factor exceeding the stipulated ratio"),
|
||||||
StakePoolError::WithdrawalTooSmall => msg!("Error: Not enough pool tokens provided to withdraw 1-lamport stake"),
|
StakePoolError::WithdrawalTooSmall => msg!("Error: Not enough pool tokens provided to withdraw 1-lamport stake"),
|
||||||
StakePoolError::DepositTooSmall => msg!("Error: Not enough lamports provided for deposit to result in one pool token"),
|
StakePoolError::DepositTooSmall => msg!("Error: Not enough lamports provided for deposit to result in one pool token"),
|
||||||
|
StakePoolError::InvalidStakeDepositAuthority => msg!("Error: Provided stake deposit authority does not match the program's"),
|
||||||
|
StakePoolError::InvalidSolDepositAuthority => msg!("Error: Provided sol deposit authority does not match the program's"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
//! State transition types
|
//! State transition types
|
||||||
|
|
||||||
use {
|
use {
|
||||||
crate::{big_vec::BigVec, error::StakePoolError, stake_program::Lockup},
|
crate::{
|
||||||
|
big_vec::BigVec, error::StakePoolError, stake_program::Lockup, MAX_WITHDRAWAL_FEE_INCREASE,
|
||||||
|
WITHDRAWAL_BASELINE_FEE,
|
||||||
|
},
|
||||||
borsh::{BorshDeserialize, BorshSchema, BorshSerialize},
|
borsh::{BorshDeserialize, BorshSchema, BorshSerialize},
|
||||||
num_derive::FromPrimitive,
|
num_derive::FromPrimitive,
|
||||||
num_traits::FromPrimitive,
|
num_traits::FromPrimitive,
|
||||||
|
@ -16,6 +19,7 @@ use {
|
||||||
},
|
},
|
||||||
spl_math::checked_ceil_div::CheckedCeilDiv,
|
spl_math::checked_ceil_div::CheckedCeilDiv,
|
||||||
std::convert::TryFrom,
|
std::convert::TryFrom,
|
||||||
|
std::{fmt, matches},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Enum representing the account type managed by the program
|
/// Enum representing the account type managed by the program
|
||||||
|
@ -49,7 +53,7 @@ pub struct StakePool {
|
||||||
/// distribution
|
/// distribution
|
||||||
pub staker: Pubkey,
|
pub staker: Pubkey,
|
||||||
|
|
||||||
/// Deposit authority
|
/// Stake deposit authority
|
||||||
///
|
///
|
||||||
/// If a depositor pubkey is specified on initialization, then deposits must be
|
/// If a depositor pubkey is specified on initialization, then deposits must be
|
||||||
/// signed by this authority. If no deposit authority is specified,
|
/// signed by this authority. If no deposit authority is specified,
|
||||||
|
@ -58,11 +62,11 @@ pub struct StakePool {
|
||||||
/// &[&stake_pool_address.to_bytes()[..32], b"deposit"],
|
/// &[&stake_pool_address.to_bytes()[..32], b"deposit"],
|
||||||
/// program_id,
|
/// program_id,
|
||||||
/// )`
|
/// )`
|
||||||
pub deposit_authority: Pubkey,
|
pub stake_deposit_authority: Pubkey,
|
||||||
|
|
||||||
/// Withdrawal authority bump seed
|
/// Stake withdrawal authority bump seed
|
||||||
/// for `create_program_address(&[state::StakePool account, "withdrawal"])`
|
/// for `create_program_address(&[state::StakePool account, "withdrawal"])`
|
||||||
pub withdraw_bump_seed: u8,
|
pub stake_withdraw_bump_seed: u8,
|
||||||
|
|
||||||
/// Validator stake list storage account
|
/// Validator stake list storage account
|
||||||
pub validator_list: Pubkey,
|
pub validator_list: Pubkey,
|
||||||
|
@ -105,8 +109,8 @@ pub struct StakePool {
|
||||||
/// Preferred withdraw validator vote account pubkey
|
/// Preferred withdraw validator vote account pubkey
|
||||||
pub preferred_withdraw_validator_vote_address: Option<Pubkey>,
|
pub preferred_withdraw_validator_vote_address: Option<Pubkey>,
|
||||||
|
|
||||||
/// Fee assessed on deposits
|
/// Fee assessed on stake deposits
|
||||||
pub deposit_fee: Fee,
|
pub stake_deposit_fee: Fee,
|
||||||
|
|
||||||
/// Fee assessed on withdrawals
|
/// Fee assessed on withdrawals
|
||||||
pub withdrawal_fee: Fee,
|
pub withdrawal_fee: Fee,
|
||||||
|
@ -114,14 +118,28 @@ pub struct StakePool {
|
||||||
/// Future withdrawal fee, to be set for the following epoch
|
/// Future withdrawal fee, to be set for the following epoch
|
||||||
pub next_withdrawal_fee: Option<Fee>,
|
pub next_withdrawal_fee: Option<Fee>,
|
||||||
|
|
||||||
/// Fees paid out to referrers on referred deposits.
|
/// Fees paid out to referrers on referred stake deposits.
|
||||||
/// Expressed as a percentage (0 - 100) of deposit fees.
|
/// Expressed as a percentage (0 - 100) of deposit fees.
|
||||||
/// i.e. `deposit_fee`% is collected as deposit fees for every deposit
|
/// i.e. `stake_deposit_fee`% of stake deposited is collected as deposit fees for every deposit
|
||||||
/// and `referral_fee`% of the collected deposit fees is paid out to the referrer
|
/// and `stake_referral_fee`% of the collected stake deposit fees is paid out to the referrer
|
||||||
pub referral_fee: u8,
|
pub stake_referral_fee: u8,
|
||||||
|
|
||||||
|
/// Toggles whether the `DepositSol` instruction requires a signature from
|
||||||
|
/// the `deposit_authority`
|
||||||
|
pub sol_deposit_authority: Option<Pubkey>,
|
||||||
|
|
||||||
|
/// Fee assessed on SOL deposits
|
||||||
|
pub sol_deposit_fee: Fee,
|
||||||
|
|
||||||
|
/// Fees paid out to referrers on referred SOL deposits.
|
||||||
|
/// Expressed as a percentage (0 - 100) of SOL deposit fees.
|
||||||
|
/// i.e. `sol_deposit_fee`% of SOL deposited is collected as deposit fees for every deposit
|
||||||
|
/// and `sol_referral_fee`% of the collected SOL deposit fees is paid out to the referrer
|
||||||
|
pub sol_referral_fee: u8,
|
||||||
}
|
}
|
||||||
impl StakePool {
|
impl StakePool {
|
||||||
/// calculate the pool tokens that should be minted for a deposit of `stake_lamports`
|
/// calculate the pool tokens that should be minted for a deposit of `stake_lamports`
|
||||||
|
#[inline]
|
||||||
pub fn calc_pool_tokens_for_deposit(&self, stake_lamports: u64) -> Option<u64> {
|
pub fn calc_pool_tokens_for_deposit(&self, stake_lamports: u64) -> Option<u64> {
|
||||||
if self.total_stake_lamports == 0 || self.pool_token_supply == 0 {
|
if self.total_stake_lamports == 0 || self.pool_token_supply == 0 {
|
||||||
return Some(stake_lamports);
|
return Some(stake_lamports);
|
||||||
|
@ -135,6 +153,7 @@ impl StakePool {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// calculate lamports amount on withdrawal
|
/// calculate lamports amount on withdrawal
|
||||||
|
#[inline]
|
||||||
pub fn calc_lamports_withdraw_amount(&self, pool_tokens: u64) -> Option<u64> {
|
pub fn calc_lamports_withdraw_amount(&self, pool_tokens: u64) -> Option<u64> {
|
||||||
// `checked_ceil_div` returns `None` for a 0 quotient result, but in this
|
// `checked_ceil_div` returns `None` for a 0 quotient result, but in this
|
||||||
// case, a return of 0 is valid for small amounts of pool tokens. So
|
// case, a return of 0 is valid for small amounts of pool tokens. So
|
||||||
|
@ -150,15 +169,51 @@ impl StakePool {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// calculate pool tokens to be deducted as withdrawal fees
|
/// calculate pool tokens to be deducted as withdrawal fees
|
||||||
|
#[inline]
|
||||||
pub fn calc_pool_tokens_withdrawal_fee(&self, pool_tokens: u64) -> Option<u64> {
|
pub fn calc_pool_tokens_withdrawal_fee(&self, pool_tokens: u64) -> Option<u64> {
|
||||||
u64::try_from(self.withdrawal_fee.apply(pool_tokens)?).ok()
|
u64::try_from(self.withdrawal_fee.apply(pool_tokens)?).ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// calculate pool tokens to be deducted as stake deposit fees
|
||||||
|
#[inline]
|
||||||
|
pub fn calc_pool_tokens_stake_deposit_fee(&self, pool_tokens_minted: u64) -> Option<u64> {
|
||||||
|
u64::try_from(self.stake_deposit_fee.apply(pool_tokens_minted)?).ok()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// calculate pool tokens to be deducted from deposit fees as referral fees
|
||||||
|
#[inline]
|
||||||
|
pub fn calc_pool_tokens_stake_referral_fee(&self, stake_deposit_fee: u64) -> Option<u64> {
|
||||||
|
u64::try_from(
|
||||||
|
(stake_deposit_fee as u128)
|
||||||
|
.checked_mul(self.stake_referral_fee as u128)?
|
||||||
|
.checked_div(100u128)?,
|
||||||
|
)
|
||||||
|
.ok()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// calculate pool tokens to be deducted as SOL deposit fees
|
||||||
|
#[inline]
|
||||||
|
pub fn calc_pool_tokens_sol_deposit_fee(&self, pool_tokens_minted: u64) -> Option<u64> {
|
||||||
|
u64::try_from(self.sol_deposit_fee.apply(pool_tokens_minted)?).ok()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// calculate pool tokens to be deducted from SOL deposit fees as referral fees
|
||||||
|
#[inline]
|
||||||
|
pub fn calc_pool_tokens_sol_referral_fee(&self, sol_deposit_fee: u64) -> Option<u64> {
|
||||||
|
u64::try_from(
|
||||||
|
(sol_deposit_fee as u128)
|
||||||
|
.checked_mul(self.sol_referral_fee as u128)?
|
||||||
|
.checked_div(100u128)?,
|
||||||
|
)
|
||||||
|
.ok()
|
||||||
|
}
|
||||||
|
|
||||||
/// Calculate the fee in pool tokens that goes to the manager
|
/// Calculate the fee in pool tokens that goes to the manager
|
||||||
///
|
///
|
||||||
/// This function assumes that `reward_lamports` has not already been added
|
/// This function assumes that `reward_lamports` has not already been added
|
||||||
/// to the stake pool's `total_stake_lamports`
|
/// to the stake pool's `total_stake_lamports`
|
||||||
pub fn calc_fee_amount(&self, reward_lamports: u64) -> Option<u64> {
|
#[inline]
|
||||||
|
pub fn calc_epoch_fee_amount(&self, reward_lamports: u64) -> Option<u64> {
|
||||||
if reward_lamports == 0 {
|
if reward_lamports == 0 {
|
||||||
return Some(0);
|
return Some(0);
|
||||||
}
|
}
|
||||||
|
@ -178,7 +233,7 @@ impl StakePool {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks that the withdraw or deposit authority is valid
|
/// Checks that the withdraw or deposit authority is valid
|
||||||
fn check_authority(
|
fn check_program_derived_authority(
|
||||||
authority_address: &Pubkey,
|
authority_address: &Pubkey,
|
||||||
program_id: &Pubkey,
|
program_id: &Pubkey,
|
||||||
stake_pool_address: &Pubkey,
|
stake_pool_address: &Pubkey,
|
||||||
|
@ -207,33 +262,55 @@ impl StakePool {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks that the withdraw authority is valid
|
/// Checks that the withdraw authority is valid
|
||||||
|
#[inline]
|
||||||
pub(crate) fn check_authority_withdraw(
|
pub(crate) fn check_authority_withdraw(
|
||||||
&self,
|
&self,
|
||||||
withdraw_authority: &Pubkey,
|
withdraw_authority: &Pubkey,
|
||||||
program_id: &Pubkey,
|
program_id: &Pubkey,
|
||||||
stake_pool_address: &Pubkey,
|
stake_pool_address: &Pubkey,
|
||||||
) -> Result<(), ProgramError> {
|
) -> Result<(), ProgramError> {
|
||||||
Self::check_authority(
|
Self::check_program_derived_authority(
|
||||||
withdraw_authority,
|
withdraw_authority,
|
||||||
program_id,
|
program_id,
|
||||||
stake_pool_address,
|
stake_pool_address,
|
||||||
crate::AUTHORITY_WITHDRAW,
|
crate::AUTHORITY_WITHDRAW,
|
||||||
self.withdraw_bump_seed,
|
self.stake_withdraw_bump_seed,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
/// Checks that the deposit authority is valid
|
/// Checks that the deposit authority is valid
|
||||||
pub(crate) fn check_deposit_authority(
|
#[inline]
|
||||||
|
pub(crate) fn check_stake_deposit_authority(
|
||||||
&self,
|
&self,
|
||||||
deposit_authority: &Pubkey,
|
stake_deposit_authority: &Pubkey,
|
||||||
) -> Result<(), ProgramError> {
|
) -> Result<(), ProgramError> {
|
||||||
if self.deposit_authority == *deposit_authority {
|
if self.stake_deposit_authority == *stake_deposit_authority {
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(StakePoolError::InvalidProgramAddress.into())
|
Err(StakePoolError::InvalidStakeDepositAuthority.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Checks that the deposit authority is valid
|
||||||
|
/// Does nothing if `sol_deposit_authority` is currently not set
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn check_sol_deposit_authority(
|
||||||
|
&self,
|
||||||
|
sol_deposit_authority: &AccountInfo,
|
||||||
|
) -> Result<(), ProgramError> {
|
||||||
|
if let Some(auth) = self.sol_deposit_authority {
|
||||||
|
if auth != *sol_deposit_authority.key {
|
||||||
|
return Err(StakePoolError::InvalidSolDepositAuthority.into());
|
||||||
|
}
|
||||||
|
if !sol_deposit_authority.is_signer {
|
||||||
|
msg!("SOL Deposit authority signature missing");
|
||||||
|
return Err(StakePoolError::SignatureMissing.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Check staker validity and signature
|
/// Check staker validity and signature
|
||||||
|
#[inline]
|
||||||
pub(crate) fn check_mint(&self, mint_info: &AccountInfo) -> Result<(), ProgramError> {
|
pub(crate) fn check_mint(&self, mint_info: &AccountInfo) -> Result<(), ProgramError> {
|
||||||
if *mint_info.key != self.pool_mint {
|
if *mint_info.key != self.pool_mint {
|
||||||
Err(StakePoolError::WrongPoolMint.into())
|
Err(StakePoolError::WrongPoolMint.into())
|
||||||
|
@ -319,6 +396,18 @@ impl StakePool {
|
||||||
pub fn is_uninitialized(&self) -> bool {
|
pub fn is_uninitialized(&self) -> bool {
|
||||||
self.account_type == AccountType::Uninitialized
|
self.account_type == AccountType::Uninitialized
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Updates one of the StakePool's fees.
|
||||||
|
pub fn update_fee(&mut self, fee: &FeeType) {
|
||||||
|
match fee {
|
||||||
|
FeeType::SolReferral(new_fee) => self.sol_referral_fee = *new_fee,
|
||||||
|
FeeType::StakeReferral(new_fee) => self.stake_referral_fee = *new_fee,
|
||||||
|
FeeType::Epoch(new_fee) => self.next_epoch_fee = Some(*new_fee),
|
||||||
|
FeeType::Withdrawal(new_fee) => self.next_withdrawal_fee = Some(*new_fee),
|
||||||
|
FeeType::SolDeposit(new_fee) => self.sol_deposit_fee = *new_fee,
|
||||||
|
FeeType::StakeDeposit(new_fee) => self.stake_deposit_fee = *new_fee,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Storage list for all validator stake accounts in the pool.
|
/// Storage list for all validator stake accounts in the pool.
|
||||||
|
@ -579,6 +668,96 @@ impl Fee {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Fee {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "{}/{}", self.numerator, self.denominator)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The type of fees that can be set on the stake pool
|
||||||
|
#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)]
|
||||||
|
pub enum FeeType {
|
||||||
|
/// Referral fees for SOL deposits
|
||||||
|
SolReferral(u8),
|
||||||
|
/// Referral fees for stake deposits
|
||||||
|
StakeReferral(u8),
|
||||||
|
/// Management fee paid per epoch
|
||||||
|
Epoch(Fee),
|
||||||
|
/// Withdrawal fee
|
||||||
|
Withdrawal(Fee),
|
||||||
|
/// Deposit fee for SOL deposits
|
||||||
|
SolDeposit(Fee),
|
||||||
|
/// Deposit fee for stake deposits
|
||||||
|
StakeDeposit(Fee),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FeeType {
|
||||||
|
/// Checks if the provided fee is too high, returning an error if so
|
||||||
|
pub fn check_too_high(&self) -> Result<(), StakePoolError> {
|
||||||
|
let too_high = match self {
|
||||||
|
Self::SolReferral(pct) => *pct > 100u8,
|
||||||
|
Self::StakeReferral(pct) => *pct > 100u8,
|
||||||
|
Self::Epoch(fee) => fee.numerator > fee.denominator,
|
||||||
|
Self::Withdrawal(fee) => fee.numerator > fee.denominator,
|
||||||
|
Self::SolDeposit(fee) => fee.numerator > fee.denominator,
|
||||||
|
Self::StakeDeposit(fee) => fee.numerator > fee.denominator,
|
||||||
|
};
|
||||||
|
if too_high {
|
||||||
|
msg!("Fee greater than 100%: {:?}", self);
|
||||||
|
return Err(StakePoolError::FeeTooHigh);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Withdrawal fees have some additional restrictions,
|
||||||
|
/// this fn checks if those are met, returning an error if not.
|
||||||
|
/// Does nothing and returns Ok if fee type is not withdrawal
|
||||||
|
pub fn check_withdrawal(&self, old_withdrawal_fee: &Fee) -> Result<(), StakePoolError> {
|
||||||
|
let fee = match self {
|
||||||
|
Self::Withdrawal(fee) => fee,
|
||||||
|
_ => return Ok(()),
|
||||||
|
};
|
||||||
|
|
||||||
|
// If the previous withdrawal fee was 0, we allow the fee to be set to a
|
||||||
|
// maximum of (WITHDRAWAL_BASELINE_FEE * MAX_WITHDRAWAL_FEE_INCREASE)
|
||||||
|
let (old_num, old_denom) =
|
||||||
|
if old_withdrawal_fee.denominator == 0 || old_withdrawal_fee.numerator == 0 {
|
||||||
|
(
|
||||||
|
WITHDRAWAL_BASELINE_FEE.numerator,
|
||||||
|
WITHDRAWAL_BASELINE_FEE.denominator,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
(old_withdrawal_fee.numerator, old_withdrawal_fee.denominator)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Check that new_fee / old_fee <= MAX_WITHDRAWAL_FEE_INCREASE
|
||||||
|
// Program fails if provided numerator or denominator is too large, resulting in overflow
|
||||||
|
if (old_num as u128)
|
||||||
|
.checked_mul(fee.denominator as u128)
|
||||||
|
.map(|x| x.checked_mul(MAX_WITHDRAWAL_FEE_INCREASE.numerator as u128))
|
||||||
|
.ok_or(StakePoolError::CalculationFailure)?
|
||||||
|
< (fee.numerator as u128)
|
||||||
|
.checked_mul(old_denom as u128)
|
||||||
|
.map(|x| x.checked_mul(MAX_WITHDRAWAL_FEE_INCREASE.denominator as u128))
|
||||||
|
.ok_or(StakePoolError::CalculationFailure)?
|
||||||
|
{
|
||||||
|
msg!(
|
||||||
|
"Fee increase exceeds maximum allowed, proposed increase factor ({} / {})",
|
||||||
|
fee.numerator * old_denom,
|
||||||
|
old_num * fee.denominator,
|
||||||
|
);
|
||||||
|
return Err(StakePoolError::FeeIncreaseTooHigh);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns if the contained fee can only be updated earliest on the next epoch
|
||||||
|
#[inline]
|
||||||
|
pub fn can_only_change_next_epoch(&self) -> bool {
|
||||||
|
matches!(self, Self::Withdrawal(_) | Self::Epoch(_))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use {
|
use {
|
||||||
|
@ -779,7 +958,7 @@ mod test {
|
||||||
..StakePool::default()
|
..StakePool::default()
|
||||||
};
|
};
|
||||||
let reward_lamports = 10 * LAMPORTS_PER_SOL;
|
let reward_lamports = 10 * LAMPORTS_PER_SOL;
|
||||||
let pool_token_fee = stake_pool.calc_fee_amount(reward_lamports).unwrap();
|
let pool_token_fee = stake_pool.calc_epoch_fee_amount(reward_lamports).unwrap();
|
||||||
|
|
||||||
stake_pool.total_stake_lamports += reward_lamports;
|
stake_pool.total_stake_lamports += reward_lamports;
|
||||||
stake_pool.pool_token_supply += pool_token_fee;
|
stake_pool.pool_token_supply += pool_token_fee;
|
||||||
|
@ -815,7 +994,7 @@ mod test {
|
||||||
..StakePool::default()
|
..StakePool::default()
|
||||||
};
|
};
|
||||||
let rewards = 10;
|
let rewards = 10;
|
||||||
let fee = stake_pool.calc_fee_amount(rewards).unwrap();
|
let fee = stake_pool.calc_epoch_fee_amount(rewards).unwrap();
|
||||||
assert_eq!(fee, rewards);
|
assert_eq!(fee, rewards);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -832,7 +1011,7 @@ mod test {
|
||||||
fee,
|
fee,
|
||||||
..StakePool::default()
|
..StakePool::default()
|
||||||
};
|
};
|
||||||
let pool_token_fee = stake_pool.calc_fee_amount(reward_lamports).unwrap();
|
let pool_token_fee = stake_pool.calc_epoch_fee_amount(reward_lamports).unwrap();
|
||||||
|
|
||||||
stake_pool.total_stake_lamports += reward_lamports;
|
stake_pool.total_stake_lamports += reward_lamports;
|
||||||
stake_pool.pool_token_supply += pool_token_fee;
|
stake_pool.pool_token_supply += pool_token_fee;
|
||||||
|
|
|
@ -41,7 +41,7 @@ async fn setup() -> (
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let deposit_info = simple_deposit(
|
let deposit_info = simple_deposit_stake(
|
||||||
&mut banks_client,
|
&mut banks_client,
|
||||||
&payer,
|
&payer,
|
||||||
&recent_blockhash,
|
&recent_blockhash,
|
||||||
|
|
|
@ -212,7 +212,9 @@ async fn success() {
|
||||||
// Check minted tokens
|
// Check minted tokens
|
||||||
let user_token_balance =
|
let user_token_balance =
|
||||||
get_token_balance(&mut context.banks_client, &pool_token_account).await;
|
get_token_balance(&mut context.banks_client, &pool_token_account).await;
|
||||||
assert_eq!(user_token_balance, tokens_issued);
|
let tokens_issued_user =
|
||||||
|
tokens_issued - stake_pool_accounts.calculate_deposit_fee(tokens_issued);
|
||||||
|
assert_eq!(user_token_balance, tokens_issued_user);
|
||||||
|
|
||||||
// Check balances in validator stake account list storage
|
// Check balances in validator stake account list storage
|
||||||
let validator_list = get_account(
|
let validator_list = get_account(
|
||||||
|
@ -272,12 +274,14 @@ async fn fail_with_wrong_stake_program_id() {
|
||||||
let accounts = vec![
|
let accounts = vec![
|
||||||
AccountMeta::new(stake_pool_accounts.stake_pool.pubkey(), false),
|
AccountMeta::new(stake_pool_accounts.stake_pool.pubkey(), false),
|
||||||
AccountMeta::new(stake_pool_accounts.validator_list.pubkey(), false),
|
AccountMeta::new(stake_pool_accounts.validator_list.pubkey(), false),
|
||||||
AccountMeta::new_readonly(stake_pool_accounts.deposit_authority, false),
|
AccountMeta::new_readonly(stake_pool_accounts.stake_deposit_authority, false),
|
||||||
AccountMeta::new_readonly(stake_pool_accounts.withdraw_authority, false),
|
AccountMeta::new_readonly(stake_pool_accounts.withdraw_authority, false),
|
||||||
AccountMeta::new(deposit_stake, false),
|
AccountMeta::new(deposit_stake, false),
|
||||||
AccountMeta::new(validator_stake_account.stake_account, false),
|
AccountMeta::new(validator_stake_account.stake_account, false),
|
||||||
AccountMeta::new(stake_pool_accounts.reserve_stake.pubkey(), false),
|
AccountMeta::new(stake_pool_accounts.reserve_stake.pubkey(), false),
|
||||||
AccountMeta::new(pool_token_account, false),
|
AccountMeta::new(pool_token_account, false),
|
||||||
|
AccountMeta::new(stake_pool_accounts.pool_fee_account.pubkey(), false),
|
||||||
|
AccountMeta::new(stake_pool_accounts.pool_fee_account.pubkey(), false),
|
||||||
AccountMeta::new(stake_pool_accounts.pool_mint.pubkey(), false),
|
AccountMeta::new(stake_pool_accounts.pool_mint.pubkey(), false),
|
||||||
AccountMeta::new_readonly(sysvar::clock::id(), false),
|
AccountMeta::new_readonly(sysvar::clock::id(), false),
|
||||||
AccountMeta::new_readonly(sysvar::stake_history::id(), false),
|
AccountMeta::new_readonly(sysvar::stake_history::id(), false),
|
||||||
|
@ -287,7 +291,7 @@ async fn fail_with_wrong_stake_program_id() {
|
||||||
let instruction = Instruction {
|
let instruction = Instruction {
|
||||||
program_id: id(),
|
program_id: id(),
|
||||||
accounts,
|
accounts,
|
||||||
data: instruction::StakePoolInstruction::Deposit
|
data: instruction::StakePoolInstruction::DepositStake
|
||||||
.try_to_vec()
|
.try_to_vec()
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
};
|
};
|
||||||
|
@ -325,7 +329,7 @@ async fn fail_with_wrong_token_program_id() {
|
||||||
let wrong_token_program = Keypair::new();
|
let wrong_token_program = Keypair::new();
|
||||||
|
|
||||||
let mut transaction = Transaction::new_with_payer(
|
let mut transaction = Transaction::new_with_payer(
|
||||||
&instruction::deposit(
|
&instruction::deposit_stake(
|
||||||
&id(),
|
&id(),
|
||||||
&stake_pool_accounts.stake_pool.pubkey(),
|
&stake_pool_accounts.stake_pool.pubkey(),
|
||||||
&stake_pool_accounts.validator_list.pubkey(),
|
&stake_pool_accounts.validator_list.pubkey(),
|
||||||
|
@ -335,6 +339,8 @@ async fn fail_with_wrong_token_program_id() {
|
||||||
&validator_stake_account.stake_account,
|
&validator_stake_account.stake_account,
|
||||||
&stake_pool_accounts.reserve_stake.pubkey(),
|
&stake_pool_accounts.reserve_stake.pubkey(),
|
||||||
&pool_token_account,
|
&pool_token_account,
|
||||||
|
&stake_pool_accounts.pool_fee_account.pubkey(),
|
||||||
|
&stake_pool_accounts.pool_fee_account.pubkey(),
|
||||||
&stake_pool_accounts.pool_mint.pubkey(),
|
&stake_pool_accounts.pool_mint.pubkey(),
|
||||||
&wrong_token_program.pubkey(),
|
&wrong_token_program.pubkey(),
|
||||||
),
|
),
|
||||||
|
@ -567,7 +573,9 @@ async fn fail_with_wrong_mint_for_receiver_acc() {
|
||||||
let program_error = token_error::TokenError::MintMismatch as u32;
|
let program_error = token_error::TokenError::MintMismatch as u32;
|
||||||
assert_eq!(error_index, program_error);
|
assert_eq!(error_index, program_error);
|
||||||
}
|
}
|
||||||
_ => panic!("Wrong error occurs while try to deposit with wrong mint fro receiver account"),
|
_ => {
|
||||||
|
panic!("Wrong error occurs while try to deposit with wrong mint from receiver account")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -578,10 +586,11 @@ async fn fail_with_uninitialized_validator_list() {} // TODO
|
||||||
async fn fail_with_out_of_dated_pool_balances() {} // TODO
|
async fn fail_with_out_of_dated_pool_balances() {} // TODO
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn success_with_deposit_authority() {
|
async fn success_with_stake_deposit_authority() {
|
||||||
let (mut banks_client, payer, recent_blockhash) = program_test().start().await;
|
let (mut banks_client, payer, recent_blockhash) = program_test().start().await;
|
||||||
let deposit_authority = Keypair::new();
|
let stake_deposit_authority = Keypair::new();
|
||||||
let stake_pool_accounts = StakePoolAccounts::new_with_deposit_authority(deposit_authority);
|
let stake_pool_accounts =
|
||||||
|
StakePoolAccounts::new_with_stake_deposit_authority(stake_deposit_authority);
|
||||||
stake_pool_accounts
|
stake_pool_accounts
|
||||||
.initialize_stake_pool(&mut banks_client, &payer, &recent_blockhash, 1)
|
.initialize_stake_pool(&mut banks_client, &payer, &recent_blockhash, 1)
|
||||||
.await
|
.await
|
||||||
|
@ -659,10 +668,11 @@ async fn success_with_deposit_authority() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn fail_without_deposit_authority_signature() {
|
async fn fail_without_stake_deposit_authority_signature() {
|
||||||
let (mut banks_client, payer, recent_blockhash) = program_test().start().await;
|
let (mut banks_client, payer, recent_blockhash) = program_test().start().await;
|
||||||
let deposit_authority = Keypair::new();
|
let stake_deposit_authority = Keypair::new();
|
||||||
let mut stake_pool_accounts = StakePoolAccounts::new_with_deposit_authority(deposit_authority);
|
let mut stake_pool_accounts =
|
||||||
|
StakePoolAccounts::new_with_stake_deposit_authority(stake_deposit_authority);
|
||||||
stake_pool_accounts
|
stake_pool_accounts
|
||||||
.initialize_stake_pool(&mut banks_client, &payer, &recent_blockhash, 1)
|
.initialize_stake_pool(&mut banks_client, &payer, &recent_blockhash, 1)
|
||||||
.await
|
.await
|
||||||
|
@ -726,8 +736,8 @@ async fn fail_without_deposit_authority_signature() {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let wrong_depositor = Keypair::new();
|
let wrong_depositor = Keypair::new();
|
||||||
stake_pool_accounts.deposit_authority = wrong_depositor.pubkey();
|
stake_pool_accounts.stake_deposit_authority = wrong_depositor.pubkey();
|
||||||
stake_pool_accounts.deposit_authority_keypair = Some(wrong_depositor);
|
stake_pool_accounts.stake_deposit_authority_keypair = Some(wrong_depositor);
|
||||||
|
|
||||||
let error = stake_pool_accounts
|
let error = stake_pool_accounts
|
||||||
.deposit_stake(
|
.deposit_stake(
|
||||||
|
@ -747,7 +757,7 @@ async fn fail_without_deposit_authority_signature() {
|
||||||
TransactionError::InstructionError(_, InstructionError::Custom(error_index)) => {
|
TransactionError::InstructionError(_, InstructionError::Custom(error_index)) => {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
error_index,
|
error_index,
|
||||||
error::StakePoolError::InvalidProgramAddress as u32
|
error::StakePoolError::InvalidStakeDepositAuthority as u32
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
_ => panic!("Wrong error occurs while try to make a deposit with wrong stake program ID"),
|
_ => panic!("Wrong error occurs while try to make a deposit with wrong stake program ID"),
|
||||||
|
@ -843,3 +853,113 @@ async fn fail_with_wrong_preferred_deposit() {
|
||||||
_ => panic!("Wrong error occurs while try to make a deposit with wrong stake program ID"),
|
_ => panic!("Wrong error occurs while try to make a deposit with wrong stake program ID"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn success_with_referral_fee() {
|
||||||
|
let (
|
||||||
|
mut context,
|
||||||
|
stake_pool_accounts,
|
||||||
|
validator_stake_account,
|
||||||
|
user,
|
||||||
|
deposit_stake,
|
||||||
|
pool_token_account,
|
||||||
|
stake_lamports,
|
||||||
|
) = setup().await;
|
||||||
|
|
||||||
|
let referrer = Keypair::new();
|
||||||
|
let referrer_token_account = Keypair::new();
|
||||||
|
create_token_account(
|
||||||
|
&mut context.banks_client,
|
||||||
|
&context.payer,
|
||||||
|
&context.last_blockhash,
|
||||||
|
&referrer_token_account,
|
||||||
|
&stake_pool_accounts.pool_mint.pubkey(),
|
||||||
|
&referrer.pubkey(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let referrer_balance_pre =
|
||||||
|
get_token_balance(&mut context.banks_client, &referrer_token_account.pubkey()).await;
|
||||||
|
|
||||||
|
let mut transaction = Transaction::new_with_payer(
|
||||||
|
&instruction::deposit_stake(
|
||||||
|
&id(),
|
||||||
|
&stake_pool_accounts.stake_pool.pubkey(),
|
||||||
|
&stake_pool_accounts.validator_list.pubkey(),
|
||||||
|
&stake_pool_accounts.withdraw_authority,
|
||||||
|
&deposit_stake,
|
||||||
|
&user.pubkey(),
|
||||||
|
&validator_stake_account.stake_account,
|
||||||
|
&stake_pool_accounts.reserve_stake.pubkey(),
|
||||||
|
&pool_token_account,
|
||||||
|
&stake_pool_accounts.pool_fee_account.pubkey(),
|
||||||
|
&referrer_token_account.pubkey(),
|
||||||
|
&stake_pool_accounts.pool_mint.pubkey(),
|
||||||
|
&spl_token::id(),
|
||||||
|
),
|
||||||
|
Some(&context.payer.pubkey()),
|
||||||
|
);
|
||||||
|
transaction.sign(&[&context.payer, &user], context.last_blockhash);
|
||||||
|
context
|
||||||
|
.banks_client
|
||||||
|
.process_transaction(transaction)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let referrer_balance_post =
|
||||||
|
get_token_balance(&mut context.banks_client, &referrer_token_account.pubkey()).await;
|
||||||
|
let referral_fee = stake_pool_accounts
|
||||||
|
.calculate_referral_fee(stake_pool_accounts.calculate_deposit_fee(stake_lamports));
|
||||||
|
assert!(referral_fee > 0);
|
||||||
|
assert_eq!(referrer_balance_pre + referral_fee, referrer_balance_post);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn fail_with_invalid_referrer() {
|
||||||
|
let (
|
||||||
|
mut context,
|
||||||
|
stake_pool_accounts,
|
||||||
|
validator_stake_account,
|
||||||
|
user,
|
||||||
|
deposit_stake,
|
||||||
|
pool_token_account,
|
||||||
|
_stake_lamports,
|
||||||
|
) = setup().await;
|
||||||
|
|
||||||
|
let invalid_token_account = Keypair::new();
|
||||||
|
|
||||||
|
let mut transaction = Transaction::new_with_payer(
|
||||||
|
&instruction::deposit_stake(
|
||||||
|
&id(),
|
||||||
|
&stake_pool_accounts.stake_pool.pubkey(),
|
||||||
|
&stake_pool_accounts.validator_list.pubkey(),
|
||||||
|
&stake_pool_accounts.withdraw_authority,
|
||||||
|
&deposit_stake,
|
||||||
|
&user.pubkey(),
|
||||||
|
&validator_stake_account.stake_account,
|
||||||
|
&stake_pool_accounts.reserve_stake.pubkey(),
|
||||||
|
&pool_token_account,
|
||||||
|
&stake_pool_accounts.pool_fee_account.pubkey(),
|
||||||
|
&invalid_token_account.pubkey(),
|
||||||
|
&stake_pool_accounts.pool_mint.pubkey(),
|
||||||
|
&spl_token::id(),
|
||||||
|
),
|
||||||
|
Some(&context.payer.pubkey()),
|
||||||
|
);
|
||||||
|
transaction.sign(&[&context.payer, &user], context.last_blockhash);
|
||||||
|
let transaction_error = context
|
||||||
|
.banks_client
|
||||||
|
.process_transaction(transaction)
|
||||||
|
.await
|
||||||
|
.err()
|
||||||
|
.unwrap()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
match transaction_error {
|
||||||
|
TransactionError::InstructionError(_, InstructionError::InvalidAccountData) => (),
|
||||||
|
_ => panic!(
|
||||||
|
"Wrong error occurs while try to make a deposit with an invalid referrer account"
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,493 @@
|
||||||
|
#![cfg(feature = "test-bpf")]
|
||||||
|
|
||||||
|
mod helpers;
|
||||||
|
|
||||||
|
use {
|
||||||
|
helpers::*,
|
||||||
|
solana_program::{
|
||||||
|
borsh::try_from_slice_unchecked, instruction::InstructionError, pubkey::Pubkey,
|
||||||
|
},
|
||||||
|
solana_program_test::*,
|
||||||
|
solana_sdk::{
|
||||||
|
signature::{Keypair, Signer},
|
||||||
|
transaction::Transaction,
|
||||||
|
transaction::TransactionError,
|
||||||
|
transport::TransportError,
|
||||||
|
},
|
||||||
|
spl_stake_pool::{error, id, instruction, state},
|
||||||
|
spl_token::error as token_error,
|
||||||
|
};
|
||||||
|
|
||||||
|
async fn setup() -> (ProgramTestContext, StakePoolAccounts, Keypair, Pubkey) {
|
||||||
|
let mut context = program_test().start_with_context().await;
|
||||||
|
|
||||||
|
let stake_pool_accounts = StakePoolAccounts::new();
|
||||||
|
stake_pool_accounts
|
||||||
|
.initialize_stake_pool(
|
||||||
|
&mut context.banks_client,
|
||||||
|
&context.payer,
|
||||||
|
&context.last_blockhash,
|
||||||
|
1,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let user = Keypair::new();
|
||||||
|
|
||||||
|
// make pool token account for user
|
||||||
|
let pool_token_account = Keypair::new();
|
||||||
|
create_token_account(
|
||||||
|
&mut context.banks_client,
|
||||||
|
&context.payer,
|
||||||
|
&context.last_blockhash,
|
||||||
|
&pool_token_account,
|
||||||
|
&stake_pool_accounts.pool_mint.pubkey(),
|
||||||
|
&user.pubkey(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
let mut transaction = Transaction::new_with_payer(
|
||||||
|
&[
|
||||||
|
instruction::set_sol_deposit_fee(
|
||||||
|
&id(),
|
||||||
|
&stake_pool_accounts.stake_pool.pubkey(),
|
||||||
|
&stake_pool_accounts.manager.pubkey(),
|
||||||
|
stake_pool_accounts.deposit_fee,
|
||||||
|
),
|
||||||
|
instruction::set_sol_referral_fee(
|
||||||
|
&id(),
|
||||||
|
&stake_pool_accounts.stake_pool.pubkey(),
|
||||||
|
&stake_pool_accounts.manager.pubkey(),
|
||||||
|
stake_pool_accounts.referral_fee,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
Some(&context.payer.pubkey()),
|
||||||
|
);
|
||||||
|
|
||||||
|
transaction.sign(
|
||||||
|
&[&context.payer, &stake_pool_accounts.manager],
|
||||||
|
context.last_blockhash,
|
||||||
|
);
|
||||||
|
context
|
||||||
|
.banks_client
|
||||||
|
.process_transaction(transaction)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
(
|
||||||
|
context,
|
||||||
|
stake_pool_accounts,
|
||||||
|
user,
|
||||||
|
pool_token_account.pubkey(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn success() {
|
||||||
|
let (mut context, stake_pool_accounts, _user, pool_token_account) = setup().await;
|
||||||
|
|
||||||
|
// Save stake pool state before depositing
|
||||||
|
let pre_stake_pool = get_account(
|
||||||
|
&mut context.banks_client,
|
||||||
|
&stake_pool_accounts.stake_pool.pubkey(),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
let pre_stake_pool =
|
||||||
|
try_from_slice_unchecked::<state::StakePool>(&pre_stake_pool.data.as_slice()).unwrap();
|
||||||
|
|
||||||
|
// Save reserve state before depositing
|
||||||
|
let pre_reserve_lamports = get_account(
|
||||||
|
&mut context.banks_client,
|
||||||
|
&stake_pool_accounts.reserve_stake.pubkey(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.lamports;
|
||||||
|
|
||||||
|
let error = stake_pool_accounts
|
||||||
|
.deposit_sol(
|
||||||
|
&mut context.banks_client,
|
||||||
|
&context.payer,
|
||||||
|
&context.last_blockhash,
|
||||||
|
&pool_token_account,
|
||||||
|
TEST_STAKE_AMOUNT,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
assert!(error.is_none());
|
||||||
|
|
||||||
|
let tokens_issued = TEST_STAKE_AMOUNT; // For now tokens are 1:1 to stake
|
||||||
|
|
||||||
|
// Stake pool should add its balance to the pool balance
|
||||||
|
let post_stake_pool = get_account(
|
||||||
|
&mut context.banks_client,
|
||||||
|
&stake_pool_accounts.stake_pool.pubkey(),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
let post_stake_pool =
|
||||||
|
try_from_slice_unchecked::<state::StakePool>(&post_stake_pool.data.as_slice()).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
post_stake_pool.total_stake_lamports,
|
||||||
|
pre_stake_pool.total_stake_lamports + TEST_STAKE_AMOUNT
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
post_stake_pool.pool_token_supply,
|
||||||
|
pre_stake_pool.pool_token_supply + tokens_issued
|
||||||
|
);
|
||||||
|
|
||||||
|
// Check minted tokens
|
||||||
|
let user_token_balance =
|
||||||
|
get_token_balance(&mut context.banks_client, &pool_token_account).await;
|
||||||
|
let tokens_issued_user =
|
||||||
|
tokens_issued - stake_pool_accounts.calculate_deposit_fee(tokens_issued);
|
||||||
|
assert_eq!(user_token_balance, tokens_issued_user);
|
||||||
|
|
||||||
|
// Check reserve
|
||||||
|
let post_reserve_lamports = get_account(
|
||||||
|
&mut context.banks_client,
|
||||||
|
&stake_pool_accounts.reserve_stake.pubkey(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.lamports;
|
||||||
|
assert_eq!(
|
||||||
|
post_reserve_lamports,
|
||||||
|
pre_reserve_lamports + TEST_STAKE_AMOUNT
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn fail_with_wrong_token_program_id() {
|
||||||
|
let (mut context, stake_pool_accounts, _user, pool_token_account) = setup().await;
|
||||||
|
|
||||||
|
let wrong_token_program = Keypair::new();
|
||||||
|
|
||||||
|
let mut transaction = Transaction::new_with_payer(
|
||||||
|
&instruction::deposit_sol(
|
||||||
|
&id(),
|
||||||
|
&stake_pool_accounts.stake_pool.pubkey(),
|
||||||
|
&stake_pool_accounts.withdraw_authority,
|
||||||
|
&stake_pool_accounts.reserve_stake.pubkey(),
|
||||||
|
&context.payer.pubkey(),
|
||||||
|
&pool_token_account,
|
||||||
|
&stake_pool_accounts.pool_fee_account.pubkey(),
|
||||||
|
&stake_pool_accounts.pool_fee_account.pubkey(),
|
||||||
|
&stake_pool_accounts.pool_mint.pubkey(),
|
||||||
|
&wrong_token_program.pubkey(),
|
||||||
|
TEST_STAKE_AMOUNT,
|
||||||
|
),
|
||||||
|
Some(&context.payer.pubkey()),
|
||||||
|
);
|
||||||
|
transaction.sign(&[&context.payer], context.last_blockhash);
|
||||||
|
let transaction_error = context
|
||||||
|
.banks_client
|
||||||
|
.process_transaction(transaction)
|
||||||
|
.await
|
||||||
|
.err()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
match transaction_error {
|
||||||
|
TransportError::TransactionError(TransactionError::InstructionError(_, error)) => {
|
||||||
|
assert_eq!(error, InstructionError::IncorrectProgramId);
|
||||||
|
}
|
||||||
|
_ => panic!("Wrong error occurs while try to make a deposit with wrong token program ID"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn fail_with_wrong_withdraw_authority() {
|
||||||
|
let (mut context, mut stake_pool_accounts, _user, pool_token_account) = setup().await;
|
||||||
|
|
||||||
|
stake_pool_accounts.withdraw_authority = Pubkey::new_unique();
|
||||||
|
|
||||||
|
let transaction_error = stake_pool_accounts
|
||||||
|
.deposit_sol(
|
||||||
|
&mut context.banks_client,
|
||||||
|
&context.payer,
|
||||||
|
&context.last_blockhash,
|
||||||
|
&pool_token_account,
|
||||||
|
TEST_STAKE_AMOUNT,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
match transaction_error {
|
||||||
|
TransactionError::InstructionError(_, InstructionError::Custom(error_index)) => {
|
||||||
|
let program_error = error::StakePoolError::InvalidProgramAddress as u32;
|
||||||
|
assert_eq!(error_index, program_error);
|
||||||
|
}
|
||||||
|
_ => panic!("Wrong error occurs while try to make a deposit with wrong withdraw authority"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn fail_with_wrong_mint_for_receiver_acc() {
|
||||||
|
let (mut context, stake_pool_accounts, _user, _pool_token_account) = setup().await;
|
||||||
|
|
||||||
|
let outside_mint = Keypair::new();
|
||||||
|
let outside_withdraw_auth = Keypair::new();
|
||||||
|
let outside_manager = Keypair::new();
|
||||||
|
let outside_pool_fee_acc = Keypair::new();
|
||||||
|
|
||||||
|
create_mint(
|
||||||
|
&mut context.banks_client,
|
||||||
|
&context.payer,
|
||||||
|
&context.last_blockhash,
|
||||||
|
&outside_mint,
|
||||||
|
&outside_withdraw_auth.pubkey(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
create_token_account(
|
||||||
|
&mut context.banks_client,
|
||||||
|
&context.payer,
|
||||||
|
&context.last_blockhash,
|
||||||
|
&outside_pool_fee_acc,
|
||||||
|
&outside_mint.pubkey(),
|
||||||
|
&outside_manager.pubkey(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let transaction_error = stake_pool_accounts
|
||||||
|
.deposit_sol(
|
||||||
|
&mut context.banks_client,
|
||||||
|
&context.payer,
|
||||||
|
&context.last_blockhash,
|
||||||
|
&outside_pool_fee_acc.pubkey(),
|
||||||
|
TEST_STAKE_AMOUNT,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
match transaction_error {
|
||||||
|
TransactionError::InstructionError(_, InstructionError::Custom(error_index)) => {
|
||||||
|
let program_error = token_error::TokenError::MintMismatch as u32;
|
||||||
|
assert_eq!(error_index, program_error);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
panic!("Wrong error occurs while try to deposit with wrong mint from receiver account")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn success_with_sol_deposit_authority() {
|
||||||
|
let (mut banks_client, payer, recent_blockhash) = program_test().start().await;
|
||||||
|
let stake_pool_accounts = StakePoolAccounts::new();
|
||||||
|
stake_pool_accounts
|
||||||
|
.initialize_stake_pool(&mut banks_client, &payer, &recent_blockhash, 1)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let user = Keypair::new();
|
||||||
|
|
||||||
|
// make pool token account
|
||||||
|
let user_pool_account = Keypair::new();
|
||||||
|
create_token_account(
|
||||||
|
&mut banks_client,
|
||||||
|
&payer,
|
||||||
|
&recent_blockhash,
|
||||||
|
&user_pool_account,
|
||||||
|
&stake_pool_accounts.pool_mint.pubkey(),
|
||||||
|
&user.pubkey(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let error = stake_pool_accounts
|
||||||
|
.deposit_sol(
|
||||||
|
&mut banks_client,
|
||||||
|
&payer,
|
||||||
|
&recent_blockhash,
|
||||||
|
&user_pool_account.pubkey(),
|
||||||
|
TEST_STAKE_AMOUNT,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
assert!(error.is_none());
|
||||||
|
|
||||||
|
let sol_deposit_authority = Keypair::new();
|
||||||
|
|
||||||
|
let mut transaction = Transaction::new_with_payer(
|
||||||
|
&[instruction::set_deposit_authority(
|
||||||
|
&id(),
|
||||||
|
&stake_pool_accounts.stake_pool.pubkey(),
|
||||||
|
&stake_pool_accounts.manager.pubkey(),
|
||||||
|
Some(&sol_deposit_authority.pubkey()),
|
||||||
|
false,
|
||||||
|
)],
|
||||||
|
Some(&payer.pubkey()),
|
||||||
|
);
|
||||||
|
transaction.sign(&[&payer, &stake_pool_accounts.manager], recent_blockhash);
|
||||||
|
banks_client.process_transaction(transaction).await.unwrap();
|
||||||
|
|
||||||
|
let error = stake_pool_accounts
|
||||||
|
.deposit_sol(
|
||||||
|
&mut banks_client,
|
||||||
|
&payer,
|
||||||
|
&recent_blockhash,
|
||||||
|
&user_pool_account.pubkey(),
|
||||||
|
TEST_STAKE_AMOUNT,
|
||||||
|
Some(&sol_deposit_authority),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
assert!(error.is_none());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn fail_without_sol_deposit_authority_signature() {
|
||||||
|
let (mut banks_client, payer, recent_blockhash) = program_test().start().await;
|
||||||
|
let sol_deposit_authority = Keypair::new();
|
||||||
|
let stake_pool_accounts = StakePoolAccounts::new();
|
||||||
|
stake_pool_accounts
|
||||||
|
.initialize_stake_pool(&mut banks_client, &payer, &recent_blockhash, 1)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let user = Keypair::new();
|
||||||
|
|
||||||
|
// make pool token account
|
||||||
|
let user_pool_account = Keypair::new();
|
||||||
|
create_token_account(
|
||||||
|
&mut banks_client,
|
||||||
|
&payer,
|
||||||
|
&recent_blockhash,
|
||||||
|
&user_pool_account,
|
||||||
|
&stake_pool_accounts.pool_mint.pubkey(),
|
||||||
|
&user.pubkey(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let mut transaction = Transaction::new_with_payer(
|
||||||
|
&[instruction::set_deposit_authority(
|
||||||
|
&id(),
|
||||||
|
&stake_pool_accounts.stake_pool.pubkey(),
|
||||||
|
&stake_pool_accounts.manager.pubkey(),
|
||||||
|
Some(&sol_deposit_authority.pubkey()),
|
||||||
|
false,
|
||||||
|
)],
|
||||||
|
Some(&payer.pubkey()),
|
||||||
|
);
|
||||||
|
transaction.sign(&[&payer, &stake_pool_accounts.manager], recent_blockhash);
|
||||||
|
banks_client.process_transaction(transaction).await.unwrap();
|
||||||
|
|
||||||
|
let wrong_depositor = Keypair::new();
|
||||||
|
|
||||||
|
let error = stake_pool_accounts
|
||||||
|
.deposit_sol(
|
||||||
|
&mut banks_client,
|
||||||
|
&payer,
|
||||||
|
&recent_blockhash,
|
||||||
|
&user_pool_account.pubkey(),
|
||||||
|
TEST_STAKE_AMOUNT,
|
||||||
|
Some(&wrong_depositor),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
match error {
|
||||||
|
TransactionError::InstructionError(_, InstructionError::Custom(error_index)) => {
|
||||||
|
assert_eq!(
|
||||||
|
error_index,
|
||||||
|
error::StakePoolError::InvalidSolDepositAuthority as u32
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_ => panic!("Wrong error occurs while trying to make a deposit without SOL deposit authority signature"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn success_with_referral_fee() {
|
||||||
|
let (mut context, stake_pool_accounts, _user, pool_token_account) = setup().await;
|
||||||
|
|
||||||
|
let referrer = Keypair::new();
|
||||||
|
let referrer_token_account = Keypair::new();
|
||||||
|
create_token_account(
|
||||||
|
&mut context.banks_client,
|
||||||
|
&context.payer,
|
||||||
|
&context.last_blockhash,
|
||||||
|
&referrer_token_account,
|
||||||
|
&stake_pool_accounts.pool_mint.pubkey(),
|
||||||
|
&referrer.pubkey(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let referrer_balance_pre =
|
||||||
|
get_token_balance(&mut context.banks_client, &referrer_token_account.pubkey()).await;
|
||||||
|
|
||||||
|
let mut transaction = Transaction::new_with_payer(
|
||||||
|
&instruction::deposit_sol(
|
||||||
|
&id(),
|
||||||
|
&stake_pool_accounts.stake_pool.pubkey(),
|
||||||
|
&stake_pool_accounts.withdraw_authority,
|
||||||
|
&stake_pool_accounts.reserve_stake.pubkey(),
|
||||||
|
&context.payer.pubkey(),
|
||||||
|
&pool_token_account,
|
||||||
|
&stake_pool_accounts.pool_fee_account.pubkey(),
|
||||||
|
&referrer_token_account.pubkey(),
|
||||||
|
&stake_pool_accounts.pool_mint.pubkey(),
|
||||||
|
&spl_token::id(),
|
||||||
|
TEST_STAKE_AMOUNT,
|
||||||
|
),
|
||||||
|
Some(&context.payer.pubkey()),
|
||||||
|
);
|
||||||
|
transaction.sign(&[&context.payer], context.last_blockhash);
|
||||||
|
context
|
||||||
|
.banks_client
|
||||||
|
.process_transaction(transaction)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let referrer_balance_post =
|
||||||
|
get_token_balance(&mut context.banks_client, &referrer_token_account.pubkey()).await;
|
||||||
|
let referral_fee = stake_pool_accounts
|
||||||
|
.calculate_referral_fee(stake_pool_accounts.calculate_deposit_fee(TEST_STAKE_AMOUNT));
|
||||||
|
assert!(referral_fee > 0);
|
||||||
|
assert_eq!(referrer_balance_pre + referral_fee, referrer_balance_post);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn fail_with_invalid_referrer() {
|
||||||
|
let (mut context, stake_pool_accounts, _user, pool_token_account) = setup().await;
|
||||||
|
|
||||||
|
let invalid_token_account = Keypair::new();
|
||||||
|
|
||||||
|
let mut transaction = Transaction::new_with_payer(
|
||||||
|
&instruction::deposit_sol(
|
||||||
|
&id(),
|
||||||
|
&stake_pool_accounts.stake_pool.pubkey(),
|
||||||
|
&stake_pool_accounts.withdraw_authority,
|
||||||
|
&stake_pool_accounts.reserve_stake.pubkey(),
|
||||||
|
&context.payer.pubkey(),
|
||||||
|
&pool_token_account,
|
||||||
|
&stake_pool_accounts.pool_fee_account.pubkey(),
|
||||||
|
&invalid_token_account.pubkey(),
|
||||||
|
&stake_pool_accounts.pool_mint.pubkey(),
|
||||||
|
&spl_token::id(),
|
||||||
|
TEST_STAKE_AMOUNT,
|
||||||
|
),
|
||||||
|
Some(&context.payer.pubkey()),
|
||||||
|
);
|
||||||
|
transaction.sign(&[&context.payer], context.last_blockhash);
|
||||||
|
let transaction_error = context
|
||||||
|
.banks_client
|
||||||
|
.process_transaction(transaction)
|
||||||
|
.await
|
||||||
|
.err()
|
||||||
|
.unwrap()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
match transaction_error {
|
||||||
|
TransactionError::InstructionError(_, InstructionError::InvalidAccountData) => (),
|
||||||
|
_ => panic!(
|
||||||
|
"Wrong error occurs while try to make a deposit with an invalid referrer account"
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
|
@ -266,9 +266,11 @@ pub async fn create_stake_pool(
|
||||||
pool_token_account: &Pubkey,
|
pool_token_account: &Pubkey,
|
||||||
manager: &Keypair,
|
manager: &Keypair,
|
||||||
staker: &Pubkey,
|
staker: &Pubkey,
|
||||||
deposit_authority: &Option<Keypair>,
|
stake_deposit_authority: &Option<Keypair>,
|
||||||
fee: &state::Fee,
|
fee: &state::Fee,
|
||||||
withdrawal_fee: &state::Fee,
|
withdrawal_fee: &state::Fee,
|
||||||
|
deposit_fee: &state::Fee,
|
||||||
|
referral_fee: u8,
|
||||||
max_validators: u32,
|
max_validators: u32,
|
||||||
) -> Result<(), TransportError> {
|
) -> Result<(), TransportError> {
|
||||||
let rent = banks_client.get_rent().await.unwrap();
|
let rent = banks_client.get_rent().await.unwrap();
|
||||||
|
@ -303,17 +305,19 @@ pub async fn create_stake_pool(
|
||||||
pool_mint,
|
pool_mint,
|
||||||
pool_token_account,
|
pool_token_account,
|
||||||
&spl_token::id(),
|
&spl_token::id(),
|
||||||
deposit_authority.as_ref().map(|k| k.pubkey()),
|
stake_deposit_authority.as_ref().map(|k| k.pubkey()),
|
||||||
*fee,
|
*fee,
|
||||||
*withdrawal_fee,
|
*withdrawal_fee,
|
||||||
|
*deposit_fee,
|
||||||
|
referral_fee,
|
||||||
max_validators,
|
max_validators,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
Some(&payer.pubkey()),
|
Some(&payer.pubkey()),
|
||||||
);
|
);
|
||||||
let mut signers = vec![payer, stake_pool, validator_list, manager];
|
let mut signers = vec![payer, stake_pool, validator_list, manager];
|
||||||
if let Some(deposit_authority) = deposit_authority.as_ref() {
|
if let Some(stake_deposit_authority) = stake_deposit_authority.as_ref() {
|
||||||
signers.push(deposit_authority);
|
signers.push(stake_deposit_authority);
|
||||||
}
|
}
|
||||||
transaction.sign(&signers, *recent_blockhash);
|
transaction.sign(&signers, *recent_blockhash);
|
||||||
banks_client.process_transaction(transaction).await?;
|
banks_client.process_transaction(transaction).await?;
|
||||||
|
@ -542,10 +546,12 @@ pub struct StakePoolAccounts {
|
||||||
pub manager: Keypair,
|
pub manager: Keypair,
|
||||||
pub staker: Keypair,
|
pub staker: Keypair,
|
||||||
pub withdraw_authority: Pubkey,
|
pub withdraw_authority: Pubkey,
|
||||||
pub deposit_authority: Pubkey,
|
pub stake_deposit_authority: Pubkey,
|
||||||
pub deposit_authority_keypair: Option<Keypair>,
|
pub stake_deposit_authority_keypair: Option<Keypair>,
|
||||||
pub fee: state::Fee,
|
pub fee: state::Fee,
|
||||||
pub withdrawal_fee: state::Fee,
|
pub withdrawal_fee: state::Fee,
|
||||||
|
pub deposit_fee: state::Fee,
|
||||||
|
pub referral_fee: u8,
|
||||||
pub max_validators: u32,
|
pub max_validators: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -558,7 +564,7 @@ impl StakePoolAccounts {
|
||||||
&[&stake_pool_address.to_bytes()[..32], b"withdraw"],
|
&[&stake_pool_address.to_bytes()[..32], b"withdraw"],
|
||||||
&id(),
|
&id(),
|
||||||
);
|
);
|
||||||
let (deposit_authority, _) = Pubkey::find_program_address(
|
let (stake_deposit_authority, _) = Pubkey::find_program_address(
|
||||||
&[&stake_pool_address.to_bytes()[..32], b"deposit"],
|
&[&stake_pool_address.to_bytes()[..32], b"deposit"],
|
||||||
&id(),
|
&id(),
|
||||||
);
|
);
|
||||||
|
@ -577,8 +583,8 @@ impl StakePoolAccounts {
|
||||||
manager,
|
manager,
|
||||||
staker,
|
staker,
|
||||||
withdraw_authority,
|
withdraw_authority,
|
||||||
deposit_authority,
|
stake_deposit_authority,
|
||||||
deposit_authority_keypair: None,
|
stake_deposit_authority_keypair: None,
|
||||||
fee: state::Fee {
|
fee: state::Fee {
|
||||||
numerator: 1,
|
numerator: 1,
|
||||||
denominator: 100,
|
denominator: 100,
|
||||||
|
@ -587,14 +593,19 @@ impl StakePoolAccounts {
|
||||||
numerator: 3,
|
numerator: 3,
|
||||||
denominator: 1000,
|
denominator: 1000,
|
||||||
},
|
},
|
||||||
|
deposit_fee: state::Fee {
|
||||||
|
numerator: 1,
|
||||||
|
denominator: 1000,
|
||||||
|
},
|
||||||
|
referral_fee: 25,
|
||||||
max_validators: MAX_TEST_VALIDATORS,
|
max_validators: MAX_TEST_VALIDATORS,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_with_deposit_authority(deposit_authority: Keypair) -> Self {
|
pub fn new_with_stake_deposit_authority(stake_deposit_authority: Keypair) -> Self {
|
||||||
let mut stake_pool_accounts = Self::new();
|
let mut stake_pool_accounts = Self::new();
|
||||||
stake_pool_accounts.deposit_authority = deposit_authority.pubkey();
|
stake_pool_accounts.stake_deposit_authority = stake_deposit_authority.pubkey();
|
||||||
stake_pool_accounts.deposit_authority_keypair = Some(deposit_authority);
|
stake_pool_accounts.stake_deposit_authority_keypair = Some(stake_deposit_authority);
|
||||||
stake_pool_accounts
|
stake_pool_accounts
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -606,6 +617,14 @@ impl StakePoolAccounts {
|
||||||
pool_tokens * self.withdrawal_fee.numerator / self.withdrawal_fee.denominator
|
pool_tokens * self.withdrawal_fee.numerator / self.withdrawal_fee.denominator
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn calculate_deposit_fee(&self, pool_tokens: u64) -> u64 {
|
||||||
|
pool_tokens * self.deposit_fee.numerator / self.deposit_fee.denominator
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn calculate_referral_fee(&self, deposit_fee_collected: u64) -> u64 {
|
||||||
|
deposit_fee_collected * self.referral_fee as u64 / 100
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn initialize_stake_pool(
|
pub async fn initialize_stake_pool(
|
||||||
&self,
|
&self,
|
||||||
mut banks_client: &mut BanksClient,
|
mut banks_client: &mut BanksClient,
|
||||||
|
@ -654,9 +673,11 @@ impl StakePoolAccounts {
|
||||||
&self.pool_fee_account.pubkey(),
|
&self.pool_fee_account.pubkey(),
|
||||||
&self.manager,
|
&self.manager,
|
||||||
&self.staker.pubkey(),
|
&self.staker.pubkey(),
|
||||||
&self.deposit_authority_keypair,
|
&self.stake_deposit_authority_keypair,
|
||||||
&self.fee,
|
&self.fee,
|
||||||
&self.withdrawal_fee,
|
&self.withdrawal_fee,
|
||||||
|
&self.deposit_fee,
|
||||||
|
self.referral_fee,
|
||||||
self.max_validators,
|
self.max_validators,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
@ -675,25 +696,27 @@ impl StakePoolAccounts {
|
||||||
current_staker: &Keypair,
|
current_staker: &Keypair,
|
||||||
) -> Option<TransportError> {
|
) -> Option<TransportError> {
|
||||||
let mut signers = vec![payer, current_staker];
|
let mut signers = vec![payer, current_staker];
|
||||||
let instructions = if let Some(deposit_authority) = self.deposit_authority_keypair.as_ref()
|
let instructions =
|
||||||
{
|
if let Some(stake_deposit_authority) = self.stake_deposit_authority_keypair.as_ref() {
|
||||||
signers.push(deposit_authority);
|
signers.push(stake_deposit_authority);
|
||||||
instruction::deposit_with_authority(
|
instruction::deposit_stake_with_authority(
|
||||||
&id(),
|
&id(),
|
||||||
&self.stake_pool.pubkey(),
|
&self.stake_pool.pubkey(),
|
||||||
&self.validator_list.pubkey(),
|
&self.validator_list.pubkey(),
|
||||||
&self.deposit_authority,
|
&self.stake_deposit_authority,
|
||||||
&self.withdraw_authority,
|
&self.withdraw_authority,
|
||||||
stake,
|
stake,
|
||||||
¤t_staker.pubkey(),
|
¤t_staker.pubkey(),
|
||||||
validator_stake_account,
|
validator_stake_account,
|
||||||
&self.reserve_stake.pubkey(),
|
&self.reserve_stake.pubkey(),
|
||||||
pool_account,
|
pool_account,
|
||||||
|
&self.pool_fee_account.pubkey(),
|
||||||
|
&self.pool_fee_account.pubkey(),
|
||||||
&self.pool_mint.pubkey(),
|
&self.pool_mint.pubkey(),
|
||||||
&spl_token::id(),
|
&spl_token::id(),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
instruction::deposit(
|
instruction::deposit_stake(
|
||||||
&id(),
|
&id(),
|
||||||
&self.stake_pool.pubkey(),
|
&self.stake_pool.pubkey(),
|
||||||
&self.validator_list.pubkey(),
|
&self.validator_list.pubkey(),
|
||||||
|
@ -703,6 +726,8 @@ impl StakePoolAccounts {
|
||||||
validator_stake_account,
|
validator_stake_account,
|
||||||
&self.reserve_stake.pubkey(),
|
&self.reserve_stake.pubkey(),
|
||||||
pool_account,
|
pool_account,
|
||||||
|
&self.pool_fee_account.pubkey(),
|
||||||
|
&self.pool_fee_account.pubkey(),
|
||||||
&self.pool_mint.pubkey(),
|
&self.pool_mint.pubkey(),
|
||||||
&spl_token::id(),
|
&spl_token::id(),
|
||||||
)
|
)
|
||||||
|
@ -716,6 +741,57 @@ impl StakePoolAccounts {
|
||||||
banks_client.process_transaction(transaction).await.err()
|
banks_client.process_transaction(transaction).await.err()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
pub async fn deposit_sol(
|
||||||
|
&self,
|
||||||
|
banks_client: &mut BanksClient,
|
||||||
|
payer: &Keypair,
|
||||||
|
recent_blockhash: &Hash,
|
||||||
|
pool_account: &Pubkey,
|
||||||
|
amount: u64,
|
||||||
|
sol_deposit_authority: Option<&Keypair>,
|
||||||
|
) -> Option<TransportError> {
|
||||||
|
let mut signers = vec![payer];
|
||||||
|
let instructions = if let Some(sol_deposit_authority) = sol_deposit_authority {
|
||||||
|
signers.push(sol_deposit_authority);
|
||||||
|
instruction::deposit_sol_with_authority(
|
||||||
|
&id(),
|
||||||
|
&self.stake_pool.pubkey(),
|
||||||
|
&sol_deposit_authority.pubkey(),
|
||||||
|
&self.withdraw_authority,
|
||||||
|
&self.reserve_stake.pubkey(),
|
||||||
|
&payer.pubkey(),
|
||||||
|
pool_account,
|
||||||
|
&self.pool_fee_account.pubkey(),
|
||||||
|
&self.pool_fee_account.pubkey(),
|
||||||
|
&self.pool_mint.pubkey(),
|
||||||
|
&spl_token::id(),
|
||||||
|
amount,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
instruction::deposit_sol(
|
||||||
|
&id(),
|
||||||
|
&self.stake_pool.pubkey(),
|
||||||
|
&self.withdraw_authority,
|
||||||
|
&self.reserve_stake.pubkey(),
|
||||||
|
&payer.pubkey(),
|
||||||
|
pool_account,
|
||||||
|
&self.pool_fee_account.pubkey(),
|
||||||
|
&self.pool_fee_account.pubkey(),
|
||||||
|
&self.pool_mint.pubkey(),
|
||||||
|
&spl_token::id(),
|
||||||
|
amount,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
let transaction = Transaction::new_signed_with_payer(
|
||||||
|
&instructions,
|
||||||
|
Some(&payer.pubkey()),
|
||||||
|
&signers,
|
||||||
|
*recent_blockhash,
|
||||||
|
);
|
||||||
|
banks_client.process_transaction(transaction).await.err()
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub async fn withdraw_stake(
|
pub async fn withdraw_stake(
|
||||||
&self,
|
&self,
|
||||||
|
@ -730,7 +806,7 @@ impl StakePoolAccounts {
|
||||||
amount: u64,
|
amount: u64,
|
||||||
) -> Option<TransportError> {
|
) -> Option<TransportError> {
|
||||||
let transaction = Transaction::new_signed_with_payer(
|
let transaction = Transaction::new_signed_with_payer(
|
||||||
&[instruction::withdraw(
|
&[instruction::withdraw_stake(
|
||||||
&id(),
|
&id(),
|
||||||
&self.stake_pool.pubkey(),
|
&self.stake_pool.pubkey(),
|
||||||
&self.validator_list.pubkey(),
|
&self.validator_list.pubkey(),
|
||||||
|
@ -1084,7 +1160,7 @@ impl DepositStakeAccount {
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn deposit(
|
pub async fn deposit_stake(
|
||||||
&mut self,
|
&mut self,
|
||||||
banks_client: &mut BanksClient,
|
banks_client: &mut BanksClient,
|
||||||
payer: &Keypair,
|
payer: &Keypair,
|
||||||
|
@ -1119,7 +1195,7 @@ impl DepositStakeAccount {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn simple_deposit(
|
pub async fn simple_deposit_stake(
|
||||||
banks_client: &mut BanksClient,
|
banks_client: &mut BanksClient,
|
||||||
payer: &Keypair,
|
payer: &Keypair,
|
||||||
recent_blockhash: &Hash,
|
recent_blockhash: &Hash,
|
||||||
|
|
|
@ -32,7 +32,7 @@ use {
|
||||||
spl_token::state::{Account as SplAccount, AccountState as SplAccountState, Mint},
|
spl_token::state::{Account as SplAccount, AccountState as SplAccountState, Mint},
|
||||||
};
|
};
|
||||||
|
|
||||||
const HUGE_POOL_SIZE: u32 = 4_000;
|
const HUGE_POOL_SIZE: u32 = 3_950;
|
||||||
const ACCOUNT_RENT_EXEMPTION: u64 = 1_000_000_000; // go with something big to be safe
|
const ACCOUNT_RENT_EXEMPTION: u64 = 1_000_000_000; // go with something big to be safe
|
||||||
const STAKE_AMOUNT: u64 = 200_000_000_000;
|
const STAKE_AMOUNT: u64 = 200_000_000_000;
|
||||||
const STAKE_ACCOUNT_RENT_EXEMPTION: u64 = 2_282_880;
|
const STAKE_ACCOUNT_RENT_EXEMPTION: u64 = 2_282_880;
|
||||||
|
@ -56,15 +56,15 @@ async fn setup(
|
||||||
stake_pool_accounts.max_validators = max_validators;
|
stake_pool_accounts.max_validators = max_validators;
|
||||||
|
|
||||||
let stake_pool_pubkey = stake_pool_accounts.stake_pool.pubkey();
|
let stake_pool_pubkey = stake_pool_accounts.stake_pool.pubkey();
|
||||||
let (_, withdraw_bump_seed) =
|
let (_, stake_withdraw_bump_seed) =
|
||||||
find_withdraw_authority_program_address(&id(), &stake_pool_pubkey);
|
find_withdraw_authority_program_address(&id(), &stake_pool_pubkey);
|
||||||
|
|
||||||
let mut stake_pool = StakePool {
|
let mut stake_pool = StakePool {
|
||||||
account_type: AccountType::StakePool,
|
account_type: AccountType::StakePool,
|
||||||
manager: stake_pool_accounts.manager.pubkey(),
|
manager: stake_pool_accounts.manager.pubkey(),
|
||||||
staker: stake_pool_accounts.staker.pubkey(),
|
staker: stake_pool_accounts.staker.pubkey(),
|
||||||
deposit_authority: stake_pool_accounts.deposit_authority,
|
stake_deposit_authority: stake_pool_accounts.stake_deposit_authority,
|
||||||
withdraw_bump_seed,
|
stake_withdraw_bump_seed,
|
||||||
validator_list: stake_pool_accounts.validator_list.pubkey(),
|
validator_list: stake_pool_accounts.validator_list.pubkey(),
|
||||||
reserve_stake: stake_pool_accounts.reserve_stake.pubkey(),
|
reserve_stake: stake_pool_accounts.reserve_stake.pubkey(),
|
||||||
pool_mint: stake_pool_accounts.pool_mint.pubkey(),
|
pool_mint: stake_pool_accounts.pool_mint.pubkey(),
|
||||||
|
@ -78,10 +78,13 @@ async fn setup(
|
||||||
next_epoch_fee: None,
|
next_epoch_fee: None,
|
||||||
preferred_deposit_validator_vote_address: None,
|
preferred_deposit_validator_vote_address: None,
|
||||||
preferred_withdraw_validator_vote_address: None,
|
preferred_withdraw_validator_vote_address: None,
|
||||||
deposit_fee: Fee::default(),
|
stake_deposit_fee: Fee::default(),
|
||||||
|
sol_deposit_fee: Fee::default(),
|
||||||
withdrawal_fee: Fee::default(),
|
withdrawal_fee: Fee::default(),
|
||||||
next_withdrawal_fee: None,
|
next_withdrawal_fee: None,
|
||||||
referral_fee: 0,
|
stake_referral_fee: 0,
|
||||||
|
sol_referral_fee: 0,
|
||||||
|
sol_deposit_authority: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut validator_list = ValidatorList::new(max_validators);
|
let mut validator_list = ValidatorList::new(max_validators);
|
||||||
|
@ -592,7 +595,7 @@ async fn add_validator_to_pool() {
|
||||||
increase_amount,
|
increase_amount,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
assert!(error.is_none());
|
assert!(error.is_none(), "{:?}", error);
|
||||||
|
|
||||||
let validator_list = get_account(
|
let validator_list = get_account(
|
||||||
&mut context.banks_client,
|
&mut context.banks_client,
|
||||||
|
@ -655,7 +658,7 @@ async fn set_preferred() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn deposit() {
|
async fn deposit_stake() {
|
||||||
let (mut context, stake_pool_accounts, _, vote_pubkey, user, stake_pubkey, pool_account_pubkey) =
|
let (mut context, stake_pool_accounts, _, vote_pubkey, user, stake_pubkey, pool_account_pubkey) =
|
||||||
setup(HUGE_POOL_SIZE, HUGE_POOL_SIZE, STAKE_AMOUNT).await;
|
setup(HUGE_POOL_SIZE, HUGE_POOL_SIZE, STAKE_AMOUNT).await;
|
||||||
|
|
||||||
|
|
|
@ -46,7 +46,7 @@ async fn setup() -> (
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let _deposit_info = simple_deposit(
|
let _deposit_info = simple_deposit_stake(
|
||||||
&mut banks_client,
|
&mut banks_client,
|
||||||
&payer,
|
&payer,
|
||||||
&recent_blockhash,
|
&recent_blockhash,
|
||||||
|
|
|
@ -254,6 +254,8 @@ async fn fail_with_wrong_max_validators() {
|
||||||
None,
|
None,
|
||||||
stake_pool_accounts.fee,
|
stake_pool_accounts.fee,
|
||||||
stake_pool_accounts.withdrawal_fee,
|
stake_pool_accounts.withdrawal_fee,
|
||||||
|
stake_pool_accounts.deposit_fee,
|
||||||
|
stake_pool_accounts.referral_fee,
|
||||||
stake_pool_accounts.max_validators,
|
stake_pool_accounts.max_validators,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -325,6 +327,8 @@ async fn fail_with_wrong_mint_authority() {
|
||||||
&None,
|
&None,
|
||||||
&stake_pool_accounts.fee,
|
&stake_pool_accounts.fee,
|
||||||
&stake_pool_accounts.withdrawal_fee,
|
&stake_pool_accounts.withdrawal_fee,
|
||||||
|
&stake_pool_accounts.deposit_fee,
|
||||||
|
stake_pool_accounts.referral_fee,
|
||||||
stake_pool_accounts.max_validators,
|
stake_pool_accounts.max_validators,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
@ -411,6 +415,8 @@ async fn fail_with_freeze_authority() {
|
||||||
&None,
|
&None,
|
||||||
&stake_pool_accounts.fee,
|
&stake_pool_accounts.fee,
|
||||||
&stake_pool_accounts.withdrawal_fee,
|
&stake_pool_accounts.withdrawal_fee,
|
||||||
|
&stake_pool_accounts.deposit_fee,
|
||||||
|
stake_pool_accounts.referral_fee,
|
||||||
stake_pool_accounts.max_validators,
|
stake_pool_accounts.max_validators,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
@ -499,6 +505,8 @@ async fn fail_with_wrong_token_program_id() {
|
||||||
None,
|
None,
|
||||||
stake_pool_accounts.fee,
|
stake_pool_accounts.fee,
|
||||||
stake_pool_accounts.withdrawal_fee,
|
stake_pool_accounts.withdrawal_fee,
|
||||||
|
stake_pool_accounts.deposit_fee,
|
||||||
|
stake_pool_accounts.referral_fee,
|
||||||
stake_pool_accounts.max_validators,
|
stake_pool_accounts.max_validators,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -576,6 +584,8 @@ async fn fail_with_wrong_fee_account() {
|
||||||
&None,
|
&None,
|
||||||
&stake_pool_accounts.fee,
|
&stake_pool_accounts.fee,
|
||||||
&stake_pool_accounts.withdrawal_fee,
|
&stake_pool_accounts.withdrawal_fee,
|
||||||
|
&stake_pool_accounts.deposit_fee,
|
||||||
|
stake_pool_accounts.referral_fee,
|
||||||
stake_pool_accounts.max_validators,
|
stake_pool_accounts.max_validators,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
@ -665,6 +675,8 @@ async fn fail_with_not_rent_exempt_pool() {
|
||||||
None,
|
None,
|
||||||
stake_pool_accounts.fee,
|
stake_pool_accounts.fee,
|
||||||
stake_pool_accounts.withdrawal_fee,
|
stake_pool_accounts.withdrawal_fee,
|
||||||
|
stake_pool_accounts.deposit_fee,
|
||||||
|
stake_pool_accounts.referral_fee,
|
||||||
stake_pool_accounts.max_validators,
|
stake_pool_accounts.max_validators,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -740,6 +752,8 @@ async fn fail_with_not_rent_exempt_validator_list() {
|
||||||
None,
|
None,
|
||||||
stake_pool_accounts.fee,
|
stake_pool_accounts.fee,
|
||||||
stake_pool_accounts.withdrawal_fee,
|
stake_pool_accounts.withdrawal_fee,
|
||||||
|
stake_pool_accounts.deposit_fee,
|
||||||
|
stake_pool_accounts.referral_fee,
|
||||||
stake_pool_accounts.max_validators,
|
stake_pool_accounts.max_validators,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -792,6 +806,8 @@ async fn fail_without_manager_signature() {
|
||||||
let init_data = instruction::StakePoolInstruction::Initialize {
|
let init_data = instruction::StakePoolInstruction::Initialize {
|
||||||
fee: stake_pool_accounts.fee,
|
fee: stake_pool_accounts.fee,
|
||||||
withdrawal_fee: stake_pool_accounts.withdrawal_fee,
|
withdrawal_fee: stake_pool_accounts.withdrawal_fee,
|
||||||
|
deposit_fee: stake_pool_accounts.deposit_fee,
|
||||||
|
referral_fee: stake_pool_accounts.referral_fee,
|
||||||
max_validators: stake_pool_accounts.max_validators,
|
max_validators: stake_pool_accounts.max_validators,
|
||||||
};
|
};
|
||||||
let data = init_data.try_to_vec().unwrap();
|
let data = init_data.try_to_vec().unwrap();
|
||||||
|
@ -914,6 +930,8 @@ async fn fail_with_pre_minted_pool_tokens() {
|
||||||
&None,
|
&None,
|
||||||
&stake_pool_accounts.fee,
|
&stake_pool_accounts.fee,
|
||||||
&stake_pool_accounts.withdrawal_fee,
|
&stake_pool_accounts.withdrawal_fee,
|
||||||
|
&stake_pool_accounts.deposit_fee,
|
||||||
|
stake_pool_accounts.referral_fee,
|
||||||
stake_pool_accounts.max_validators,
|
stake_pool_accounts.max_validators,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
@ -976,6 +994,8 @@ async fn fail_with_bad_reserve() {
|
||||||
&None,
|
&None,
|
||||||
&stake_pool_accounts.fee,
|
&stake_pool_accounts.fee,
|
||||||
&stake_pool_accounts.withdrawal_fee,
|
&stake_pool_accounts.withdrawal_fee,
|
||||||
|
&stake_pool_accounts.deposit_fee,
|
||||||
|
stake_pool_accounts.referral_fee,
|
||||||
stake_pool_accounts.max_validators,
|
stake_pool_accounts.max_validators,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
@ -1022,6 +1042,8 @@ async fn fail_with_bad_reserve() {
|
||||||
&None,
|
&None,
|
||||||
&stake_pool_accounts.fee,
|
&stake_pool_accounts.fee,
|
||||||
&stake_pool_accounts.withdrawal_fee,
|
&stake_pool_accounts.withdrawal_fee,
|
||||||
|
&stake_pool_accounts.deposit_fee,
|
||||||
|
stake_pool_accounts.referral_fee,
|
||||||
stake_pool_accounts.max_validators,
|
stake_pool_accounts.max_validators,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
@ -1071,6 +1093,8 @@ async fn fail_with_bad_reserve() {
|
||||||
&None,
|
&None,
|
||||||
&stake_pool_accounts.fee,
|
&stake_pool_accounts.fee,
|
||||||
&stake_pool_accounts.withdrawal_fee,
|
&stake_pool_accounts.withdrawal_fee,
|
||||||
|
&stake_pool_accounts.deposit_fee,
|
||||||
|
stake_pool_accounts.referral_fee,
|
||||||
stake_pool_accounts.max_validators,
|
stake_pool_accounts.max_validators,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
@ -1120,6 +1144,8 @@ async fn fail_with_bad_reserve() {
|
||||||
&None,
|
&None,
|
||||||
&stake_pool_accounts.fee,
|
&stake_pool_accounts.fee,
|
||||||
&stake_pool_accounts.withdrawal_fee,
|
&stake_pool_accounts.withdrawal_fee,
|
||||||
|
&stake_pool_accounts.deposit_fee,
|
||||||
|
stake_pool_accounts.referral_fee,
|
||||||
stake_pool_accounts.max_validators,
|
stake_pool_accounts.max_validators,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
@ -1138,10 +1164,11 @@ async fn fail_with_bad_reserve() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn success_with_required_deposit_authority() {
|
async fn success_with_required_stake_deposit_authority() {
|
||||||
let (mut banks_client, payer, recent_blockhash) = program_test().start().await;
|
let (mut banks_client, payer, recent_blockhash) = program_test().start().await;
|
||||||
let deposit_authority = Keypair::new();
|
let stake_deposit_authority = Keypair::new();
|
||||||
let stake_pool_accounts = StakePoolAccounts::new_with_deposit_authority(deposit_authority);
|
let stake_pool_accounts =
|
||||||
|
StakePoolAccounts::new_with_stake_deposit_authority(stake_deposit_authority);
|
||||||
stake_pool_accounts
|
stake_pool_accounts
|
||||||
.initialize_stake_pool(&mut banks_client, &payer, &recent_blockhash, 1)
|
.initialize_stake_pool(&mut banks_client, &payer, &recent_blockhash, 1)
|
||||||
.await
|
.await
|
||||||
|
@ -1153,7 +1180,7 @@ async fn success_with_required_deposit_authority() {
|
||||||
let stake_pool =
|
let stake_pool =
|
||||||
try_from_slice_unchecked::<state::StakePool>(stake_pool_account.data.as_slice()).unwrap();
|
try_from_slice_unchecked::<state::StakePool>(stake_pool_account.data.as_slice()).unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
stake_pool.deposit_authority,
|
stake_pool.stake_deposit_authority,
|
||||||
stake_pool_accounts.deposit_authority
|
stake_pool_accounts.stake_deposit_authority
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,359 @@
|
||||||
|
#![cfg(feature = "test-bpf")]
|
||||||
|
|
||||||
|
mod helpers;
|
||||||
|
|
||||||
|
use {
|
||||||
|
borsh::BorshSerialize,
|
||||||
|
helpers::*,
|
||||||
|
solana_program::{
|
||||||
|
borsh::try_from_slice_unchecked,
|
||||||
|
hash::Hash,
|
||||||
|
instruction::{AccountMeta, Instruction},
|
||||||
|
},
|
||||||
|
solana_program_test::*,
|
||||||
|
solana_sdk::{
|
||||||
|
instruction::InstructionError, signature::Keypair, signature::Signer,
|
||||||
|
transaction::Transaction, transaction::TransactionError, transport::TransportError,
|
||||||
|
},
|
||||||
|
spl_stake_pool::{error, find_deposit_authority_program_address, id, instruction, state},
|
||||||
|
};
|
||||||
|
|
||||||
|
async fn setup() -> (BanksClient, Keypair, Hash, StakePoolAccounts, Keypair) {
|
||||||
|
let (mut banks_client, payer, recent_blockhash) = program_test().start().await;
|
||||||
|
let stake_pool_accounts = StakePoolAccounts::new();
|
||||||
|
stake_pool_accounts
|
||||||
|
.initialize_stake_pool(&mut banks_client, &payer, &recent_blockhash, 1)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let new_deposit_authority = Keypair::new();
|
||||||
|
|
||||||
|
(
|
||||||
|
banks_client,
|
||||||
|
payer,
|
||||||
|
recent_blockhash,
|
||||||
|
stake_pool_accounts,
|
||||||
|
new_deposit_authority,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn success_set_stake_deposit_authority() {
|
||||||
|
let (
|
||||||
|
mut banks_client,
|
||||||
|
payer,
|
||||||
|
recent_blockhash,
|
||||||
|
stake_pool_accounts,
|
||||||
|
new_stake_deposit_authority,
|
||||||
|
) = setup().await;
|
||||||
|
|
||||||
|
let mut transaction = Transaction::new_with_payer(
|
||||||
|
&[instruction::set_deposit_authority(
|
||||||
|
&id(),
|
||||||
|
&stake_pool_accounts.stake_pool.pubkey(),
|
||||||
|
&stake_pool_accounts.manager.pubkey(),
|
||||||
|
Some(&new_stake_deposit_authority.pubkey()),
|
||||||
|
true,
|
||||||
|
)],
|
||||||
|
Some(&payer.pubkey()),
|
||||||
|
);
|
||||||
|
transaction.sign(&[&payer, &stake_pool_accounts.manager], recent_blockhash);
|
||||||
|
banks_client.process_transaction(transaction).await.unwrap();
|
||||||
|
|
||||||
|
let stake_pool = get_account(&mut banks_client, &stake_pool_accounts.stake_pool.pubkey()).await;
|
||||||
|
let stake_pool =
|
||||||
|
try_from_slice_unchecked::<state::StakePool>(&stake_pool.data.as_slice()).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
stake_pool.stake_deposit_authority,
|
||||||
|
new_stake_deposit_authority.pubkey()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn success_set_stake_deposit_authority_to_none() {
|
||||||
|
let (
|
||||||
|
mut banks_client,
|
||||||
|
payer,
|
||||||
|
recent_blockhash,
|
||||||
|
stake_pool_accounts,
|
||||||
|
new_stake_deposit_authority,
|
||||||
|
) = setup().await;
|
||||||
|
|
||||||
|
let mut transaction = Transaction::new_with_payer(
|
||||||
|
&[instruction::set_deposit_authority(
|
||||||
|
&id(),
|
||||||
|
&stake_pool_accounts.stake_pool.pubkey(),
|
||||||
|
&stake_pool_accounts.manager.pubkey(),
|
||||||
|
Some(&new_stake_deposit_authority.pubkey()),
|
||||||
|
true,
|
||||||
|
)],
|
||||||
|
Some(&payer.pubkey()),
|
||||||
|
);
|
||||||
|
transaction.sign(&[&payer, &stake_pool_accounts.manager], recent_blockhash);
|
||||||
|
banks_client.process_transaction(transaction).await.unwrap();
|
||||||
|
|
||||||
|
let stake_pool = get_account(&mut banks_client, &stake_pool_accounts.stake_pool.pubkey()).await;
|
||||||
|
let stake_pool =
|
||||||
|
try_from_slice_unchecked::<state::StakePool>(&stake_pool.data.as_slice()).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
stake_pool.stake_deposit_authority,
|
||||||
|
new_stake_deposit_authority.pubkey()
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut transaction = Transaction::new_with_payer(
|
||||||
|
&[instruction::set_deposit_authority(
|
||||||
|
&id(),
|
||||||
|
&stake_pool_accounts.stake_pool.pubkey(),
|
||||||
|
&stake_pool_accounts.manager.pubkey(),
|
||||||
|
None,
|
||||||
|
true,
|
||||||
|
)],
|
||||||
|
Some(&payer.pubkey()),
|
||||||
|
);
|
||||||
|
transaction.sign(&[&payer, &stake_pool_accounts.manager], recent_blockhash);
|
||||||
|
banks_client.process_transaction(transaction).await.unwrap();
|
||||||
|
|
||||||
|
let stake_pool = get_account(&mut banks_client, &stake_pool_accounts.stake_pool.pubkey()).await;
|
||||||
|
let stake_pool =
|
||||||
|
try_from_slice_unchecked::<state::StakePool>(&stake_pool.data.as_slice()).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
stake_pool.stake_deposit_authority,
|
||||||
|
find_deposit_authority_program_address(&id(), &stake_pool_accounts.stake_pool.pubkey()).0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn fail_stake_wrong_manager() {
|
||||||
|
let (
|
||||||
|
mut banks_client,
|
||||||
|
payer,
|
||||||
|
recent_blockhash,
|
||||||
|
stake_pool_accounts,
|
||||||
|
new_stake_deposit_authority,
|
||||||
|
) = setup().await;
|
||||||
|
|
||||||
|
let mut transaction = Transaction::new_with_payer(
|
||||||
|
&[instruction::set_deposit_authority(
|
||||||
|
&id(),
|
||||||
|
&stake_pool_accounts.stake_pool.pubkey(),
|
||||||
|
&new_stake_deposit_authority.pubkey(),
|
||||||
|
Some(&new_stake_deposit_authority.pubkey()),
|
||||||
|
true,
|
||||||
|
)],
|
||||||
|
Some(&payer.pubkey()),
|
||||||
|
);
|
||||||
|
transaction.sign(&[&payer, &new_stake_deposit_authority], recent_blockhash);
|
||||||
|
let transaction_error = banks_client
|
||||||
|
.process_transaction(transaction)
|
||||||
|
.await
|
||||||
|
.err()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
match transaction_error {
|
||||||
|
TransportError::TransactionError(TransactionError::InstructionError(
|
||||||
|
_,
|
||||||
|
InstructionError::Custom(error_index),
|
||||||
|
)) => {
|
||||||
|
let program_error = error::StakePoolError::WrongManager as u32;
|
||||||
|
assert_eq!(error_index, program_error);
|
||||||
|
}
|
||||||
|
_ => panic!("Wrong error occurs while malicious try to set manager"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn fail_set_stake_deposit_authority_without_signature() {
|
||||||
|
let (
|
||||||
|
mut banks_client,
|
||||||
|
payer,
|
||||||
|
recent_blockhash,
|
||||||
|
stake_pool_accounts,
|
||||||
|
new_stake_deposit_authority,
|
||||||
|
) = setup().await;
|
||||||
|
|
||||||
|
let data =
|
||||||
|
instruction::StakePoolInstruction::SetDepositAuthority(instruction::DepositType::Stake)
|
||||||
|
.try_to_vec()
|
||||||
|
.unwrap();
|
||||||
|
let accounts = vec![
|
||||||
|
AccountMeta::new(stake_pool_accounts.stake_pool.pubkey(), false),
|
||||||
|
AccountMeta::new_readonly(stake_pool_accounts.manager.pubkey(), false),
|
||||||
|
AccountMeta::new_readonly(new_stake_deposit_authority.pubkey(), false),
|
||||||
|
];
|
||||||
|
let instruction = Instruction {
|
||||||
|
program_id: id(),
|
||||||
|
accounts,
|
||||||
|
data,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut transaction = Transaction::new_with_payer(&[instruction], Some(&payer.pubkey()));
|
||||||
|
transaction.sign(&[&payer], recent_blockhash);
|
||||||
|
let transaction_error = banks_client
|
||||||
|
.process_transaction(transaction)
|
||||||
|
.await
|
||||||
|
.err()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
match transaction_error {
|
||||||
|
TransportError::TransactionError(TransactionError::InstructionError(
|
||||||
|
_,
|
||||||
|
InstructionError::Custom(error_index),
|
||||||
|
)) => {
|
||||||
|
let program_error = error::StakePoolError::SignatureMissing as u32;
|
||||||
|
assert_eq!(error_index, program_error);
|
||||||
|
}
|
||||||
|
_ => panic!("Wrong error occurs while try to set new manager without signature"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn success_set_sol_deposit_authority() {
|
||||||
|
let (mut banks_client, payer, recent_blockhash, stake_pool_accounts, new_sol_deposit_authority) =
|
||||||
|
setup().await;
|
||||||
|
|
||||||
|
let mut transaction = Transaction::new_with_payer(
|
||||||
|
&[instruction::set_deposit_authority(
|
||||||
|
&id(),
|
||||||
|
&stake_pool_accounts.stake_pool.pubkey(),
|
||||||
|
&stake_pool_accounts.manager.pubkey(),
|
||||||
|
Some(&new_sol_deposit_authority.pubkey()),
|
||||||
|
false,
|
||||||
|
)],
|
||||||
|
Some(&payer.pubkey()),
|
||||||
|
);
|
||||||
|
transaction.sign(&[&payer, &stake_pool_accounts.manager], recent_blockhash);
|
||||||
|
banks_client.process_transaction(transaction).await.unwrap();
|
||||||
|
|
||||||
|
let stake_pool = get_account(&mut banks_client, &stake_pool_accounts.stake_pool.pubkey()).await;
|
||||||
|
let stake_pool =
|
||||||
|
try_from_slice_unchecked::<state::StakePool>(&stake_pool.data.as_slice()).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
stake_pool.sol_deposit_authority,
|
||||||
|
Some(new_sol_deposit_authority.pubkey())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn success_set_sol_deposit_authority_to_none() {
|
||||||
|
let (mut banks_client, payer, recent_blockhash, stake_pool_accounts, new_sol_deposit_authority) =
|
||||||
|
setup().await;
|
||||||
|
|
||||||
|
let mut transaction = Transaction::new_with_payer(
|
||||||
|
&[instruction::set_deposit_authority(
|
||||||
|
&id(),
|
||||||
|
&stake_pool_accounts.stake_pool.pubkey(),
|
||||||
|
&stake_pool_accounts.manager.pubkey(),
|
||||||
|
Some(&new_sol_deposit_authority.pubkey()),
|
||||||
|
false,
|
||||||
|
)],
|
||||||
|
Some(&payer.pubkey()),
|
||||||
|
);
|
||||||
|
transaction.sign(&[&payer, &stake_pool_accounts.manager], recent_blockhash);
|
||||||
|
banks_client.process_transaction(transaction).await.unwrap();
|
||||||
|
|
||||||
|
let stake_pool = get_account(&mut banks_client, &stake_pool_accounts.stake_pool.pubkey()).await;
|
||||||
|
let stake_pool =
|
||||||
|
try_from_slice_unchecked::<state::StakePool>(&stake_pool.data.as_slice()).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
stake_pool.sol_deposit_authority,
|
||||||
|
Some(new_sol_deposit_authority.pubkey())
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut transaction = Transaction::new_with_payer(
|
||||||
|
&[instruction::set_deposit_authority(
|
||||||
|
&id(),
|
||||||
|
&stake_pool_accounts.stake_pool.pubkey(),
|
||||||
|
&stake_pool_accounts.manager.pubkey(),
|
||||||
|
None,
|
||||||
|
false,
|
||||||
|
)],
|
||||||
|
Some(&payer.pubkey()),
|
||||||
|
);
|
||||||
|
transaction.sign(&[&payer, &stake_pool_accounts.manager], recent_blockhash);
|
||||||
|
banks_client.process_transaction(transaction).await.unwrap();
|
||||||
|
|
||||||
|
let stake_pool = get_account(&mut banks_client, &stake_pool_accounts.stake_pool.pubkey()).await;
|
||||||
|
let stake_pool =
|
||||||
|
try_from_slice_unchecked::<state::StakePool>(&stake_pool.data.as_slice()).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(stake_pool.sol_deposit_authority, None);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn fail_sol_wrong_manager() {
|
||||||
|
let (mut banks_client, payer, recent_blockhash, stake_pool_accounts, new_sol_deposit_authority) =
|
||||||
|
setup().await;
|
||||||
|
|
||||||
|
let mut transaction = Transaction::new_with_payer(
|
||||||
|
&[instruction::set_deposit_authority(
|
||||||
|
&id(),
|
||||||
|
&stake_pool_accounts.stake_pool.pubkey(),
|
||||||
|
&new_sol_deposit_authority.pubkey(),
|
||||||
|
Some(&new_sol_deposit_authority.pubkey()),
|
||||||
|
false,
|
||||||
|
)],
|
||||||
|
Some(&payer.pubkey()),
|
||||||
|
);
|
||||||
|
transaction.sign(&[&payer, &new_sol_deposit_authority], recent_blockhash);
|
||||||
|
let transaction_error = banks_client
|
||||||
|
.process_transaction(transaction)
|
||||||
|
.await
|
||||||
|
.err()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
match transaction_error {
|
||||||
|
TransportError::TransactionError(TransactionError::InstructionError(
|
||||||
|
_,
|
||||||
|
InstructionError::Custom(error_index),
|
||||||
|
)) => {
|
||||||
|
let program_error = error::StakePoolError::WrongManager as u32;
|
||||||
|
assert_eq!(error_index, program_error);
|
||||||
|
}
|
||||||
|
_ => panic!("Wrong error occurs while malicious try to set manager"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn fail_set_sol_deposit_authority_without_signature() {
|
||||||
|
let (mut banks_client, payer, recent_blockhash, stake_pool_accounts, new_sol_deposit_authority) =
|
||||||
|
setup().await;
|
||||||
|
|
||||||
|
let data =
|
||||||
|
instruction::StakePoolInstruction::SetDepositAuthority(instruction::DepositType::Sol)
|
||||||
|
.try_to_vec()
|
||||||
|
.unwrap();
|
||||||
|
let accounts = vec![
|
||||||
|
AccountMeta::new(stake_pool_accounts.stake_pool.pubkey(), false),
|
||||||
|
AccountMeta::new_readonly(stake_pool_accounts.manager.pubkey(), false),
|
||||||
|
AccountMeta::new_readonly(new_sol_deposit_authority.pubkey(), false),
|
||||||
|
];
|
||||||
|
let instruction = Instruction {
|
||||||
|
program_id: id(),
|
||||||
|
accounts,
|
||||||
|
data,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut transaction = Transaction::new_with_payer(&[instruction], Some(&payer.pubkey()));
|
||||||
|
transaction.sign(&[&payer], recent_blockhash);
|
||||||
|
let transaction_error = banks_client
|
||||||
|
.process_transaction(transaction)
|
||||||
|
.await
|
||||||
|
.err()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
match transaction_error {
|
||||||
|
TransportError::TransactionError(TransactionError::InstructionError(
|
||||||
|
_,
|
||||||
|
InstructionError::Custom(error_index),
|
||||||
|
)) => {
|
||||||
|
let program_error = error::StakePoolError::SignatureMissing as u32;
|
||||||
|
assert_eq!(error_index, program_error);
|
||||||
|
}
|
||||||
|
_ => panic!("Wrong error occurs while try to set new manager without signature"),
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,274 @@
|
||||||
|
#![cfg(feature = "test-bpf")]
|
||||||
|
|
||||||
|
mod helpers;
|
||||||
|
|
||||||
|
use {
|
||||||
|
helpers::*,
|
||||||
|
solana_program_test::*,
|
||||||
|
solana_sdk::{
|
||||||
|
borsh::try_from_slice_unchecked,
|
||||||
|
instruction::InstructionError,
|
||||||
|
signature::{Keypair, Signer},
|
||||||
|
transaction::{Transaction, TransactionError},
|
||||||
|
},
|
||||||
|
spl_stake_pool::{error, id, instruction, state::Fee, state::StakePool},
|
||||||
|
};
|
||||||
|
|
||||||
|
async fn setup(fee: Option<Fee>) -> (ProgramTestContext, StakePoolAccounts, Fee) {
|
||||||
|
let mut context = program_test().start_with_context().await;
|
||||||
|
let mut stake_pool_accounts = StakePoolAccounts::new();
|
||||||
|
if let Some(fee) = fee {
|
||||||
|
stake_pool_accounts.deposit_fee = fee;
|
||||||
|
}
|
||||||
|
stake_pool_accounts
|
||||||
|
.initialize_stake_pool(
|
||||||
|
&mut context.banks_client,
|
||||||
|
&context.payer,
|
||||||
|
&context.last_blockhash,
|
||||||
|
1,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
let new_deposit_fee = Fee {
|
||||||
|
numerator: 823,
|
||||||
|
denominator: 1000,
|
||||||
|
};
|
||||||
|
|
||||||
|
(context, stake_pool_accounts, new_deposit_fee)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn success_stake() {
|
||||||
|
let (mut context, stake_pool_accounts, new_deposit_fee) = setup(None).await;
|
||||||
|
|
||||||
|
let transaction = Transaction::new_signed_with_payer(
|
||||||
|
&[instruction::set_stake_deposit_fee(
|
||||||
|
&id(),
|
||||||
|
&stake_pool_accounts.stake_pool.pubkey(),
|
||||||
|
&stake_pool_accounts.manager.pubkey(),
|
||||||
|
new_deposit_fee,
|
||||||
|
)],
|
||||||
|
Some(&context.payer.pubkey()),
|
||||||
|
&[&context.payer, &stake_pool_accounts.manager],
|
||||||
|
context.last_blockhash,
|
||||||
|
);
|
||||||
|
context
|
||||||
|
.banks_client
|
||||||
|
.process_transaction(transaction)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let stake_pool = get_account(
|
||||||
|
&mut context.banks_client,
|
||||||
|
&stake_pool_accounts.stake_pool.pubkey(),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
let stake_pool = try_from_slice_unchecked::<StakePool>(&stake_pool.data.as_slice()).unwrap();
|
||||||
|
assert_eq!(stake_pool.stake_deposit_fee, new_deposit_fee);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn success_stake_increase_fee_from_0() {
|
||||||
|
let (mut context, stake_pool_accounts, _) = setup(Some(Fee {
|
||||||
|
numerator: 0,
|
||||||
|
denominator: 0,
|
||||||
|
}))
|
||||||
|
.await;
|
||||||
|
let new_deposit_fee = Fee {
|
||||||
|
numerator: 324,
|
||||||
|
denominator: 1234,
|
||||||
|
};
|
||||||
|
|
||||||
|
let transaction = Transaction::new_signed_with_payer(
|
||||||
|
&[instruction::set_stake_deposit_fee(
|
||||||
|
&id(),
|
||||||
|
&stake_pool_accounts.stake_pool.pubkey(),
|
||||||
|
&stake_pool_accounts.manager.pubkey(),
|
||||||
|
new_deposit_fee,
|
||||||
|
)],
|
||||||
|
Some(&context.payer.pubkey()),
|
||||||
|
&[&context.payer, &stake_pool_accounts.manager],
|
||||||
|
context.last_blockhash,
|
||||||
|
);
|
||||||
|
context
|
||||||
|
.banks_client
|
||||||
|
.process_transaction(transaction)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let stake_pool = get_account(
|
||||||
|
&mut context.banks_client,
|
||||||
|
&stake_pool_accounts.stake_pool.pubkey(),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
let stake_pool = try_from_slice_unchecked::<StakePool>(&stake_pool.data.as_slice()).unwrap();
|
||||||
|
assert_eq!(stake_pool.stake_deposit_fee, new_deposit_fee);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn fail_stake_wrong_manager() {
|
||||||
|
let (mut context, stake_pool_accounts, new_deposit_fee) = setup(None).await;
|
||||||
|
|
||||||
|
let wrong_manager = Keypair::new();
|
||||||
|
let transaction = Transaction::new_signed_with_payer(
|
||||||
|
&[instruction::set_stake_deposit_fee(
|
||||||
|
&id(),
|
||||||
|
&stake_pool_accounts.stake_pool.pubkey(),
|
||||||
|
&wrong_manager.pubkey(),
|
||||||
|
new_deposit_fee,
|
||||||
|
)],
|
||||||
|
Some(&context.payer.pubkey()),
|
||||||
|
&[&context.payer, &wrong_manager],
|
||||||
|
context.last_blockhash,
|
||||||
|
);
|
||||||
|
let error = context
|
||||||
|
.banks_client
|
||||||
|
.process_transaction(transaction)
|
||||||
|
.await
|
||||||
|
.err()
|
||||||
|
.unwrap()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
match error {
|
||||||
|
TransactionError::InstructionError(_, InstructionError::Custom(error_index)) => {
|
||||||
|
let program_error = error::StakePoolError::WrongManager as u32;
|
||||||
|
assert_eq!(error_index, program_error);
|
||||||
|
}
|
||||||
|
_ => panic!("Wrong error occurs while signing with the wrong manager"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn fail_stake_high_deposit_fee() {
|
||||||
|
let (mut context, stake_pool_accounts, _new_deposit_fee) = setup(None).await;
|
||||||
|
|
||||||
|
let new_deposit_fee = Fee {
|
||||||
|
numerator: 100001,
|
||||||
|
denominator: 100000,
|
||||||
|
};
|
||||||
|
let transaction = Transaction::new_signed_with_payer(
|
||||||
|
&[instruction::set_stake_deposit_fee(
|
||||||
|
&id(),
|
||||||
|
&stake_pool_accounts.stake_pool.pubkey(),
|
||||||
|
&stake_pool_accounts.manager.pubkey(),
|
||||||
|
new_deposit_fee,
|
||||||
|
)],
|
||||||
|
Some(&context.payer.pubkey()),
|
||||||
|
&[&context.payer, &stake_pool_accounts.manager],
|
||||||
|
context.last_blockhash,
|
||||||
|
);
|
||||||
|
let error = context
|
||||||
|
.banks_client
|
||||||
|
.process_transaction(transaction)
|
||||||
|
.await
|
||||||
|
.err()
|
||||||
|
.unwrap()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
match error {
|
||||||
|
TransactionError::InstructionError(_, InstructionError::Custom(error_index)) => {
|
||||||
|
let program_error = error::StakePoolError::FeeTooHigh as u32;
|
||||||
|
assert_eq!(error_index, program_error);
|
||||||
|
}
|
||||||
|
_ => panic!("Wrong error occurs when setting fee too high"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn success_sol() {
|
||||||
|
let (mut context, stake_pool_accounts, new_deposit_fee) = setup(None).await;
|
||||||
|
|
||||||
|
let transaction = Transaction::new_signed_with_payer(
|
||||||
|
&[instruction::set_sol_deposit_fee(
|
||||||
|
&id(),
|
||||||
|
&stake_pool_accounts.stake_pool.pubkey(),
|
||||||
|
&stake_pool_accounts.manager.pubkey(),
|
||||||
|
new_deposit_fee,
|
||||||
|
)],
|
||||||
|
Some(&context.payer.pubkey()),
|
||||||
|
&[&context.payer, &stake_pool_accounts.manager],
|
||||||
|
context.last_blockhash,
|
||||||
|
);
|
||||||
|
context
|
||||||
|
.banks_client
|
||||||
|
.process_transaction(transaction)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let stake_pool = get_account(
|
||||||
|
&mut context.banks_client,
|
||||||
|
&stake_pool_accounts.stake_pool.pubkey(),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
let stake_pool = try_from_slice_unchecked::<StakePool>(&stake_pool.data.as_slice()).unwrap();
|
||||||
|
assert_eq!(stake_pool.sol_deposit_fee, new_deposit_fee);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn fail_sol_wrong_manager() {
|
||||||
|
let (mut context, stake_pool_accounts, new_deposit_fee) = setup(None).await;
|
||||||
|
|
||||||
|
let wrong_manager = Keypair::new();
|
||||||
|
let transaction = Transaction::new_signed_with_payer(
|
||||||
|
&[instruction::set_sol_deposit_fee(
|
||||||
|
&id(),
|
||||||
|
&stake_pool_accounts.stake_pool.pubkey(),
|
||||||
|
&wrong_manager.pubkey(),
|
||||||
|
new_deposit_fee,
|
||||||
|
)],
|
||||||
|
Some(&context.payer.pubkey()),
|
||||||
|
&[&context.payer, &wrong_manager],
|
||||||
|
context.last_blockhash,
|
||||||
|
);
|
||||||
|
let error = context
|
||||||
|
.banks_client
|
||||||
|
.process_transaction(transaction)
|
||||||
|
.await
|
||||||
|
.err()
|
||||||
|
.unwrap()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
match error {
|
||||||
|
TransactionError::InstructionError(_, InstructionError::Custom(error_index)) => {
|
||||||
|
let program_error = error::StakePoolError::WrongManager as u32;
|
||||||
|
assert_eq!(error_index, program_error);
|
||||||
|
}
|
||||||
|
_ => panic!("Wrong error occurs while signing with the wrong manager"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn fail_sol_high_deposit_fee() {
|
||||||
|
let (mut context, stake_pool_accounts, _new_deposit_fee) = setup(None).await;
|
||||||
|
|
||||||
|
let new_deposit_fee = Fee {
|
||||||
|
numerator: 100001,
|
||||||
|
denominator: 100000,
|
||||||
|
};
|
||||||
|
let transaction = Transaction::new_signed_with_payer(
|
||||||
|
&[instruction::set_sol_deposit_fee(
|
||||||
|
&id(),
|
||||||
|
&stake_pool_accounts.stake_pool.pubkey(),
|
||||||
|
&stake_pool_accounts.manager.pubkey(),
|
||||||
|
new_deposit_fee,
|
||||||
|
)],
|
||||||
|
Some(&context.payer.pubkey()),
|
||||||
|
&[&context.payer, &stake_pool_accounts.manager],
|
||||||
|
context.last_blockhash,
|
||||||
|
);
|
||||||
|
let error = context
|
||||||
|
.banks_client
|
||||||
|
.process_transaction(transaction)
|
||||||
|
.await
|
||||||
|
.err()
|
||||||
|
.unwrap()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
match error {
|
||||||
|
TransactionError::InstructionError(_, InstructionError::Custom(error_index)) => {
|
||||||
|
let program_error = error::StakePoolError::FeeTooHigh as u32;
|
||||||
|
assert_eq!(error_index, program_error);
|
||||||
|
}
|
||||||
|
_ => panic!("Wrong error occurs when setting fee too high"),
|
||||||
|
}
|
||||||
|
}
|
|
@ -50,7 +50,7 @@ async fn success() {
|
||||||
let old_fee = stake_pool.fee;
|
let old_fee = stake_pool.fee;
|
||||||
|
|
||||||
let transaction = Transaction::new_signed_with_payer(
|
let transaction = Transaction::new_signed_with_payer(
|
||||||
&[instruction::set_fee(
|
&[instruction::set_epoch_fee(
|
||||||
&id(),
|
&id(),
|
||||||
&stake_pool_accounts.stake_pool.pubkey(),
|
&stake_pool_accounts.stake_pool.pubkey(),
|
||||||
&stake_pool_accounts.manager.pubkey(),
|
&stake_pool_accounts.manager.pubkey(),
|
||||||
|
@ -108,7 +108,7 @@ async fn fail_wrong_manager() {
|
||||||
|
|
||||||
let wrong_manager = Keypair::new();
|
let wrong_manager = Keypair::new();
|
||||||
let transaction = Transaction::new_signed_with_payer(
|
let transaction = Transaction::new_signed_with_payer(
|
||||||
&[instruction::set_fee(
|
&[instruction::set_epoch_fee(
|
||||||
&id(),
|
&id(),
|
||||||
&stake_pool_accounts.stake_pool.pubkey(),
|
&stake_pool_accounts.stake_pool.pubkey(),
|
||||||
&wrong_manager.pubkey(),
|
&wrong_manager.pubkey(),
|
||||||
|
@ -144,7 +144,7 @@ async fn fail_high_fee() {
|
||||||
denominator: 10,
|
denominator: 10,
|
||||||
};
|
};
|
||||||
let transaction = Transaction::new_signed_with_payer(
|
let transaction = Transaction::new_signed_with_payer(
|
||||||
&[instruction::set_fee(
|
&[instruction::set_epoch_fee(
|
||||||
&id(),
|
&id(),
|
||||||
&stake_pool_accounts.stake_pool.pubkey(),
|
&stake_pool_accounts.stake_pool.pubkey(),
|
||||||
&stake_pool_accounts.manager.pubkey(),
|
&stake_pool_accounts.manager.pubkey(),
|
||||||
|
@ -193,7 +193,7 @@ async fn fail_not_updated() {
|
||||||
context.warp_to_slot(50_000).unwrap();
|
context.warp_to_slot(50_000).unwrap();
|
||||||
|
|
||||||
let transaction = Transaction::new_signed_with_payer(
|
let transaction = Transaction::new_signed_with_payer(
|
||||||
&[instruction::set_fee(
|
&[instruction::set_epoch_fee(
|
||||||
&id(),
|
&id(),
|
||||||
&stake_pool_accounts.stake_pool.pubkey(),
|
&stake_pool_accounts.stake_pool.pubkey(),
|
||||||
&stake_pool_accounts.manager.pubkey(),
|
&stake_pool_accounts.manager.pubkey(),
|
||||||
|
|
|
@ -0,0 +1,258 @@
|
||||||
|
#![cfg(feature = "test-bpf")]
|
||||||
|
|
||||||
|
mod helpers;
|
||||||
|
|
||||||
|
use {
|
||||||
|
helpers::*,
|
||||||
|
solana_program_test::*,
|
||||||
|
solana_sdk::{
|
||||||
|
borsh::try_from_slice_unchecked,
|
||||||
|
instruction::InstructionError,
|
||||||
|
signature::{Keypair, Signer},
|
||||||
|
transaction::{Transaction, TransactionError},
|
||||||
|
},
|
||||||
|
spl_stake_pool::{error, id, instruction, state::StakePool},
|
||||||
|
};
|
||||||
|
|
||||||
|
async fn setup(fee: Option<u8>) -> (ProgramTestContext, StakePoolAccounts, u8) {
|
||||||
|
let mut context = program_test().start_with_context().await;
|
||||||
|
let mut stake_pool_accounts = StakePoolAccounts::new();
|
||||||
|
if let Some(fee) = fee {
|
||||||
|
stake_pool_accounts.referral_fee = fee;
|
||||||
|
}
|
||||||
|
stake_pool_accounts
|
||||||
|
.initialize_stake_pool(
|
||||||
|
&mut context.banks_client,
|
||||||
|
&context.payer,
|
||||||
|
&context.last_blockhash,
|
||||||
|
1,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
let new_referral_fee = 15u8;
|
||||||
|
|
||||||
|
(context, stake_pool_accounts, new_referral_fee)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn success_stake() {
|
||||||
|
let (mut context, stake_pool_accounts, new_referral_fee) = setup(None).await;
|
||||||
|
|
||||||
|
let transaction = Transaction::new_signed_with_payer(
|
||||||
|
&[instruction::set_stake_referral_fee(
|
||||||
|
&id(),
|
||||||
|
&stake_pool_accounts.stake_pool.pubkey(),
|
||||||
|
&stake_pool_accounts.manager.pubkey(),
|
||||||
|
new_referral_fee,
|
||||||
|
)],
|
||||||
|
Some(&context.payer.pubkey()),
|
||||||
|
&[&context.payer, &stake_pool_accounts.manager],
|
||||||
|
context.last_blockhash,
|
||||||
|
);
|
||||||
|
context
|
||||||
|
.banks_client
|
||||||
|
.process_transaction(transaction)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let stake_pool = get_account(
|
||||||
|
&mut context.banks_client,
|
||||||
|
&stake_pool_accounts.stake_pool.pubkey(),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
let stake_pool = try_from_slice_unchecked::<StakePool>(&stake_pool.data.as_slice()).unwrap();
|
||||||
|
assert_eq!(stake_pool.stake_referral_fee, new_referral_fee);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn success_stake_increase_fee_from_0() {
|
||||||
|
let (mut context, stake_pool_accounts, _) = setup(Some(0u8)).await;
|
||||||
|
let new_referral_fee = 30u8;
|
||||||
|
|
||||||
|
let transaction = Transaction::new_signed_with_payer(
|
||||||
|
&[instruction::set_stake_referral_fee(
|
||||||
|
&id(),
|
||||||
|
&stake_pool_accounts.stake_pool.pubkey(),
|
||||||
|
&stake_pool_accounts.manager.pubkey(),
|
||||||
|
new_referral_fee,
|
||||||
|
)],
|
||||||
|
Some(&context.payer.pubkey()),
|
||||||
|
&[&context.payer, &stake_pool_accounts.manager],
|
||||||
|
context.last_blockhash,
|
||||||
|
);
|
||||||
|
context
|
||||||
|
.banks_client
|
||||||
|
.process_transaction(transaction)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let stake_pool = get_account(
|
||||||
|
&mut context.banks_client,
|
||||||
|
&stake_pool_accounts.stake_pool.pubkey(),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
let stake_pool = try_from_slice_unchecked::<StakePool>(&stake_pool.data.as_slice()).unwrap();
|
||||||
|
assert_eq!(stake_pool.stake_referral_fee, new_referral_fee);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn fail_stake_wrong_manager() {
|
||||||
|
let (mut context, stake_pool_accounts, new_referral_fee) = setup(None).await;
|
||||||
|
|
||||||
|
let wrong_manager = Keypair::new();
|
||||||
|
let transaction = Transaction::new_signed_with_payer(
|
||||||
|
&[instruction::set_stake_referral_fee(
|
||||||
|
&id(),
|
||||||
|
&stake_pool_accounts.stake_pool.pubkey(),
|
||||||
|
&wrong_manager.pubkey(),
|
||||||
|
new_referral_fee,
|
||||||
|
)],
|
||||||
|
Some(&context.payer.pubkey()),
|
||||||
|
&[&context.payer, &wrong_manager],
|
||||||
|
context.last_blockhash,
|
||||||
|
);
|
||||||
|
let error = context
|
||||||
|
.banks_client
|
||||||
|
.process_transaction(transaction)
|
||||||
|
.await
|
||||||
|
.err()
|
||||||
|
.unwrap()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
match error {
|
||||||
|
TransactionError::InstructionError(_, InstructionError::Custom(error_index)) => {
|
||||||
|
let program_error = error::StakePoolError::WrongManager as u32;
|
||||||
|
assert_eq!(error_index, program_error);
|
||||||
|
}
|
||||||
|
_ => panic!("Wrong error occurs while signing with the wrong manager"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn fail_stake_high_referral_fee() {
|
||||||
|
let (mut context, stake_pool_accounts, _new_referral_fee) = setup(None).await;
|
||||||
|
|
||||||
|
let new_referral_fee = 110u8;
|
||||||
|
let transaction = Transaction::new_signed_with_payer(
|
||||||
|
&[instruction::set_stake_referral_fee(
|
||||||
|
&id(),
|
||||||
|
&stake_pool_accounts.stake_pool.pubkey(),
|
||||||
|
&stake_pool_accounts.manager.pubkey(),
|
||||||
|
new_referral_fee,
|
||||||
|
)],
|
||||||
|
Some(&context.payer.pubkey()),
|
||||||
|
&[&context.payer, &stake_pool_accounts.manager],
|
||||||
|
context.last_blockhash,
|
||||||
|
);
|
||||||
|
let error = context
|
||||||
|
.banks_client
|
||||||
|
.process_transaction(transaction)
|
||||||
|
.await
|
||||||
|
.err()
|
||||||
|
.unwrap()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
match error {
|
||||||
|
TransactionError::InstructionError(_, InstructionError::Custom(error_index)) => {
|
||||||
|
let program_error = error::StakePoolError::FeeTooHigh as u32;
|
||||||
|
assert_eq!(error_index, program_error);
|
||||||
|
}
|
||||||
|
_ => panic!("Wrong error occurs when setting fee too high"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn success_sol() {
|
||||||
|
let (mut context, stake_pool_accounts, new_referral_fee) = setup(None).await;
|
||||||
|
|
||||||
|
let transaction = Transaction::new_signed_with_payer(
|
||||||
|
&[instruction::set_sol_referral_fee(
|
||||||
|
&id(),
|
||||||
|
&stake_pool_accounts.stake_pool.pubkey(),
|
||||||
|
&stake_pool_accounts.manager.pubkey(),
|
||||||
|
new_referral_fee,
|
||||||
|
)],
|
||||||
|
Some(&context.payer.pubkey()),
|
||||||
|
&[&context.payer, &stake_pool_accounts.manager],
|
||||||
|
context.last_blockhash,
|
||||||
|
);
|
||||||
|
context
|
||||||
|
.banks_client
|
||||||
|
.process_transaction(transaction)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let stake_pool = get_account(
|
||||||
|
&mut context.banks_client,
|
||||||
|
&stake_pool_accounts.stake_pool.pubkey(),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
let stake_pool = try_from_slice_unchecked::<StakePool>(&stake_pool.data.as_slice()).unwrap();
|
||||||
|
assert_eq!(stake_pool.sol_referral_fee, new_referral_fee);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn fail_sol_wrong_manager() {
|
||||||
|
let (mut context, stake_pool_accounts, new_referral_fee) = setup(None).await;
|
||||||
|
|
||||||
|
let wrong_manager = Keypair::new();
|
||||||
|
let transaction = Transaction::new_signed_with_payer(
|
||||||
|
&[instruction::set_sol_referral_fee(
|
||||||
|
&id(),
|
||||||
|
&stake_pool_accounts.stake_pool.pubkey(),
|
||||||
|
&wrong_manager.pubkey(),
|
||||||
|
new_referral_fee,
|
||||||
|
)],
|
||||||
|
Some(&context.payer.pubkey()),
|
||||||
|
&[&context.payer, &wrong_manager],
|
||||||
|
context.last_blockhash,
|
||||||
|
);
|
||||||
|
let error = context
|
||||||
|
.banks_client
|
||||||
|
.process_transaction(transaction)
|
||||||
|
.await
|
||||||
|
.err()
|
||||||
|
.unwrap()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
match error {
|
||||||
|
TransactionError::InstructionError(_, InstructionError::Custom(error_index)) => {
|
||||||
|
let program_error = error::StakePoolError::WrongManager as u32;
|
||||||
|
assert_eq!(error_index, program_error);
|
||||||
|
}
|
||||||
|
_ => panic!("Wrong error occurs while signing with the wrong manager"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn fail_sol_high_referral_fee() {
|
||||||
|
let (mut context, stake_pool_accounts, _new_referral_fee) = setup(None).await;
|
||||||
|
|
||||||
|
let new_referral_fee = 110u8;
|
||||||
|
let transaction = Transaction::new_signed_with_payer(
|
||||||
|
&[instruction::set_sol_referral_fee(
|
||||||
|
&id(),
|
||||||
|
&stake_pool_accounts.stake_pool.pubkey(),
|
||||||
|
&stake_pool_accounts.manager.pubkey(),
|
||||||
|
new_referral_fee,
|
||||||
|
)],
|
||||||
|
Some(&context.payer.pubkey()),
|
||||||
|
&[&context.payer, &stake_pool_accounts.manager],
|
||||||
|
context.last_blockhash,
|
||||||
|
);
|
||||||
|
let error = context
|
||||||
|
.banks_client
|
||||||
|
.process_transaction(transaction)
|
||||||
|
.await
|
||||||
|
.err()
|
||||||
|
.unwrap()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
match error {
|
||||||
|
TransactionError::InstructionError(_, InstructionError::Custom(error_index)) => {
|
||||||
|
let program_error = error::StakePoolError::FeeTooHigh as u32;
|
||||||
|
assert_eq!(error_index, program_error);
|
||||||
|
}
|
||||||
|
_ => panic!("Wrong error occurs when setting fee too high"),
|
||||||
|
}
|
||||||
|
}
|
|
@ -321,42 +321,6 @@ async fn fail_high_withdrawal_fee_increase_from_0() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn fail_bad_fee() {
|
|
||||||
let (mut context, stake_pool_accounts, _new_fee) = setup(None).await;
|
|
||||||
|
|
||||||
let new_withdrawal_fee = Fee {
|
|
||||||
numerator: 11,
|
|
||||||
denominator: 10,
|
|
||||||
};
|
|
||||||
let transaction = Transaction::new_signed_with_payer(
|
|
||||||
&[instruction::set_withdrawal_fee(
|
|
||||||
&id(),
|
|
||||||
&stake_pool_accounts.stake_pool.pubkey(),
|
|
||||||
&stake_pool_accounts.manager.pubkey(),
|
|
||||||
new_withdrawal_fee,
|
|
||||||
)],
|
|
||||||
Some(&context.payer.pubkey()),
|
|
||||||
&[&context.payer, &stake_pool_accounts.manager],
|
|
||||||
context.last_blockhash,
|
|
||||||
);
|
|
||||||
let error = context
|
|
||||||
.banks_client
|
|
||||||
.process_transaction(transaction)
|
|
||||||
.await
|
|
||||||
.err()
|
|
||||||
.unwrap()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
match error {
|
|
||||||
TransactionError::InstructionError(_, InstructionError::Custom(error_index)) => {
|
|
||||||
let program_error = error::StakePoolError::FeeTooHigh as u32;
|
|
||||||
assert_eq!(error_index, program_error);
|
|
||||||
}
|
|
||||||
_ => panic!("Wrong error occurs when setting fee too high"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn fail_not_updated() {
|
async fn fail_not_updated() {
|
||||||
let mut context = program_test().start_with_context().await;
|
let mut context = program_test().start_with_context().await;
|
||||||
|
|
|
@ -44,7 +44,7 @@ async fn setup() -> (
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let _deposit_info = simple_deposit(
|
let _deposit_info = simple_deposit_stake(
|
||||||
&mut context.banks_client,
|
&mut context.banks_client,
|
||||||
&context.payer,
|
&context.payer,
|
||||||
&context.last_blockhash,
|
&context.last_blockhash,
|
||||||
|
@ -65,6 +65,12 @@ async fn setup() -> (
|
||||||
async fn success() {
|
async fn success() {
|
||||||
let (mut context, stake_pool_accounts, stake_accounts) = setup().await;
|
let (mut context, stake_pool_accounts, stake_accounts) = setup().await;
|
||||||
|
|
||||||
|
let pre_fee = get_token_balance(
|
||||||
|
&mut context.banks_client,
|
||||||
|
&stake_pool_accounts.pool_fee_account.pubkey(),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
let pre_balance = get_validator_list_sum(
|
let pre_balance = get_validator_list_sum(
|
||||||
&mut context.banks_client,
|
&mut context.banks_client,
|
||||||
&stake_pool_accounts.reserve_stake.pubkey(),
|
&stake_pool_accounts.reserve_stake.pubkey(),
|
||||||
|
@ -134,7 +140,7 @@ async fn success() {
|
||||||
let stake_pool = try_from_slice_unchecked::<StakePool>(&stake_pool.data.as_slice()).unwrap();
|
let stake_pool = try_from_slice_unchecked::<StakePool>(&stake_pool.data.as_slice()).unwrap();
|
||||||
assert_eq!(post_balance, stake_pool.total_stake_lamports);
|
assert_eq!(post_balance, stake_pool.total_stake_lamports);
|
||||||
|
|
||||||
let actual_fee = get_token_balance(
|
let post_fee = get_token_balance(
|
||||||
&mut context.banks_client,
|
&mut context.banks_client,
|
||||||
&stake_pool_accounts.pool_fee_account.pubkey(),
|
&stake_pool_accounts.pool_fee_account.pubkey(),
|
||||||
)
|
)
|
||||||
|
@ -144,6 +150,7 @@ async fn success() {
|
||||||
&stake_pool_accounts.pool_mint.pubkey(),
|
&stake_pool_accounts.pool_mint.pubkey(),
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
let actual_fee = post_fee - pre_fee;
|
||||||
assert_eq!(pool_token_supply - pre_token_supply, actual_fee);
|
assert_eq!(pool_token_supply - pre_token_supply, actual_fee);
|
||||||
|
|
||||||
let expected_fee_lamports =
|
let expected_fee_lamports =
|
||||||
|
|
|
@ -103,7 +103,7 @@ async fn setup(
|
||||||
|
|
||||||
for deposit_account in &mut deposit_accounts {
|
for deposit_account in &mut deposit_accounts {
|
||||||
deposit_account
|
deposit_account
|
||||||
.deposit(
|
.deposit_stake(
|
||||||
&mut context.banks_client,
|
&mut context.banks_client,
|
||||||
&context.payer,
|
&context.payer,
|
||||||
&context.last_blockhash,
|
&context.last_blockhash,
|
||||||
|
|
|
@ -434,7 +434,7 @@ async fn success_with_deactivating_transient_stake() {
|
||||||
|
|
||||||
let rent = banks_client.get_rent().await.unwrap();
|
let rent = banks_client.get_rent().await.unwrap();
|
||||||
let stake_rent = rent.minimum_balance(std::mem::size_of::<stake_program::StakeState>());
|
let stake_rent = rent.minimum_balance(std::mem::size_of::<stake_program::StakeState>());
|
||||||
let deposit_info = simple_deposit(
|
let deposit_info = simple_deposit_stake(
|
||||||
&mut banks_client,
|
&mut banks_client,
|
||||||
&payer,
|
&payer,
|
||||||
&recent_blockhash,
|
&recent_blockhash,
|
||||||
|
@ -472,7 +472,7 @@ async fn success_with_deactivating_transient_stake() {
|
||||||
assert!(error.is_none());
|
assert!(error.is_none());
|
||||||
|
|
||||||
// fail deposit
|
// fail deposit
|
||||||
let maybe_deposit = simple_deposit(
|
let maybe_deposit = simple_deposit_stake(
|
||||||
&mut banks_client,
|
&mut banks_client,
|
||||||
&payer,
|
&payer,
|
||||||
&recent_blockhash,
|
&recent_blockhash,
|
||||||
|
|
|
@ -51,7 +51,7 @@ async fn setup() -> (
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let deposit_info = simple_deposit(
|
let deposit_info = simple_deposit_stake(
|
||||||
&mut banks_client,
|
&mut banks_client,
|
||||||
&payer,
|
&payer,
|
||||||
&recent_blockhash,
|
&recent_blockhash,
|
||||||
|
@ -274,7 +274,7 @@ async fn fail_with_wrong_stake_program() {
|
||||||
let instruction = Instruction {
|
let instruction = Instruction {
|
||||||
program_id: id(),
|
program_id: id(),
|
||||||
accounts,
|
accounts,
|
||||||
data: instruction::StakePoolInstruction::Withdraw(tokens_to_burn)
|
data: instruction::StakePoolInstruction::WithdrawStake(tokens_to_burn)
|
||||||
.try_to_vec()
|
.try_to_vec()
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
};
|
};
|
||||||
|
@ -361,7 +361,7 @@ async fn fail_with_wrong_token_program_id() {
|
||||||
let wrong_token_program = Keypair::new();
|
let wrong_token_program = Keypair::new();
|
||||||
|
|
||||||
let transaction = Transaction::new_signed_with_payer(
|
let transaction = Transaction::new_signed_with_payer(
|
||||||
&[instruction::withdraw(
|
&[instruction::withdraw_stake(
|
||||||
&id(),
|
&id(),
|
||||||
&stake_pool_accounts.stake_pool.pubkey(),
|
&stake_pool_accounts.stake_pool.pubkey(),
|
||||||
&stake_pool_accounts.validator_list.pubkey(),
|
&stake_pool_accounts.validator_list.pubkey(),
|
||||||
|
@ -475,6 +475,67 @@ async fn fail_with_unknown_validator() {
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
|
let user_pool_account = Keypair::new();
|
||||||
|
let user = Keypair::new();
|
||||||
|
create_token_account(
|
||||||
|
&mut banks_client,
|
||||||
|
&payer,
|
||||||
|
&recent_blockhash,
|
||||||
|
&user_pool_account,
|
||||||
|
&stake_pool_accounts.pool_mint.pubkey(),
|
||||||
|
&user.pubkey(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let user = Keypair::new();
|
||||||
|
// make stake account
|
||||||
|
let user_stake = Keypair::new();
|
||||||
|
let lockup = stake_program::Lockup::default();
|
||||||
|
let authorized = stake_program::Authorized {
|
||||||
|
staker: stake_pool_accounts.stake_deposit_authority,
|
||||||
|
withdrawer: stake_pool_accounts.stake_deposit_authority,
|
||||||
|
};
|
||||||
|
create_independent_stake_account(
|
||||||
|
&mut banks_client,
|
||||||
|
&payer,
|
||||||
|
&recent_blockhash,
|
||||||
|
&user_stake,
|
||||||
|
&authorized,
|
||||||
|
&lockup,
|
||||||
|
TEST_STAKE_AMOUNT,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
// make pool token account
|
||||||
|
let user_pool_account = Keypair::new();
|
||||||
|
create_token_account(
|
||||||
|
&mut banks_client,
|
||||||
|
&payer,
|
||||||
|
&recent_blockhash,
|
||||||
|
&user_pool_account,
|
||||||
|
&stake_pool_accounts.pool_mint.pubkey(),
|
||||||
|
&user.pubkey(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let user_pool_account = user_pool_account.pubkey();
|
||||||
|
let pool_tokens = get_token_balance(&mut banks_client, &user_pool_account).await;
|
||||||
|
|
||||||
|
let tokens_to_burn = pool_tokens / 4;
|
||||||
|
|
||||||
|
// Delegate tokens for burning
|
||||||
|
delegate_tokens(
|
||||||
|
&mut banks_client,
|
||||||
|
&payer,
|
||||||
|
&recent_blockhash,
|
||||||
|
&user_pool_account,
|
||||||
|
&user,
|
||||||
|
&user_transfer_authority.pubkey(),
|
||||||
|
tokens_to_burn,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
let new_authority = Pubkey::new_unique();
|
let new_authority = Pubkey::new_unique();
|
||||||
let transaction_error = stake_pool_accounts
|
let transaction_error = stake_pool_accounts
|
||||||
.withdraw_stake(
|
.withdraw_stake(
|
||||||
|
@ -585,7 +646,7 @@ async fn fail_without_token_approval() {
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let deposit_info = simple_deposit(
|
let deposit_info = simple_deposit_stake(
|
||||||
&mut banks_client,
|
&mut banks_client,
|
||||||
&payer,
|
&payer,
|
||||||
&recent_blockhash,
|
&recent_blockhash,
|
||||||
|
@ -656,7 +717,7 @@ async fn fail_with_low_delegation() {
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let deposit_info = simple_deposit(
|
let deposit_info = simple_deposit_stake(
|
||||||
&mut banks_client,
|
&mut banks_client,
|
||||||
&payer,
|
&payer,
|
||||||
&recent_blockhash,
|
&recent_blockhash,
|
||||||
|
@ -796,7 +857,7 @@ async fn success_with_reserve() {
|
||||||
let rent = context.banks_client.get_rent().await.unwrap();
|
let rent = context.banks_client.get_rent().await.unwrap();
|
||||||
let stake_rent = rent.minimum_balance(std::mem::size_of::<stake_program::StakeState>());
|
let stake_rent = rent.minimum_balance(std::mem::size_of::<stake_program::StakeState>());
|
||||||
|
|
||||||
let deposit_info = simple_deposit(
|
let deposit_info = simple_deposit_stake(
|
||||||
&mut context.banks_client,
|
&mut context.banks_client,
|
||||||
&context.payer,
|
&context.payer,
|
||||||
&context.last_blockhash,
|
&context.last_blockhash,
|
||||||
|
@ -930,10 +991,12 @@ async fn success_with_reserve() {
|
||||||
assert!(error.is_none());
|
assert!(error.is_none());
|
||||||
|
|
||||||
// first and only deposit, lamports:pool 1:1
|
// first and only deposit, lamports:pool 1:1
|
||||||
|
let tokens_deposit_fee =
|
||||||
|
stake_pool_accounts.calculate_deposit_fee(deposit_info.stake_lamports + stake_rent);
|
||||||
let tokens_withdrawal_fee =
|
let tokens_withdrawal_fee =
|
||||||
stake_pool_accounts.calculate_withdrawal_fee(deposit_info.pool_tokens);
|
stake_pool_accounts.calculate_withdrawal_fee(deposit_info.pool_tokens);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
deposit_info.stake_lamports + stake_rent,
|
deposit_info.stake_lamports + stake_rent - tokens_deposit_fee,
|
||||||
deposit_info.pool_tokens,
|
deposit_info.pool_tokens,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -954,8 +1017,12 @@ async fn success_with_reserve() {
|
||||||
let stake_state =
|
let stake_state =
|
||||||
deserialize::<stake_program::StakeState>(&reserve_stake_account.data).unwrap();
|
deserialize::<stake_program::StakeState>(&reserve_stake_account.data).unwrap();
|
||||||
let meta = stake_state.meta().unwrap();
|
let meta = stake_state.meta().unwrap();
|
||||||
|
// TODO: these numbers dont add up even with +tokens_deposit_fee
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
initial_reserve_lamports + meta.rent_exempt_reserve + tokens_withdrawal_fee,
|
initial_reserve_lamports
|
||||||
|
+ meta.rent_exempt_reserve
|
||||||
|
+ tokens_withdrawal_fee
|
||||||
|
+ tokens_deposit_fee,
|
||||||
reserve_stake_account.lamports
|
reserve_stake_account.lamports
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -964,7 +1031,9 @@ async fn success_with_reserve() {
|
||||||
get_account(&mut context.banks_client, &withdraw_destination.pubkey()).await;
|
get_account(&mut context.banks_client, &withdraw_destination.pubkey()).await;
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
user_stake_recipient_account.lamports,
|
user_stake_recipient_account.lamports,
|
||||||
initial_stake_lamports + deposit_info.stake_lamports + stake_rent - tokens_withdrawal_fee
|
initial_stake_lamports + deposit_info.stake_lamports + stake_rent
|
||||||
|
- tokens_withdrawal_fee
|
||||||
|
- tokens_deposit_fee
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1059,7 +1128,7 @@ async fn fail_with_wrong_preferred_withdraw() {
|
||||||
assert!(error.is_none());
|
assert!(error.is_none());
|
||||||
|
|
||||||
// deposit into preferred, then fail
|
// deposit into preferred, then fail
|
||||||
let _preferred_deposit = simple_deposit(
|
let _preferred_deposit = simple_deposit_stake(
|
||||||
&mut banks_client,
|
&mut banks_client,
|
||||||
&payer,
|
&payer,
|
||||||
&recent_blockhash,
|
&recent_blockhash,
|
||||||
|
@ -1152,7 +1221,7 @@ async fn success_withdraw_from_transient() {
|
||||||
let rent = context.banks_client.get_rent().await.unwrap();
|
let rent = context.banks_client.get_rent().await.unwrap();
|
||||||
let stake_rent = rent.minimum_balance(std::mem::size_of::<stake_program::StakeState>());
|
let stake_rent = rent.minimum_balance(std::mem::size_of::<stake_program::StakeState>());
|
||||||
|
|
||||||
let deposit_info = simple_deposit(
|
let deposit_info = simple_deposit_stake(
|
||||||
&mut context.banks_client,
|
&mut context.banks_client,
|
||||||
&context.payer,
|
&context.payer,
|
||||||
&context.last_blockhash,
|
&context.last_blockhash,
|
||||||
|
|
Loading…
Reference in New Issue