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)
|
||||
do
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -57,7 +57,7 @@ withdraw_stakes () {
|
|||
pool_amount=$3
|
||||
for validator in $(cat $validator_list)
|
||||
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
|
||||
}
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ use {
|
|||
input_parsers::{keypair_of, pubkey_of},
|
||||
input_validators::{
|
||||
is_amount, is_keypair, is_keypair_or_ask_keyword, is_parsable, is_pubkey, is_url,
|
||||
is_valid_percentage,
|
||||
},
|
||||
keypair::signer_from_path,
|
||||
},
|
||||
|
@ -38,7 +39,7 @@ use {
|
|||
find_withdraw_authority_program_address,
|
||||
instruction::PreferredValidatorType,
|
||||
stake_program::{self, StakeState},
|
||||
state::{Fee, StakePool, ValidatorList},
|
||||
state::{Fee, FeeType, StakePool, ValidatorList},
|
||||
},
|
||||
std::{process::exit, sync::Arc},
|
||||
};
|
||||
|
@ -49,6 +50,7 @@ struct Config {
|
|||
manager: Box<dyn Signer>,
|
||||
staker: Box<dyn Signer>,
|
||||
depositor: Option<Box<dyn Signer>>,
|
||||
sol_depositor: Option<Box<dyn Signer>>,
|
||||
token_owner: Box<dyn Signer>,
|
||||
fee_payer: Box<dyn Signer>,
|
||||
dry_run: bool,
|
||||
|
@ -153,9 +155,11 @@ fn checked_transaction_with_signers<T: Signers>(
|
|||
#[allow(clippy::too_many_arguments)]
|
||||
fn command_create_pool(
|
||||
config: &Config,
|
||||
deposit_authority: Option<Pubkey>,
|
||||
stake_deposit_authority: Option<Keypair>,
|
||||
fee: Fee,
|
||||
withdrawal_fee: Fee,
|
||||
stake_deposit_fee: Fee,
|
||||
stake_referral_fee: u8,
|
||||
max_validators: u32,
|
||||
stake_pool_keypair: Option<Keypair>,
|
||||
mint_keypair: Option<Keypair>,
|
||||
|
@ -283,9 +287,11 @@ fn command_create_pool(
|
|||
&mint_keypair.pubkey(),
|
||||
&pool_fee_account,
|
||||
&spl_token::id(),
|
||||
deposit_authority,
|
||||
stake_deposit_authority.as_ref().map(|x| x.pubkey()),
|
||||
fee,
|
||||
withdrawal_fee,
|
||||
stake_deposit_fee,
|
||||
stake_referral_fee,
|
||||
max_validators,
|
||||
),
|
||||
],
|
||||
|
@ -311,8 +317,15 @@ fn command_create_pool(
|
|||
&validator_list,
|
||||
config.manager.as_ref(),
|
||||
];
|
||||
unique_signers!(initialize_signers);
|
||||
initialize_transaction.sign(&initialize_signers, recent_blockhash);
|
||||
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);
|
||||
initialize_transaction.sign(&initialize_signers, recent_blockhash);
|
||||
} else {
|
||||
unique_signers!(initialize_signers);
|
||||
initialize_transaction.sign(&initialize_signers, recent_blockhash);
|
||||
}
|
||||
send_transaction(config, initialize_transaction)?;
|
||||
Ok(())
|
||||
}
|
||||
|
@ -567,11 +580,12 @@ fn add_associated_token_account(
|
|||
account
|
||||
}
|
||||
|
||||
fn command_deposit(
|
||||
fn command_deposit_stake(
|
||||
config: &Config,
|
||||
stake_pool_address: &Pubkey,
|
||||
stake: &Pubkey,
|
||||
token_receiver: &Option<Pubkey>,
|
||||
pool_token_receiver_account: &Option<Pubkey>,
|
||||
referrer_token_account: &Option<Pubkey>,
|
||||
) -> CommandResult {
|
||||
if !config.no_update {
|
||||
command_update(config, stake_pool_address, false, false)?;
|
||||
|
@ -613,44 +627,50 @@ fn command_deposit(
|
|||
let mut total_rent_free_balances: u64 = 0;
|
||||
|
||||
// Create token account if not specified
|
||||
let token_receiver = token_receiver.unwrap_or(add_associated_token_account(
|
||||
config,
|
||||
&stake_pool.pool_mint,
|
||||
&config.token_owner.pubkey(),
|
||||
&mut instructions,
|
||||
&mut total_rent_free_balances,
|
||||
));
|
||||
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(deposit_authority) = config.depositor.as_ref() {
|
||||
signers.push(deposit_authority.as_ref());
|
||||
if deposit_authority.pubkey() != stake_pool.deposit_authority {
|
||||
let mut deposit_instructions = if let Some(stake_deposit_authority) = config.depositor.as_ref()
|
||||
{
|
||||
signers.push(stake_deposit_authority.as_ref());
|
||||
if stake_deposit_authority.pubkey() != stake_pool.stake_deposit_authority {
|
||||
let error = format!(
|
||||
"Invalid deposit authority specified, expected {}, received {}",
|
||||
stake_pool.deposit_authority,
|
||||
deposit_authority.pubkey()
|
||||
stake_pool.stake_deposit_authority,
|
||||
stake_deposit_authority.pubkey()
|
||||
);
|
||||
return Err(error.into());
|
||||
}
|
||||
|
||||
spl_stake_pool::instruction::deposit_with_authority(
|
||||
spl_stake_pool::instruction::deposit_stake_with_authority(
|
||||
&spl_stake_pool::id(),
|
||||
stake_pool_address,
|
||||
&stake_pool.validator_list,
|
||||
&deposit_authority.pubkey(),
|
||||
&stake_deposit_authority.pubkey(),
|
||||
&pool_withdraw_authority,
|
||||
stake,
|
||||
&config.staker.pubkey(),
|
||||
&validator_stake_account,
|
||||
&stake_pool.reserve_stake,
|
||||
&token_receiver,
|
||||
&pool_token_receiver_account,
|
||||
&stake_pool.manager_fee_account,
|
||||
&referrer_token_account,
|
||||
&stake_pool.pool_mint,
|
||||
&spl_token::id(),
|
||||
)
|
||||
} else {
|
||||
spl_stake_pool::instruction::deposit(
|
||||
spl_stake_pool::instruction::deposit_stake(
|
||||
&spl_stake_pool::id(),
|
||||
stake_pool_address,
|
||||
&stake_pool.validator_list,
|
||||
|
@ -659,7 +679,9 @@ fn command_deposit(
|
|||
&config.staker.pubkey(),
|
||||
&validator_stake_account,
|
||||
&stake_pool.reserve_stake,
|
||||
&token_receiver,
|
||||
&pool_token_receiver_account,
|
||||
&stake_pool.manager_fee_account,
|
||||
&referrer_token_account,
|
||||
&stake_pool.pool_mint,
|
||||
&spl_token::id(),
|
||||
)
|
||||
|
@ -681,6 +703,136 @@ fn command_deposit(
|
|||
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 {
|
||||
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)?;
|
||||
|
@ -688,6 +840,9 @@ fn command_list(config: &Config, stake_pool_address: &Pubkey) -> CommandResult {
|
|||
let epoch_info = config.rpc_client.get_epoch_info()?;
|
||||
let pool_withdraw_authority =
|
||||
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 {
|
||||
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!("Manager: {}", stake_pool.manager);
|
||||
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!("Pool Token Mint: {}", stake_pool.pool_mint);
|
||||
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
|
||||
);
|
||||
}
|
||||
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!(
|
||||
"Fee: {}/{} of epoch rewards",
|
||||
stake_pool.fee.numerator, stake_pool.fee.denominator
|
||||
"Withdrawal Fee: {} of withdrawal amount",
|
||||
stake_pool.withdrawal_fee
|
||||
);
|
||||
} 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 {
|
||||
|
@ -1079,7 +1274,7 @@ fn command_withdraw(
|
|||
stake_receiver_pubkey
|
||||
});
|
||||
|
||||
instructions.push(spl_stake_pool::instruction::withdraw(
|
||||
instructions.push(spl_stake_pool::instruction::withdraw_stake(
|
||||
&spl_stake_pool::id(),
|
||||
stake_pool_address,
|
||||
&stake_pool.validator_list,
|
||||
|
@ -1179,16 +1374,22 @@ fn command_set_staker(
|
|||
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()];
|
||||
unique_signers!(signers);
|
||||
let transaction = checked_transaction_with_signers(
|
||||
config,
|
||||
&[spl_stake_pool::instruction::set_fee(
|
||||
&[spl_stake_pool::instruction::set_deposit_authority(
|
||||
&spl_stake_pool::id(),
|
||||
stake_pool_address,
|
||||
&config.manager.pubkey(),
|
||||
new_fee,
|
||||
new_sol_deposit_authority.as_ref(),
|
||||
for_stake_deposit,
|
||||
)],
|
||||
&signers,
|
||||
)?;
|
||||
|
@ -1196,16 +1397,16 @@ fn command_set_fee(config: &Config, stake_pool_address: &Pubkey, new_fee: Fee) -
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn command_set_withdrawal_fee(
|
||||
fn command_set_fee(
|
||||
config: &Config,
|
||||
stake_pool_address: &Pubkey,
|
||||
new_fee: Fee,
|
||||
new_fee: FeeType,
|
||||
) -> CommandResult {
|
||||
let mut signers = vec![config.fee_payer.as_ref(), config.manager.as_ref()];
|
||||
unique_signers!(signers);
|
||||
let transaction = checked_transaction_with_signers(
|
||||
config,
|
||||
&[spl_stake_pool::instruction::set_withdrawal_fee(
|
||||
&[spl_stake_pool::instruction::set_fee(
|
||||
&spl_stake_pool::id(),
|
||||
stake_pool_address,
|
||||
&config.manager.pubkey(),
|
||||
|
@ -1366,6 +1567,31 @@ fn main() {
|
|||
.requires("withdrawal_fee_numerator")
|
||||
.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::with_name("max_validators")
|
||||
.long("max-validators")
|
||||
|
@ -1377,11 +1603,11 @@ fn main() {
|
|||
.help("Max number of validators included in the stake pool"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("deposit_authority")
|
||||
.long("deposit-authority")
|
||||
Arg::with_name("stake_deposit_authority")
|
||||
.long("stake-deposit-authority")
|
||||
.short("a")
|
||||
.validator(is_pubkey)
|
||||
.value_name("DEPOSIT_AUTHORITY_ADDRESS")
|
||||
.validator(is_keypair_or_ask_keyword)
|
||||
.value_name("STAKE_DEPOSIT_AUTHORITY_KEYPAIR")
|
||||
.takes_value(true)
|
||||
.help("Deposit authority required to sign all deposits into the stake pool"),
|
||||
)
|
||||
|
@ -1581,8 +1807,8 @@ fn main() {
|
|||
.required(true)
|
||||
)
|
||||
)
|
||||
.subcommand(SubCommand::with_name("deposit")
|
||||
.about("Add stake account to the stake pool")
|
||||
.subcommand(SubCommand::with_name("deposit-stake")
|
||||
.about("Deposit active stake account into the stake pool in exchange for pool tokens")
|
||||
.arg(
|
||||
Arg::with_name("pool")
|
||||
.index(1)
|
||||
|
@ -1611,6 +1837,61 @@ fn main() {
|
|||
Defaults to the token-owner's associated pool token account. \
|
||||
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")
|
||||
.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."),
|
||||
)
|
||||
)
|
||||
.subcommand(SubCommand::with_name("withdraw")
|
||||
.subcommand(SubCommand::with_name("withdraw-stake")
|
||||
.about("Withdraw amount from the stake pool")
|
||||
.arg(
|
||||
Arg::with_name("pool")
|
||||
|
@ -1758,8 +2039,8 @@ fn main() {
|
|||
.help("Public key for the new stake pool staker."),
|
||||
)
|
||||
)
|
||||
.subcommand(SubCommand::with_name("set-fee")
|
||||
.about("Change the fee assessed by the stake pool. Must be signed by the manager.")
|
||||
.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)
|
||||
|
@ -1770,8 +2051,78 @@ fn main() {
|
|||
.help("Stake pool address."),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("fee_numerator")
|
||||
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")
|
||||
.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)
|
||||
|
@ -1780,7 +2131,7 @@ fn main() {
|
|||
)
|
||||
.arg(
|
||||
Arg::with_name("fee_denominator")
|
||||
.index(3)
|
||||
.index(4)
|
||||
.validator(is_parsable::<u64>)
|
||||
.value_name("DENOMINATOR")
|
||||
.takes_value(true)
|
||||
|
@ -1788,8 +2139,8 @@ fn main() {
|
|||
.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.")
|
||||
.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::with_name("pool")
|
||||
.index(1)
|
||||
|
@ -1800,22 +2151,33 @@ fn main() {
|
|||
.help("Stake pool address."),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("fee_numerator")
|
||||
Arg::with_name("fee")
|
||||
.index(2)
|
||||
.validator(is_parsable::<u64>)
|
||||
.value_name("NUMERATOR")
|
||||
.validator(is_valid_percentage)
|
||||
.value_name("FEE_PERCENTAGE")
|
||||
.takes_value(true)
|
||||
.required(true)
|
||||
.help("Fee numerator, fee amount is numerator divided by denominator."),
|
||||
.help("Fee percentage, maximum 100"),
|
||||
)
|
||||
).subcommand(SubCommand::with_name("set-sol-referral-fee")
|
||||
.about("Change the referral fee assessed by the stake pool for SOL deposits. 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_denominator")
|
||||
.index(3)
|
||||
.validator(is_parsable::<u64>)
|
||||
.value_name("DENOMINATOR")
|
||||
Arg::with_name("fee")
|
||||
.index(2)
|
||||
.validator(is_valid_percentage)
|
||||
.value_name("FEE_PERCENTAGE")
|
||||
.takes_value(true)
|
||||
.required(true)
|
||||
.help("Fee denominator, fee amount is numerator divided by denominator."),
|
||||
.help("Fee percentage, maximum 100"),
|
||||
)
|
||||
)
|
||||
.get_matches();
|
||||
|
@ -1847,6 +2209,16 @@ fn main() {
|
|||
} else {
|
||||
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(
|
||||
&matches,
|
||||
"manager",
|
||||
|
@ -1875,6 +2247,7 @@ fn main() {
|
|||
manager,
|
||||
staker,
|
||||
depositor,
|
||||
sol_depositor,
|
||||
token_owner,
|
||||
fee_payer,
|
||||
dry_run,
|
||||
|
@ -1884,18 +2257,21 @@ fn main() {
|
|||
|
||||
let _ = match matches.subcommand() {
|
||||
("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 denominator = value_t_or_exit!(arg_matches, "fee_denominator", u64);
|
||||
let w_numerator = value_t!(arg_matches, "withdrawal_fee_numerator", 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 pool_keypair = keypair_of(arg_matches, "pool_keypair");
|
||||
let mint_keypair = keypair_of(arg_matches, "mint_keypair");
|
||||
let reserve_keypair = keypair_of(arg_matches, "reserve_keypair");
|
||||
command_create_pool(
|
||||
&config,
|
||||
deposit_authority,
|
||||
stake_deposit_authority,
|
||||
Fee {
|
||||
denominator,
|
||||
numerator,
|
||||
|
@ -1904,6 +2280,11 @@ fn main() {
|
|||
numerator: w_numerator.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,
|
||||
pool_keypair,
|
||||
mint_keypair,
|
||||
|
@ -1956,15 +2337,32 @@ fn main() {
|
|||
vote_account,
|
||||
)
|
||||
}
|
||||
("deposit", Some(arg_matches)) => {
|
||||
("deposit-stake", Some(arg_matches)) => {
|
||||
let stake_pool_address = pubkey_of(arg_matches, "pool").unwrap();
|
||||
let stake_account = pubkey_of(arg_matches, "stake_account").unwrap();
|
||||
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,
|
||||
&stake_pool_address,
|
||||
&stake_account,
|
||||
&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)) => {
|
||||
|
@ -1977,7 +2375,7 @@ fn main() {
|
|||
let force = arg_matches.is_present("force");
|
||||
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 vote_account = pubkey_of(arg_matches, "vote_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();
|
||||
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)) => {
|
||||
let stake_pool_address = pubkey_of(arg_matches, "pool").unwrap();
|
||||
let numerator = value_t_or_exit!(arg_matches, "fee_numerator", u64);
|
||||
|
@ -2018,17 +2438,35 @@ fn main() {
|
|||
denominator,
|
||||
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))
|
||||
}
|
||||
"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-withdrawal-fee", Some(arg_matches)) => {
|
||||
("set-stake-referral-fee", Some(arg_matches)) => {
|
||||
let stake_pool_address = pubkey_of(arg_matches, "pool").unwrap();
|
||||
let numerator = value_t_or_exit!(arg_matches, "fee_numerator", u64);
|
||||
let denominator = value_t_or_exit!(arg_matches, "fee_denominator", u64);
|
||||
let new_fee = Fee {
|
||||
denominator,
|
||||
numerator,
|
||||
};
|
||||
command_set_withdrawal_fee(&config, &stake_pool_address, new_fee)
|
||||
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::StakeReferral(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!(),
|
||||
}
|
||||
|
|
|
@ -108,6 +108,14 @@ pub enum StakePoolError {
|
|||
/// Not enough lamports provided for deposit to result in one pool token
|
||||
#[error("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 {
|
||||
fn from(e: StakePoolError) -> Self {
|
||||
|
|
|
@ -6,7 +6,7 @@ use {
|
|||
find_deposit_authority_program_address, find_stake_program_address,
|
||||
find_transient_stake_program_address, find_withdraw_authority_program_address,
|
||||
stake_program,
|
||||
state::{Fee, StakePool, ValidatorList},
|
||||
state::{Fee, FeeType, StakePool, ValidatorList},
|
||||
MAX_VALIDATORS_TO_UPDATE,
|
||||
},
|
||||
borsh::{BorshDeserialize, BorshSchema, BorshSerialize},
|
||||
|
@ -28,6 +28,17 @@ pub enum PreferredValidatorType {
|
|||
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.
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Debug, PartialEq, BorshSerialize, BorshDeserialize, BorshSchema)]
|
||||
|
@ -55,6 +66,12 @@ pub enum StakePoolInstruction {
|
|||
/// Fee charged per withdrawal as percentage of withdrawal
|
||||
#[allow(dead_code)] // but it's not
|
||||
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
|
||||
#[allow(dead_code)] // but it's not
|
||||
max_validators: u32,
|
||||
|
@ -244,18 +261,20 @@ pub enum StakePoolInstruction {
|
|||
///
|
||||
/// 0. `[w]` Stake pool
|
||||
/// 1. `[w]` Validator stake list storage account
|
||||
/// 2. `[]` Stake pool deposit authority
|
||||
/// 2. `[s]/[]` Stake pool deposit 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)
|
||||
/// 5. `[w]` Validator stake account for the stake account to be merged with
|
||||
/// 6. `[w]` Reserve stake account, to withdraw rent exempt reserve
|
||||
/// 7. `[w]` User account to receive pool tokens
|
||||
/// 8. `[w]` Pool token mint account
|
||||
/// 9. '[]' Sysvar clock account
|
||||
/// 10. '[]' Sysvar stake history account
|
||||
/// 11. `[]` Pool token program id,
|
||||
/// 12. `[]` Stake program id,
|
||||
Deposit,
|
||||
/// 8. `[w]` Account to receive pool fee tokens
|
||||
/// 9. `[w]` Account to receive a portion of pool fee tokens as referral fees
|
||||
/// 10. `[w]` Pool token mint account
|
||||
/// 11. '[]' Sysvar clock account
|
||||
/// 12. '[]' Sysvar stake history account
|
||||
/// 13. `[]` Pool token program id,
|
||||
/// 14. `[]` Stake program id,
|
||||
DepositStake,
|
||||
|
||||
/// Withdraw the token from the pool at the current ratio.
|
||||
///
|
||||
|
@ -281,7 +300,7 @@ pub enum StakePoolInstruction {
|
|||
/// 11. `[]` Pool token program id
|
||||
/// 12. `[]` Stake program id,
|
||||
/// userdata: amount of pool tokens to withdraw
|
||||
Withdraw(u64),
|
||||
WithdrawStake(u64),
|
||||
|
||||
/// (Manager only) Update manager
|
||||
///
|
||||
|
@ -297,9 +316,9 @@ pub enum StakePoolInstruction {
|
|||
/// 1. `[s]` Manager
|
||||
/// 2. `[]` Sysvar clock
|
||||
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
|
||||
fee: Fee,
|
||||
fee: FeeType,
|
||||
},
|
||||
|
||||
/// (Manager or staker only) Update staker
|
||||
|
@ -309,16 +328,29 @@ pub enum StakePoolInstruction {
|
|||
/// 2. '[]` New staker pubkey
|
||||
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
|
||||
/// 1. `[s]` Manager
|
||||
/// 2. `[]` Sysvar clock
|
||||
SetWithdrawalFee {
|
||||
/// Fee assessed as percentage of perceived rewards
|
||||
#[allow(dead_code)] // but it's not
|
||||
fee: Fee,
|
||||
},
|
||||
/// 2. '[]` New sol_deposit_authority pubkey or none
|
||||
SetDepositAuthority(DepositType),
|
||||
}
|
||||
|
||||
/// Creates an 'initialize' instruction.
|
||||
|
@ -335,11 +367,15 @@ pub fn initialize(
|
|||
deposit_authority: Option<Pubkey>,
|
||||
fee: Fee,
|
||||
withdrawal_fee: Fee,
|
||||
deposit_fee: Fee,
|
||||
referral_fee: u8,
|
||||
max_validators: u32,
|
||||
) -> Instruction {
|
||||
let init_data = StakePoolInstruction::Initialize {
|
||||
fee,
|
||||
withdrawal_fee,
|
||||
deposit_fee,
|
||||
referral_fee,
|
||||
max_validators,
|
||||
};
|
||||
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
|
||||
/// account owned by the user.
|
||||
pub fn deposit(
|
||||
pub fn deposit_stake(
|
||||
program_id: &Pubkey,
|
||||
stake_pool: &Pubkey,
|
||||
validator_list_storage: &Pubkey,
|
||||
|
@ -836,6 +872,8 @@ pub fn deposit(
|
|||
validator_stake_account: &Pubkey,
|
||||
reserve_stake_account: &Pubkey,
|
||||
pool_tokens_to: &Pubkey,
|
||||
manager_fee_account: &Pubkey,
|
||||
referrer_pool_tokens_account: &Pubkey,
|
||||
pool_mint: &Pubkey,
|
||||
token_program_id: &Pubkey,
|
||||
) -> Vec<Instruction> {
|
||||
|
@ -850,6 +888,8 @@ pub fn deposit(
|
|||
AccountMeta::new(*validator_stake_account, false),
|
||||
AccountMeta::new(*reserve_stake_account, 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_readonly(sysvar::clock::id(), false),
|
||||
AccountMeta::new_readonly(sysvar::stake_history::id(), false),
|
||||
|
@ -872,7 +912,7 @@ pub fn deposit(
|
|||
Instruction {
|
||||
program_id: *program_id,
|
||||
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
|
||||
/// account owned by the user. The difference with `deposit()` is that a deposit
|
||||
/// 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,
|
||||
stake_pool: &Pubkey,
|
||||
validator_list_storage: &Pubkey,
|
||||
|
@ -891,6 +931,8 @@ pub fn deposit_with_authority(
|
|||
validator_stake_account: &Pubkey,
|
||||
reserve_stake_account: &Pubkey,
|
||||
pool_tokens_to: &Pubkey,
|
||||
manager_fee_account: &Pubkey,
|
||||
referrer_pool_tokens_account: &Pubkey,
|
||||
pool_mint: &Pubkey,
|
||||
token_program_id: &Pubkey,
|
||||
) -> Vec<Instruction> {
|
||||
|
@ -903,6 +945,8 @@ pub fn deposit_with_authority(
|
|||
AccountMeta::new(*validator_stake_account, false),
|
||||
AccountMeta::new(*reserve_stake_account, 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_readonly(sysvar::clock::id(), false),
|
||||
AccountMeta::new_readonly(sysvar::stake_history::id(), false),
|
||||
|
@ -925,13 +969,91 @@ pub fn deposit_with_authority(
|
|||
Instruction {
|
||||
program_id: *program_id,
|
||||
accounts,
|
||||
data: StakePoolInstruction::Deposit.try_to_vec().unwrap(),
|
||||
data: StakePoolInstruction::DepositStake.try_to_vec().unwrap(),
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
/// Creates a 'withdraw' instruction.
|
||||
pub fn withdraw(
|
||||
/// Creates instructions required to deposit SOL directly into a stake pool.
|
||||
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,
|
||||
stake_pool: &Pubkey,
|
||||
validator_list_storage: &Pubkey,
|
||||
|
@ -964,7 +1086,9 @@ pub fn withdraw(
|
|||
Instruction {
|
||||
program_id: *program_id,
|
||||
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,
|
||||
stake_pool: &Pubkey,
|
||||
manager: &Pubkey,
|
||||
fee: Fee,
|
||||
fee: FeeType,
|
||||
) -> Instruction {
|
||||
let accounts = vec![
|
||||
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(
|
||||
program_id: &Pubkey,
|
||||
stake_pool: &Pubkey,
|
||||
manager: &Pubkey,
|
||||
fee: Fee,
|
||||
) -> Instruction {
|
||||
let accounts = vec![
|
||||
AccountMeta::new(*stake_pool, false),
|
||||
AccountMeta::new_readonly(*manager, true),
|
||||
AccountMeta::new_readonly(sysvar::clock::id(), false),
|
||||
];
|
||||
Instruction {
|
||||
program_id: *program_id,
|
||||
accounts,
|
||||
data: StakePoolInstruction::SetWithdrawalFee { fee }
|
||||
.try_to_vec()
|
||||
.unwrap(),
|
||||
}
|
||||
set_fee(program_id, stake_pool, manager, FeeType::Withdrawal(fee))
|
||||
}
|
||||
|
||||
/// Creates a 'set fee' instruction for setting SOL deposit fee
|
||||
pub fn set_sol_deposit_fee(
|
||||
program_id: &Pubkey,
|
||||
stake_pool: &Pubkey,
|
||||
manager: &Pubkey,
|
||||
fee: Fee,
|
||||
) -> Instruction {
|
||||
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.
|
||||
|
@ -1047,3 +1210,79 @@ pub fn set_staker(
|
|||
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
|
||||
/// conversions
|
||||
#[inline]
|
||||
pub fn minimum_stake_lamports(meta: &Meta) -> u64 {
|
||||
meta.rent_exempt_reserve
|
||||
.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
|
||||
/// conversions
|
||||
#[inline]
|
||||
pub fn minimum_reserve_lamports(meta: &Meta) -> u64 {
|
||||
meta.rent_exempt_reserve.saturating_add(1)
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
//! Program state processor
|
||||
|
||||
use crate::instruction::DepositType;
|
||||
use {
|
||||
crate::{
|
||||
error::StakePoolError,
|
||||
|
@ -7,11 +8,10 @@ use {
|
|||
instruction::{PreferredValidatorType, StakePoolInstruction},
|
||||
minimum_reserve_lamports, minimum_stake_lamports, stake_program,
|
||||
state::{
|
||||
AccountType, Fee, StakePool, StakeStatus, ValidatorList, ValidatorListHeader,
|
||||
AccountType, Fee, FeeType, StakePool, StakeStatus, ValidatorList, ValidatorListHeader,
|
||||
ValidatorStakeInfo,
|
||||
},
|
||||
AUTHORITY_DEPOSIT, AUTHORITY_WITHDRAW, MAX_WITHDRAWAL_FEE_INCREASE, MINIMUM_ACTIVE_STAKE,
|
||||
TRANSIENT_STAKE_SEED, WITHDRAWAL_BASELINE_FEE,
|
||||
AUTHORITY_DEPOSIT, AUTHORITY_WITHDRAW, MINIMUM_ACTIVE_STAKE, TRANSIENT_STAKE_SEED,
|
||||
},
|
||||
borsh::{BorshDeserialize, BorshSerialize},
|
||||
num_traits::FromPrimitive,
|
||||
|
@ -468,12 +468,24 @@ impl Processor {
|
|||
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.
|
||||
fn process_initialize(
|
||||
program_id: &Pubkey,
|
||||
accounts: &[AccountInfo],
|
||||
fee: Fee,
|
||||
withdrawal_fee: Fee,
|
||||
stake_deposit_fee: Fee,
|
||||
stake_referral_fee: u8,
|
||||
max_validators: u32,
|
||||
) -> ProgramResult {
|
||||
let account_info_iter = &mut accounts.iter();
|
||||
|
@ -538,7 +550,11 @@ impl Processor {
|
|||
}
|
||||
|
||||
// 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());
|
||||
}
|
||||
|
||||
|
@ -556,11 +572,11 @@ impl Processor {
|
|||
return Err(StakePoolError::WrongAccountMint.into());
|
||||
}
|
||||
|
||||
let deposit_authority = match next_account_info(account_info_iter) {
|
||||
Ok(deposit_authority_info) => *deposit_authority_info.key,
|
||||
let stake_deposit_authority = match next_account_info(account_info_iter) {
|
||||
Ok(stake_deposit_authority_info) => *stake_deposit_authority_info.key,
|
||||
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);
|
||||
|
||||
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");
|
||||
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())?;
|
||||
|
||||
|
@ -627,8 +639,8 @@ impl Processor {
|
|||
stake_pool.manager = *manager_info.key;
|
||||
stake_pool.staker = *staker_info.key;
|
||||
stake_pool.reserve_stake = *reserve_stake_info.key;
|
||||
stake_pool.deposit_authority = deposit_authority;
|
||||
stake_pool.withdraw_bump_seed = withdraw_bump_seed;
|
||||
stake_pool.stake_deposit_authority = stake_deposit_authority;
|
||||
stake_pool.stake_withdraw_bump_seed = stake_withdraw_bump_seed;
|
||||
stake_pool.validator_list = *validator_list_info.key;
|
||||
stake_pool.pool_mint = *pool_mint_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.preferred_deposit_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.next_withdrawal_fee = None;
|
||||
stake_pool.stake_referral_fee = stake_referral_fee;
|
||||
stake_pool.sol_deposit_authority = None;
|
||||
|
||||
stake_pool
|
||||
.serialize(&mut *stake_pool_info.data.borrow_mut())
|
||||
|
@ -798,7 +812,7 @@ impl Processor {
|
|||
)?;
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
|
@ -813,20 +827,13 @@ impl Processor {
|
|||
// Check amount of lamports
|
||||
let stake_lamports = **stake_account_info.lamports.borrow();
|
||||
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!(
|
||||
"Error: attempting to add stake with {} lamports, must have {} lamports",
|
||||
"Error: attempting to add (stake: {}, delegation: {}), below minimum",
|
||||
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,
|
||||
MINIMUM_ACTIVE_STAKE
|
||||
);
|
||||
return Err(StakePoolError::StakeLamportsNotEqualToMinimum.into());
|
||||
}
|
||||
|
@ -970,7 +977,7 @@ impl Processor {
|
|||
stake_account_info.clone(),
|
||||
withdraw_authority_info.clone(),
|
||||
AUTHORITY_WITHDRAW,
|
||||
stake_pool.withdraw_bump_seed,
|
||||
stake_pool.stake_withdraw_bump_seed,
|
||||
new_stake_authority_info.key,
|
||||
clock_info.clone(),
|
||||
stake_program_info.clone(),
|
||||
|
@ -1097,7 +1104,7 @@ impl Processor {
|
|||
validator_stake_account_info.clone(),
|
||||
withdraw_authority_info.clone(),
|
||||
AUTHORITY_WITHDRAW,
|
||||
stake_pool.withdraw_bump_seed,
|
||||
stake_pool.stake_withdraw_bump_seed,
|
||||
lamports,
|
||||
transient_stake_account_info.clone(),
|
||||
)?;
|
||||
|
@ -1109,7 +1116,7 @@ impl Processor {
|
|||
withdraw_authority_info.clone(),
|
||||
stake_pool_info.key,
|
||||
AUTHORITY_WITHDRAW,
|
||||
stake_pool.withdraw_bump_seed,
|
||||
stake_pool.stake_withdraw_bump_seed,
|
||||
)?;
|
||||
|
||||
validator_stake_info.active_stake_lamports = validator_stake_info
|
||||
|
@ -1249,7 +1256,7 @@ impl Processor {
|
|||
reserve_stake_account_info.clone(),
|
||||
withdraw_authority_info.clone(),
|
||||
AUTHORITY_WITHDRAW,
|
||||
stake_pool.withdraw_bump_seed,
|
||||
stake_pool.stake_withdraw_bump_seed,
|
||||
total_lamports,
|
||||
transient_stake_account_info.clone(),
|
||||
)?;
|
||||
|
@ -1264,7 +1271,7 @@ impl Processor {
|
|||
withdraw_authority_info.clone(),
|
||||
stake_pool_info.key,
|
||||
AUTHORITY_WITHDRAW,
|
||||
stake_pool.withdraw_bump_seed,
|
||||
stake_pool.stake_withdraw_bump_seed,
|
||||
)?;
|
||||
|
||||
validator_stake_info.transient_stake_lamports = total_lamports;
|
||||
|
@ -1427,7 +1434,7 @@ impl Processor {
|
|||
transient_stake_info.clone(),
|
||||
withdraw_authority_info.clone(),
|
||||
AUTHORITY_WITHDRAW,
|
||||
stake_pool.withdraw_bump_seed,
|
||||
stake_pool.stake_withdraw_bump_seed,
|
||||
reserve_stake_info.clone(),
|
||||
clock_info.clone(),
|
||||
stake_history_info.clone(),
|
||||
|
@ -1453,7 +1460,7 @@ impl Processor {
|
|||
transient_stake_info.clone(),
|
||||
withdraw_authority_info.clone(),
|
||||
AUTHORITY_WITHDRAW,
|
||||
stake_pool.withdraw_bump_seed,
|
||||
stake_pool.stake_withdraw_bump_seed,
|
||||
reserve_stake_info.clone(),
|
||||
clock_info.clone(),
|
||||
stake_history_info.clone(),
|
||||
|
@ -1479,7 +1486,7 @@ impl Processor {
|
|||
transient_stake_info.clone(),
|
||||
withdraw_authority_info.clone(),
|
||||
AUTHORITY_WITHDRAW,
|
||||
stake_pool.withdraw_bump_seed,
|
||||
stake_pool.stake_withdraw_bump_seed,
|
||||
validator_stake_info.clone(),
|
||||
clock_info.clone(),
|
||||
stake_history_info.clone(),
|
||||
|
@ -1494,7 +1501,7 @@ impl Processor {
|
|||
validator_stake_info.clone(),
|
||||
withdraw_authority_info.clone(),
|
||||
AUTHORITY_WITHDRAW,
|
||||
stake_pool.withdraw_bump_seed,
|
||||
stake_pool.stake_withdraw_bump_seed,
|
||||
reserve_stake_info.clone(),
|
||||
clock_info.clone(),
|
||||
stake_history_info.clone(),
|
||||
|
@ -1623,7 +1630,7 @@ impl Processor {
|
|||
|
||||
let reward_lamports = total_stake_lamports.saturating_sub(previous_lamports);
|
||||
let fee = stake_pool
|
||||
.calc_fee_amount(reward_lamports)
|
||||
.calc_epoch_fee_amount(reward_lamports)
|
||||
.ok_or(StakePoolError::CalculationFailure)?;
|
||||
|
||||
if fee > 0 {
|
||||
|
@ -1634,7 +1641,7 @@ impl Processor {
|
|||
manager_fee_info.clone(),
|
||||
withdraw_info.clone(),
|
||||
AUTHORITY_WITHDRAW,
|
||||
stake_pool.withdraw_bump_seed,
|
||||
stake_pool.stake_withdraw_bump_seed,
|
||||
fee,
|
||||
)?;
|
||||
|
||||
|
@ -1718,17 +1725,19 @@ impl Processor {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Processes [Deposit](enum.Instruction.html).
|
||||
fn process_deposit(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult {
|
||||
/// Processes [DepositStake](enum.Instruction.html).
|
||||
fn process_deposit_stake(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult {
|
||||
let account_info_iter = &mut accounts.iter();
|
||||
let stake_pool_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 stake_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 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 clock_info = next_account_info(account_info_iter)?;
|
||||
let clock = &Clock::from_account_info(clock_info)?;
|
||||
|
@ -1753,7 +1762,7 @@ impl Processor {
|
|||
program_id,
|
||||
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_validator_list(validator_list_info)?;
|
||||
stake_pool.check_reserve_stake(reserve_stake_account_info)?;
|
||||
|
@ -1762,6 +1771,10 @@ impl Processor {
|
|||
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 {
|
||||
return Err(StakePoolError::StakeListAndPoolOutOfDate.into());
|
||||
}
|
||||
|
@ -1808,13 +1821,13 @@ impl Processor {
|
|||
|
||||
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);
|
||||
if *deposit_authority_info.key == deposit_authority_program_address {
|
||||
if *stake_deposit_authority_info.key == stake_deposit_authority_program_address {
|
||||
Self::stake_authorize_signed(
|
||||
stake_pool_info.key,
|
||||
stake_info.clone(),
|
||||
deposit_authority_info.clone(),
|
||||
stake_deposit_authority_info.clone(),
|
||||
AUTHORITY_DEPOSIT,
|
||||
deposit_bump_seed,
|
||||
withdraw_authority_info.key,
|
||||
|
@ -1824,7 +1837,7 @@ impl Processor {
|
|||
} else {
|
||||
Self::stake_authorize(
|
||||
stake_info.clone(),
|
||||
deposit_authority_info.clone(),
|
||||
stake_deposit_authority_info.clone(),
|
||||
withdraw_authority_info.key,
|
||||
clock_info.clone(),
|
||||
stake_program_info.clone(),
|
||||
|
@ -1836,7 +1849,7 @@ impl Processor {
|
|||
stake_info.clone(),
|
||||
withdraw_authority_info.clone(),
|
||||
AUTHORITY_WITHDRAW,
|
||||
stake_pool.withdraw_bump_seed,
|
||||
stake_pool.stake_withdraw_bump_seed,
|
||||
validator_stake_account_info.clone(),
|
||||
clock_info.clone(),
|
||||
stake_history_info.clone(),
|
||||
|
@ -1854,24 +1867,71 @@ impl Processor {
|
|||
.stake
|
||||
.checked_sub(validator_stake.delegation.stake)
|
||||
.ok_or(StakePoolError::CalculationFailure)?;
|
||||
|
||||
let new_pool_tokens = stake_pool
|
||||
.calc_pool_tokens_for_deposit(all_deposit_lamports)
|
||||
.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 {
|
||||
return Err(StakePoolError::DepositTooSmall.into());
|
||||
}
|
||||
|
||||
Self::token_mint_to(
|
||||
stake_pool_info.key,
|
||||
token_program_info.clone(),
|
||||
pool_mint_info.clone(),
|
||||
dest_user_info.clone(),
|
||||
withdraw_authority_info.clone(),
|
||||
AUTHORITY_WITHDRAW,
|
||||
stake_pool.withdraw_bump_seed,
|
||||
new_pool_tokens,
|
||||
)?;
|
||||
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,
|
||||
)?;
|
||||
}
|
||||
|
||||
// withdraw additional lamports to the reserve
|
||||
let additional_lamports = all_deposit_lamports
|
||||
|
@ -1883,7 +1943,7 @@ impl Processor {
|
|||
validator_stake_account_info.clone(),
|
||||
withdraw_authority_info.clone(),
|
||||
AUTHORITY_WITHDRAW,
|
||||
stake_pool.withdraw_bump_seed,
|
||||
stake_pool.stake_withdraw_bump_seed,
|
||||
reserve_stake_account_info.clone(),
|
||||
clock_info.clone(),
|
||||
stake_history_info.clone(),
|
||||
|
@ -1911,8 +1971,146 @@ impl Processor {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Processes [Withdraw](enum.Instruction.html).
|
||||
fn process_withdraw(
|
||||
/// Processes [DepositStake](enum.Instruction.html).
|
||||
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,
|
||||
accounts: &[AccountInfo],
|
||||
pool_tokens: u64,
|
||||
|
@ -1925,7 +2123,7 @@ impl Processor {
|
|||
let stake_split_to = 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 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 pool_mint_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());
|
||||
}
|
||||
|
||||
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
|
||||
} else {
|
||||
stake_pool
|
||||
|
@ -2079,7 +2277,7 @@ impl Processor {
|
|||
|
||||
Self::token_burn(
|
||||
token_program_info.clone(),
|
||||
burn_from_info.clone(),
|
||||
burn_from_pool_info.clone(),
|
||||
pool_mint_info.clone(),
|
||||
user_transfer_authority_info.clone(),
|
||||
pool_tokens_burnt,
|
||||
|
@ -2090,7 +2288,7 @@ impl Processor {
|
|||
stake_split_from.clone(),
|
||||
withdraw_authority_info.clone(),
|
||||
AUTHORITY_WITHDRAW,
|
||||
stake_pool.withdraw_bump_seed,
|
||||
stake_pool.stake_withdraw_bump_seed,
|
||||
withdraw_lamports,
|
||||
stake_split_to.clone(),
|
||||
)?;
|
||||
|
@ -2100,7 +2298,7 @@ impl Processor {
|
|||
stake_split_to.clone(),
|
||||
withdraw_authority_info.clone(),
|
||||
AUTHORITY_WITHDRAW,
|
||||
stake_pool.withdraw_bump_seed,
|
||||
stake_pool.stake_withdraw_bump_seed,
|
||||
user_stake_authority_info.key,
|
||||
clock_info.clone(),
|
||||
stake_program_info.clone(),
|
||||
|
@ -2109,7 +2307,7 @@ impl Processor {
|
|||
if pool_tokens_fee > 0 {
|
||||
Self::token_transfer(
|
||||
token_program_info.clone(),
|
||||
burn_from_info.clone(),
|
||||
burn_from_pool_info.clone(),
|
||||
manager_fee_info.clone(),
|
||||
user_transfer_authority_info.clone(),
|
||||
pool_tokens_fee,
|
||||
|
@ -2175,7 +2373,11 @@ impl Processor {
|
|||
}
|
||||
|
||||
/// 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 stake_pool_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)?;
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
// 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());
|
||||
}
|
||||
fee.check_too_high()?;
|
||||
fee.check_withdrawal(&stake_pool.withdrawal_fee)?;
|
||||
|
||||
stake_pool.next_epoch_fee = Some(fee);
|
||||
stake_pool.update_fee(&fee);
|
||||
stake_pool.serialize(&mut *stake_pool_info.data.borrow_mut())?;
|
||||
Ok(())
|
||||
}
|
||||
|
@ -2232,76 +2427,34 @@ impl Processor {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Processes [SetWithdrawalFee](enum.Instruction.html).
|
||||
fn process_set_withdrawal_fee(
|
||||
/// Processes [SetStakeDepositAuthority/SetSolDepositAuthority](enum.Instruction.html).
|
||||
fn process_set_deposit_authority(
|
||||
program_id: &Pubkey,
|
||||
accounts: &[AccountInfo],
|
||||
fee: Fee,
|
||||
deposit_type: DepositType,
|
||||
) -> ProgramResult {
|
||||
let account_info_iter = &mut accounts.iter();
|
||||
let stake_pool_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)?;
|
||||
let mut stake_pool = try_from_slice_unchecked::<StakePool>(&stake_pool_info.data.borrow())?;
|
||||
if !stake_pool.is_valid() {
|
||||
return Err(StakePoolError::InvalidState.into());
|
||||
}
|
||||
|
||||
stake_pool.check_manager(manager_info)?;
|
||||
|
||||
if stake_pool.last_update_epoch < clock.epoch {
|
||||
return Err(StakePoolError::StakeListAndPoolOutOfDate.into());
|
||||
match deposit_type {
|
||||
DepositType::Stake => {
|
||||
stake_pool.stake_deposit_authority = new_sol_deposit_authority.unwrap_or(
|
||||
find_deposit_authority_program_address(program_id, stake_pool_info.key).0,
|
||||
);
|
||||
}
|
||||
DepositType::Sol => stake_pool.sol_deposit_authority = new_sol_deposit_authority,
|
||||
}
|
||||
|
||||
// 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());
|
||||
}
|
||||
|
||||
// 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())?;
|
||||
Ok(())
|
||||
}
|
||||
|
@ -2313,10 +2466,20 @@ impl Processor {
|
|||
StakePoolInstruction::Initialize {
|
||||
fee,
|
||||
withdrawal_fee,
|
||||
deposit_fee,
|
||||
referral_fee,
|
||||
max_validators,
|
||||
} => {
|
||||
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 => {
|
||||
msg!("Instruction: CreateValidatorStakeAccount");
|
||||
|
@ -2370,13 +2533,13 @@ impl Processor {
|
|||
msg!("Instruction: CleanupRemovedValidatorEntries");
|
||||
Self::process_cleanup_removed_validator_entries(program_id, accounts)
|
||||
}
|
||||
StakePoolInstruction::Deposit => {
|
||||
msg!("Instruction: Deposit");
|
||||
Self::process_deposit(program_id, accounts)
|
||||
StakePoolInstruction::DepositStake => {
|
||||
msg!("Instruction: DepositStake");
|
||||
Self::process_deposit_stake(program_id, accounts)
|
||||
}
|
||||
StakePoolInstruction::Withdraw(amount) => {
|
||||
msg!("Instruction: Withdraw");
|
||||
Self::process_withdraw(program_id, accounts, amount)
|
||||
StakePoolInstruction::WithdrawStake(amount) => {
|
||||
msg!("Instruction: WithdrawStake");
|
||||
Self::process_withdraw_stake(program_id, accounts, amount)
|
||||
}
|
||||
StakePoolInstruction::SetManager => {
|
||||
msg!("Instruction: SetManager");
|
||||
|
@ -2390,9 +2553,13 @@ impl Processor {
|
|||
msg!("Instruction: SetStaker");
|
||||
Self::process_set_staker(program_id, accounts)
|
||||
}
|
||||
StakePoolInstruction::SetWithdrawalFee { fee } => {
|
||||
msg!("Instruction: SetWithdrawalFee");
|
||||
Self::process_set_withdrawal_fee(program_id, accounts, fee)
|
||||
StakePoolInstruction::DepositSol(lamports) => {
|
||||
msg!("Instruction: DepositSol");
|
||||
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::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::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
|
||||
|
||||
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},
|
||||
num_derive::FromPrimitive,
|
||||
num_traits::FromPrimitive,
|
||||
|
@ -16,6 +19,7 @@ use {
|
|||
},
|
||||
spl_math::checked_ceil_div::CheckedCeilDiv,
|
||||
std::convert::TryFrom,
|
||||
std::{fmt, matches},
|
||||
};
|
||||
|
||||
/// Enum representing the account type managed by the program
|
||||
|
@ -49,7 +53,7 @@ pub struct StakePool {
|
|||
/// distribution
|
||||
pub staker: Pubkey,
|
||||
|
||||
/// Deposit authority
|
||||
/// Stake deposit authority
|
||||
///
|
||||
/// If a depositor pubkey is specified on initialization, then deposits must be
|
||||
/// 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"],
|
||||
/// 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"])`
|
||||
pub withdraw_bump_seed: u8,
|
||||
pub stake_withdraw_bump_seed: u8,
|
||||
|
||||
/// Validator stake list storage account
|
||||
pub validator_list: Pubkey,
|
||||
|
@ -105,8 +109,8 @@ pub struct StakePool {
|
|||
/// Preferred withdraw validator vote account pubkey
|
||||
pub preferred_withdraw_validator_vote_address: Option<Pubkey>,
|
||||
|
||||
/// Fee assessed on deposits
|
||||
pub deposit_fee: Fee,
|
||||
/// Fee assessed on stake deposits
|
||||
pub stake_deposit_fee: Fee,
|
||||
|
||||
/// Fee assessed on withdrawals
|
||||
pub withdrawal_fee: Fee,
|
||||
|
@ -114,14 +118,28 @@ pub struct StakePool {
|
|||
/// Future withdrawal fee, to be set for the following epoch
|
||||
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.
|
||||
/// i.e. `deposit_fee`% is collected as deposit fees for every deposit
|
||||
/// and `referral_fee`% of the collected deposit fees is paid out to the referrer
|
||||
pub referral_fee: u8,
|
||||
/// i.e. `stake_deposit_fee`% of stake deposited is collected as deposit fees for every deposit
|
||||
/// and `stake_referral_fee`% of the collected stake deposit fees is paid out to the referrer
|
||||
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 {
|
||||
/// 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> {
|
||||
if self.total_stake_lamports == 0 || self.pool_token_supply == 0 {
|
||||
return Some(stake_lamports);
|
||||
|
@ -135,6 +153,7 @@ impl StakePool {
|
|||
}
|
||||
|
||||
/// calculate lamports amount on withdrawal
|
||||
#[inline]
|
||||
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
|
||||
// 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
|
||||
#[inline]
|
||||
pub fn calc_pool_tokens_withdrawal_fee(&self, pool_tokens: u64) -> Option<u64> {
|
||||
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
|
||||
///
|
||||
/// This function assumes that `reward_lamports` has not already been added
|
||||
/// 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 {
|
||||
return Some(0);
|
||||
}
|
||||
|
@ -178,7 +233,7 @@ impl StakePool {
|
|||
}
|
||||
|
||||
/// Checks that the withdraw or deposit authority is valid
|
||||
fn check_authority(
|
||||
fn check_program_derived_authority(
|
||||
authority_address: &Pubkey,
|
||||
program_id: &Pubkey,
|
||||
stake_pool_address: &Pubkey,
|
||||
|
@ -207,33 +262,55 @@ impl StakePool {
|
|||
}
|
||||
|
||||
/// Checks that the withdraw authority is valid
|
||||
#[inline]
|
||||
pub(crate) fn check_authority_withdraw(
|
||||
&self,
|
||||
withdraw_authority: &Pubkey,
|
||||
program_id: &Pubkey,
|
||||
stake_pool_address: &Pubkey,
|
||||
) -> Result<(), ProgramError> {
|
||||
Self::check_authority(
|
||||
Self::check_program_derived_authority(
|
||||
withdraw_authority,
|
||||
program_id,
|
||||
stake_pool_address,
|
||||
crate::AUTHORITY_WITHDRAW,
|
||||
self.withdraw_bump_seed,
|
||||
self.stake_withdraw_bump_seed,
|
||||
)
|
||||
}
|
||||
/// Checks that the deposit authority is valid
|
||||
pub(crate) fn check_deposit_authority(
|
||||
#[inline]
|
||||
pub(crate) fn check_stake_deposit_authority(
|
||||
&self,
|
||||
deposit_authority: &Pubkey,
|
||||
stake_deposit_authority: &Pubkey,
|
||||
) -> Result<(), ProgramError> {
|
||||
if self.deposit_authority == *deposit_authority {
|
||||
if self.stake_deposit_authority == *stake_deposit_authority {
|
||||
Ok(())
|
||||
} 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
|
||||
#[inline]
|
||||
pub(crate) fn check_mint(&self, mint_info: &AccountInfo) -> Result<(), ProgramError> {
|
||||
if *mint_info.key != self.pool_mint {
|
||||
Err(StakePoolError::WrongPoolMint.into())
|
||||
|
@ -319,6 +396,18 @@ impl StakePool {
|
|||
pub fn is_uninitialized(&self) -> bool {
|
||||
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.
|
||||
|
@ -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)]
|
||||
mod test {
|
||||
use {
|
||||
|
@ -779,7 +958,7 @@ mod test {
|
|||
..StakePool::default()
|
||||
};
|
||||
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.pool_token_supply += pool_token_fee;
|
||||
|
@ -815,7 +994,7 @@ mod test {
|
|||
..StakePool::default()
|
||||
};
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -832,7 +1011,7 @@ mod test {
|
|||
fee,
|
||||
..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.pool_token_supply += pool_token_fee;
|
||||
|
|
|
@ -41,7 +41,7 @@ async fn setup() -> (
|
|||
)
|
||||
.await;
|
||||
|
||||
let deposit_info = simple_deposit(
|
||||
let deposit_info = simple_deposit_stake(
|
||||
&mut banks_client,
|
||||
&payer,
|
||||
&recent_blockhash,
|
||||
|
|
|
@ -212,7 +212,9 @@ async fn success() {
|
|||
// Check minted tokens
|
||||
let user_token_balance =
|
||||
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
|
||||
let validator_list = get_account(
|
||||
|
@ -272,12 +274,14 @@ async fn fail_with_wrong_stake_program_id() {
|
|||
let accounts = vec![
|
||||
AccountMeta::new(stake_pool_accounts.stake_pool.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(deposit_stake, false),
|
||||
AccountMeta::new(validator_stake_account.stake_account, false),
|
||||
AccountMeta::new(stake_pool_accounts.reserve_stake.pubkey(), 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_readonly(sysvar::clock::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 {
|
||||
program_id: id(),
|
||||
accounts,
|
||||
data: instruction::StakePoolInstruction::Deposit
|
||||
data: instruction::StakePoolInstruction::DepositStake
|
||||
.try_to_vec()
|
||||
.unwrap(),
|
||||
};
|
||||
|
@ -325,7 +329,7 @@ async fn fail_with_wrong_token_program_id() {
|
|||
let wrong_token_program = Keypair::new();
|
||||
|
||||
let mut transaction = Transaction::new_with_payer(
|
||||
&instruction::deposit(
|
||||
&instruction::deposit_stake(
|
||||
&id(),
|
||||
&stake_pool_accounts.stake_pool.pubkey(),
|
||||
&stake_pool_accounts.validator_list.pubkey(),
|
||||
|
@ -335,6 +339,8 @@ async fn fail_with_wrong_token_program_id() {
|
|||
&validator_stake_account.stake_account,
|
||||
&stake_pool_accounts.reserve_stake.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(),
|
||||
),
|
||||
|
@ -567,7 +573,9 @@ async fn fail_with_wrong_mint_for_receiver_acc() {
|
|||
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 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
|
||||
|
||||
#[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 deposit_authority = Keypair::new();
|
||||
let stake_pool_accounts = StakePoolAccounts::new_with_deposit_authority(deposit_authority);
|
||||
let stake_deposit_authority = Keypair::new();
|
||||
let stake_pool_accounts =
|
||||
StakePoolAccounts::new_with_stake_deposit_authority(stake_deposit_authority);
|
||||
stake_pool_accounts
|
||||
.initialize_stake_pool(&mut banks_client, &payer, &recent_blockhash, 1)
|
||||
.await
|
||||
|
@ -659,10 +668,11 @@ async fn success_with_deposit_authority() {
|
|||
}
|
||||
|
||||
#[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 deposit_authority = Keypair::new();
|
||||
let mut stake_pool_accounts = StakePoolAccounts::new_with_deposit_authority(deposit_authority);
|
||||
let stake_deposit_authority = Keypair::new();
|
||||
let mut stake_pool_accounts =
|
||||
StakePoolAccounts::new_with_stake_deposit_authority(stake_deposit_authority);
|
||||
stake_pool_accounts
|
||||
.initialize_stake_pool(&mut banks_client, &payer, &recent_blockhash, 1)
|
||||
.await
|
||||
|
@ -726,8 +736,8 @@ async fn fail_without_deposit_authority_signature() {
|
|||
.unwrap();
|
||||
|
||||
let wrong_depositor = Keypair::new();
|
||||
stake_pool_accounts.deposit_authority = wrong_depositor.pubkey();
|
||||
stake_pool_accounts.deposit_authority_keypair = Some(wrong_depositor);
|
||||
stake_pool_accounts.stake_deposit_authority = wrong_depositor.pubkey();
|
||||
stake_pool_accounts.stake_deposit_authority_keypair = Some(wrong_depositor);
|
||||
|
||||
let error = stake_pool_accounts
|
||||
.deposit_stake(
|
||||
|
@ -747,7 +757,7 @@ async fn fail_without_deposit_authority_signature() {
|
|||
TransactionError::InstructionError(_, InstructionError::Custom(error_index)) => {
|
||||
assert_eq!(
|
||||
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"),
|
||||
|
@ -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"),
|
||||
}
|
||||
}
|
||||
|
||||
#[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,
|
||||
manager: &Keypair,
|
||||
staker: &Pubkey,
|
||||
deposit_authority: &Option<Keypair>,
|
||||
stake_deposit_authority: &Option<Keypair>,
|
||||
fee: &state::Fee,
|
||||
withdrawal_fee: &state::Fee,
|
||||
deposit_fee: &state::Fee,
|
||||
referral_fee: u8,
|
||||
max_validators: u32,
|
||||
) -> Result<(), TransportError> {
|
||||
let rent = banks_client.get_rent().await.unwrap();
|
||||
|
@ -303,17 +305,19 @@ pub async fn create_stake_pool(
|
|||
pool_mint,
|
||||
pool_token_account,
|
||||
&spl_token::id(),
|
||||
deposit_authority.as_ref().map(|k| k.pubkey()),
|
||||
stake_deposit_authority.as_ref().map(|k| k.pubkey()),
|
||||
*fee,
|
||||
*withdrawal_fee,
|
||||
*deposit_fee,
|
||||
referral_fee,
|
||||
max_validators,
|
||||
),
|
||||
],
|
||||
Some(&payer.pubkey()),
|
||||
);
|
||||
let mut signers = vec![payer, stake_pool, validator_list, manager];
|
||||
if let Some(deposit_authority) = deposit_authority.as_ref() {
|
||||
signers.push(deposit_authority);
|
||||
if let Some(stake_deposit_authority) = stake_deposit_authority.as_ref() {
|
||||
signers.push(stake_deposit_authority);
|
||||
}
|
||||
transaction.sign(&signers, *recent_blockhash);
|
||||
banks_client.process_transaction(transaction).await?;
|
||||
|
@ -542,10 +546,12 @@ pub struct StakePoolAccounts {
|
|||
pub manager: Keypair,
|
||||
pub staker: Keypair,
|
||||
pub withdraw_authority: Pubkey,
|
||||
pub deposit_authority: Pubkey,
|
||||
pub deposit_authority_keypair: Option<Keypair>,
|
||||
pub stake_deposit_authority: Pubkey,
|
||||
pub stake_deposit_authority_keypair: Option<Keypair>,
|
||||
pub fee: state::Fee,
|
||||
pub withdrawal_fee: state::Fee,
|
||||
pub deposit_fee: state::Fee,
|
||||
pub referral_fee: u8,
|
||||
pub max_validators: u32,
|
||||
}
|
||||
|
||||
|
@ -558,7 +564,7 @@ impl StakePoolAccounts {
|
|||
&[&stake_pool_address.to_bytes()[..32], b"withdraw"],
|
||||
&id(),
|
||||
);
|
||||
let (deposit_authority, _) = Pubkey::find_program_address(
|
||||
let (stake_deposit_authority, _) = Pubkey::find_program_address(
|
||||
&[&stake_pool_address.to_bytes()[..32], b"deposit"],
|
||||
&id(),
|
||||
);
|
||||
|
@ -577,8 +583,8 @@ impl StakePoolAccounts {
|
|||
manager,
|
||||
staker,
|
||||
withdraw_authority,
|
||||
deposit_authority,
|
||||
deposit_authority_keypair: None,
|
||||
stake_deposit_authority,
|
||||
stake_deposit_authority_keypair: None,
|
||||
fee: state::Fee {
|
||||
numerator: 1,
|
||||
denominator: 100,
|
||||
|
@ -587,14 +593,19 @@ impl StakePoolAccounts {
|
|||
numerator: 3,
|
||||
denominator: 1000,
|
||||
},
|
||||
deposit_fee: state::Fee {
|
||||
numerator: 1,
|
||||
denominator: 1000,
|
||||
},
|
||||
referral_fee: 25,
|
||||
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();
|
||||
stake_pool_accounts.deposit_authority = deposit_authority.pubkey();
|
||||
stake_pool_accounts.deposit_authority_keypair = Some(deposit_authority);
|
||||
stake_pool_accounts.stake_deposit_authority = stake_deposit_authority.pubkey();
|
||||
stake_pool_accounts.stake_deposit_authority_keypair = Some(stake_deposit_authority);
|
||||
stake_pool_accounts
|
||||
}
|
||||
|
||||
|
@ -606,6 +617,14 @@ impl StakePoolAccounts {
|
|||
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(
|
||||
&self,
|
||||
mut banks_client: &mut BanksClient,
|
||||
|
@ -654,9 +673,11 @@ impl StakePoolAccounts {
|
|||
&self.pool_fee_account.pubkey(),
|
||||
&self.manager,
|
||||
&self.staker.pubkey(),
|
||||
&self.deposit_authority_keypair,
|
||||
&self.stake_deposit_authority_keypair,
|
||||
&self.fee,
|
||||
&self.withdrawal_fee,
|
||||
&self.deposit_fee,
|
||||
self.referral_fee,
|
||||
self.max_validators,
|
||||
)
|
||||
.await?;
|
||||
|
@ -675,36 +696,91 @@ impl StakePoolAccounts {
|
|||
current_staker: &Keypair,
|
||||
) -> Option<TransportError> {
|
||||
let mut signers = vec![payer, current_staker];
|
||||
let instructions = if let Some(deposit_authority) = self.deposit_authority_keypair.as_ref()
|
||||
{
|
||||
signers.push(deposit_authority);
|
||||
instruction::deposit_with_authority(
|
||||
let instructions =
|
||||
if let Some(stake_deposit_authority) = self.stake_deposit_authority_keypair.as_ref() {
|
||||
signers.push(stake_deposit_authority);
|
||||
instruction::deposit_stake_with_authority(
|
||||
&id(),
|
||||
&self.stake_pool.pubkey(),
|
||||
&self.validator_list.pubkey(),
|
||||
&self.stake_deposit_authority,
|
||||
&self.withdraw_authority,
|
||||
stake,
|
||||
¤t_staker.pubkey(),
|
||||
validator_stake_account,
|
||||
&self.reserve_stake.pubkey(),
|
||||
pool_account,
|
||||
&self.pool_fee_account.pubkey(),
|
||||
&self.pool_fee_account.pubkey(),
|
||||
&self.pool_mint.pubkey(),
|
||||
&spl_token::id(),
|
||||
)
|
||||
} else {
|
||||
instruction::deposit_stake(
|
||||
&id(),
|
||||
&self.stake_pool.pubkey(),
|
||||
&self.validator_list.pubkey(),
|
||||
&self.withdraw_authority,
|
||||
stake,
|
||||
¤t_staker.pubkey(),
|
||||
validator_stake_account,
|
||||
&self.reserve_stake.pubkey(),
|
||||
pool_account,
|
||||
&self.pool_fee_account.pubkey(),
|
||||
&self.pool_fee_account.pubkey(),
|
||||
&self.pool_mint.pubkey(),
|
||||
&spl_token::id(),
|
||||
)
|
||||
};
|
||||
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)]
|
||||
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(),
|
||||
&self.validator_list.pubkey(),
|
||||
&self.deposit_authority,
|
||||
&sol_deposit_authority.pubkey(),
|
||||
&self.withdraw_authority,
|
||||
stake,
|
||||
¤t_staker.pubkey(),
|
||||
validator_stake_account,
|
||||
&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(
|
||||
instruction::deposit_sol(
|
||||
&id(),
|
||||
&self.stake_pool.pubkey(),
|
||||
&self.validator_list.pubkey(),
|
||||
&self.withdraw_authority,
|
||||
stake,
|
||||
¤t_staker.pubkey(),
|
||||
validator_stake_account,
|
||||
&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(
|
||||
|
@ -730,7 +806,7 @@ impl StakePoolAccounts {
|
|||
amount: u64,
|
||||
) -> Option<TransportError> {
|
||||
let transaction = Transaction::new_signed_with_payer(
|
||||
&[instruction::withdraw(
|
||||
&[instruction::withdraw_stake(
|
||||
&id(),
|
||||
&self.stake_pool.pubkey(),
|
||||
&self.validator_list.pubkey(),
|
||||
|
@ -1084,7 +1160,7 @@ impl DepositStakeAccount {
|
|||
.await;
|
||||
}
|
||||
|
||||
pub async fn deposit(
|
||||
pub async fn deposit_stake(
|
||||
&mut self,
|
||||
banks_client: &mut BanksClient,
|
||||
payer: &Keypair,
|
||||
|
@ -1119,7 +1195,7 @@ impl DepositStakeAccount {
|
|||
}
|
||||
}
|
||||
|
||||
pub async fn simple_deposit(
|
||||
pub async fn simple_deposit_stake(
|
||||
banks_client: &mut BanksClient,
|
||||
payer: &Keypair,
|
||||
recent_blockhash: &Hash,
|
||||
|
|
|
@ -32,7 +32,7 @@ use {
|
|||
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 STAKE_AMOUNT: u64 = 200_000_000_000;
|
||||
const STAKE_ACCOUNT_RENT_EXEMPTION: u64 = 2_282_880;
|
||||
|
@ -56,15 +56,15 @@ async fn setup(
|
|||
stake_pool_accounts.max_validators = max_validators;
|
||||
|
||||
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);
|
||||
|
||||
let mut stake_pool = StakePool {
|
||||
account_type: AccountType::StakePool,
|
||||
manager: stake_pool_accounts.manager.pubkey(),
|
||||
staker: stake_pool_accounts.staker.pubkey(),
|
||||
deposit_authority: stake_pool_accounts.deposit_authority,
|
||||
withdraw_bump_seed,
|
||||
stake_deposit_authority: stake_pool_accounts.stake_deposit_authority,
|
||||
stake_withdraw_bump_seed,
|
||||
validator_list: stake_pool_accounts.validator_list.pubkey(),
|
||||
reserve_stake: stake_pool_accounts.reserve_stake.pubkey(),
|
||||
pool_mint: stake_pool_accounts.pool_mint.pubkey(),
|
||||
|
@ -78,10 +78,13 @@ async fn setup(
|
|||
next_epoch_fee: None,
|
||||
preferred_deposit_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(),
|
||||
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);
|
||||
|
@ -592,7 +595,7 @@ async fn add_validator_to_pool() {
|
|||
increase_amount,
|
||||
)
|
||||
.await;
|
||||
assert!(error.is_none());
|
||||
assert!(error.is_none(), "{:?}", error);
|
||||
|
||||
let validator_list = get_account(
|
||||
&mut context.banks_client,
|
||||
|
@ -655,7 +658,7 @@ async fn set_preferred() {
|
|||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn deposit() {
|
||||
async fn deposit_stake() {
|
||||
let (mut context, stake_pool_accounts, _, vote_pubkey, user, stake_pubkey, pool_account_pubkey) =
|
||||
setup(HUGE_POOL_SIZE, HUGE_POOL_SIZE, STAKE_AMOUNT).await;
|
||||
|
||||
|
|
|
@ -46,7 +46,7 @@ async fn setup() -> (
|
|||
)
|
||||
.await;
|
||||
|
||||
let _deposit_info = simple_deposit(
|
||||
let _deposit_info = simple_deposit_stake(
|
||||
&mut banks_client,
|
||||
&payer,
|
||||
&recent_blockhash,
|
||||
|
|
|
@ -254,6 +254,8 @@ async fn fail_with_wrong_max_validators() {
|
|||
None,
|
||||
stake_pool_accounts.fee,
|
||||
stake_pool_accounts.withdrawal_fee,
|
||||
stake_pool_accounts.deposit_fee,
|
||||
stake_pool_accounts.referral_fee,
|
||||
stake_pool_accounts.max_validators,
|
||||
),
|
||||
],
|
||||
|
@ -325,6 +327,8 @@ async fn fail_with_wrong_mint_authority() {
|
|||
&None,
|
||||
&stake_pool_accounts.fee,
|
||||
&stake_pool_accounts.withdrawal_fee,
|
||||
&stake_pool_accounts.deposit_fee,
|
||||
stake_pool_accounts.referral_fee,
|
||||
stake_pool_accounts.max_validators,
|
||||
)
|
||||
.await
|
||||
|
@ -411,6 +415,8 @@ async fn fail_with_freeze_authority() {
|
|||
&None,
|
||||
&stake_pool_accounts.fee,
|
||||
&stake_pool_accounts.withdrawal_fee,
|
||||
&stake_pool_accounts.deposit_fee,
|
||||
stake_pool_accounts.referral_fee,
|
||||
stake_pool_accounts.max_validators,
|
||||
)
|
||||
.await
|
||||
|
@ -499,6 +505,8 @@ async fn fail_with_wrong_token_program_id() {
|
|||
None,
|
||||
stake_pool_accounts.fee,
|
||||
stake_pool_accounts.withdrawal_fee,
|
||||
stake_pool_accounts.deposit_fee,
|
||||
stake_pool_accounts.referral_fee,
|
||||
stake_pool_accounts.max_validators,
|
||||
),
|
||||
],
|
||||
|
@ -576,6 +584,8 @@ async fn fail_with_wrong_fee_account() {
|
|||
&None,
|
||||
&stake_pool_accounts.fee,
|
||||
&stake_pool_accounts.withdrawal_fee,
|
||||
&stake_pool_accounts.deposit_fee,
|
||||
stake_pool_accounts.referral_fee,
|
||||
stake_pool_accounts.max_validators,
|
||||
)
|
||||
.await
|
||||
|
@ -665,6 +675,8 @@ async fn fail_with_not_rent_exempt_pool() {
|
|||
None,
|
||||
stake_pool_accounts.fee,
|
||||
stake_pool_accounts.withdrawal_fee,
|
||||
stake_pool_accounts.deposit_fee,
|
||||
stake_pool_accounts.referral_fee,
|
||||
stake_pool_accounts.max_validators,
|
||||
),
|
||||
],
|
||||
|
@ -740,6 +752,8 @@ async fn fail_with_not_rent_exempt_validator_list() {
|
|||
None,
|
||||
stake_pool_accounts.fee,
|
||||
stake_pool_accounts.withdrawal_fee,
|
||||
stake_pool_accounts.deposit_fee,
|
||||
stake_pool_accounts.referral_fee,
|
||||
stake_pool_accounts.max_validators,
|
||||
),
|
||||
],
|
||||
|
@ -792,6 +806,8 @@ async fn fail_without_manager_signature() {
|
|||
let init_data = instruction::StakePoolInstruction::Initialize {
|
||||
fee: stake_pool_accounts.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,
|
||||
};
|
||||
let data = init_data.try_to_vec().unwrap();
|
||||
|
@ -914,6 +930,8 @@ async fn fail_with_pre_minted_pool_tokens() {
|
|||
&None,
|
||||
&stake_pool_accounts.fee,
|
||||
&stake_pool_accounts.withdrawal_fee,
|
||||
&stake_pool_accounts.deposit_fee,
|
||||
stake_pool_accounts.referral_fee,
|
||||
stake_pool_accounts.max_validators,
|
||||
)
|
||||
.await
|
||||
|
@ -976,6 +994,8 @@ async fn fail_with_bad_reserve() {
|
|||
&None,
|
||||
&stake_pool_accounts.fee,
|
||||
&stake_pool_accounts.withdrawal_fee,
|
||||
&stake_pool_accounts.deposit_fee,
|
||||
stake_pool_accounts.referral_fee,
|
||||
stake_pool_accounts.max_validators,
|
||||
)
|
||||
.await
|
||||
|
@ -1022,6 +1042,8 @@ async fn fail_with_bad_reserve() {
|
|||
&None,
|
||||
&stake_pool_accounts.fee,
|
||||
&stake_pool_accounts.withdrawal_fee,
|
||||
&stake_pool_accounts.deposit_fee,
|
||||
stake_pool_accounts.referral_fee,
|
||||
stake_pool_accounts.max_validators,
|
||||
)
|
||||
.await
|
||||
|
@ -1071,6 +1093,8 @@ async fn fail_with_bad_reserve() {
|
|||
&None,
|
||||
&stake_pool_accounts.fee,
|
||||
&stake_pool_accounts.withdrawal_fee,
|
||||
&stake_pool_accounts.deposit_fee,
|
||||
stake_pool_accounts.referral_fee,
|
||||
stake_pool_accounts.max_validators,
|
||||
)
|
||||
.await
|
||||
|
@ -1120,6 +1144,8 @@ async fn fail_with_bad_reserve() {
|
|||
&None,
|
||||
&stake_pool_accounts.fee,
|
||||
&stake_pool_accounts.withdrawal_fee,
|
||||
&stake_pool_accounts.deposit_fee,
|
||||
stake_pool_accounts.referral_fee,
|
||||
stake_pool_accounts.max_validators,
|
||||
)
|
||||
.await
|
||||
|
@ -1138,10 +1164,11 @@ async fn fail_with_bad_reserve() {
|
|||
}
|
||||
|
||||
#[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 deposit_authority = Keypair::new();
|
||||
let stake_pool_accounts = StakePoolAccounts::new_with_deposit_authority(deposit_authority);
|
||||
let stake_deposit_authority = Keypair::new();
|
||||
let stake_pool_accounts =
|
||||
StakePoolAccounts::new_with_stake_deposit_authority(stake_deposit_authority);
|
||||
stake_pool_accounts
|
||||
.initialize_stake_pool(&mut banks_client, &payer, &recent_blockhash, 1)
|
||||
.await
|
||||
|
@ -1153,7 +1180,7 @@ async fn success_with_required_deposit_authority() {
|
|||
let stake_pool =
|
||||
try_from_slice_unchecked::<state::StakePool>(stake_pool_account.data.as_slice()).unwrap();
|
||||
assert_eq!(
|
||||
stake_pool.deposit_authority,
|
||||
stake_pool_accounts.deposit_authority
|
||||
stake_pool.stake_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 transaction = Transaction::new_signed_with_payer(
|
||||
&[instruction::set_fee(
|
||||
&[instruction::set_epoch_fee(
|
||||
&id(),
|
||||
&stake_pool_accounts.stake_pool.pubkey(),
|
||||
&stake_pool_accounts.manager.pubkey(),
|
||||
|
@ -108,7 +108,7 @@ async fn fail_wrong_manager() {
|
|||
|
||||
let wrong_manager = Keypair::new();
|
||||
let transaction = Transaction::new_signed_with_payer(
|
||||
&[instruction::set_fee(
|
||||
&[instruction::set_epoch_fee(
|
||||
&id(),
|
||||
&stake_pool_accounts.stake_pool.pubkey(),
|
||||
&wrong_manager.pubkey(),
|
||||
|
@ -144,7 +144,7 @@ async fn fail_high_fee() {
|
|||
denominator: 10,
|
||||
};
|
||||
let transaction = Transaction::new_signed_with_payer(
|
||||
&[instruction::set_fee(
|
||||
&[instruction::set_epoch_fee(
|
||||
&id(),
|
||||
&stake_pool_accounts.stake_pool.pubkey(),
|
||||
&stake_pool_accounts.manager.pubkey(),
|
||||
|
@ -193,7 +193,7 @@ async fn fail_not_updated() {
|
|||
context.warp_to_slot(50_000).unwrap();
|
||||
|
||||
let transaction = Transaction::new_signed_with_payer(
|
||||
&[instruction::set_fee(
|
||||
&[instruction::set_epoch_fee(
|
||||
&id(),
|
||||
&stake_pool_accounts.stake_pool.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]
|
||||
async fn fail_not_updated() {
|
||||
let mut context = program_test().start_with_context().await;
|
||||
|
|
|
@ -44,7 +44,7 @@ async fn setup() -> (
|
|||
)
|
||||
.await;
|
||||
|
||||
let _deposit_info = simple_deposit(
|
||||
let _deposit_info = simple_deposit_stake(
|
||||
&mut context.banks_client,
|
||||
&context.payer,
|
||||
&context.last_blockhash,
|
||||
|
@ -65,6 +65,12 @@ async fn setup() -> (
|
|||
async fn success() {
|
||||
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(
|
||||
&mut context.banks_client,
|
||||
&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();
|
||||
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,
|
||||
&stake_pool_accounts.pool_fee_account.pubkey(),
|
||||
)
|
||||
|
@ -144,6 +150,7 @@ async fn success() {
|
|||
&stake_pool_accounts.pool_mint.pubkey(),
|
||||
)
|
||||
.await;
|
||||
let actual_fee = post_fee - pre_fee;
|
||||
assert_eq!(pool_token_supply - pre_token_supply, actual_fee);
|
||||
|
||||
let expected_fee_lamports =
|
||||
|
|
|
@ -103,7 +103,7 @@ async fn setup(
|
|||
|
||||
for deposit_account in &mut deposit_accounts {
|
||||
deposit_account
|
||||
.deposit(
|
||||
.deposit_stake(
|
||||
&mut context.banks_client,
|
||||
&context.payer,
|
||||
&context.last_blockhash,
|
||||
|
|
|
@ -434,7 +434,7 @@ async fn success_with_deactivating_transient_stake() {
|
|||
|
||||
let rent = banks_client.get_rent().await.unwrap();
|
||||
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,
|
||||
&payer,
|
||||
&recent_blockhash,
|
||||
|
@ -472,7 +472,7 @@ async fn success_with_deactivating_transient_stake() {
|
|||
assert!(error.is_none());
|
||||
|
||||
// fail deposit
|
||||
let maybe_deposit = simple_deposit(
|
||||
let maybe_deposit = simple_deposit_stake(
|
||||
&mut banks_client,
|
||||
&payer,
|
||||
&recent_blockhash,
|
||||
|
|
|
@ -51,7 +51,7 @@ async fn setup() -> (
|
|||
)
|
||||
.await;
|
||||
|
||||
let deposit_info = simple_deposit(
|
||||
let deposit_info = simple_deposit_stake(
|
||||
&mut banks_client,
|
||||
&payer,
|
||||
&recent_blockhash,
|
||||
|
@ -274,7 +274,7 @@ async fn fail_with_wrong_stake_program() {
|
|||
let instruction = Instruction {
|
||||
program_id: id(),
|
||||
accounts,
|
||||
data: instruction::StakePoolInstruction::Withdraw(tokens_to_burn)
|
||||
data: instruction::StakePoolInstruction::WithdrawStake(tokens_to_burn)
|
||||
.try_to_vec()
|
||||
.unwrap(),
|
||||
};
|
||||
|
@ -361,7 +361,7 @@ async fn fail_with_wrong_token_program_id() {
|
|||
let wrong_token_program = Keypair::new();
|
||||
|
||||
let transaction = Transaction::new_signed_with_payer(
|
||||
&[instruction::withdraw(
|
||||
&[instruction::withdraw_stake(
|
||||
&id(),
|
||||
&stake_pool_accounts.stake_pool.pubkey(),
|
||||
&stake_pool_accounts.validator_list.pubkey(),
|
||||
|
@ -475,6 +475,67 @@ async fn fail_with_unknown_validator() {
|
|||
)
|
||||
.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 transaction_error = stake_pool_accounts
|
||||
.withdraw_stake(
|
||||
|
@ -585,7 +646,7 @@ async fn fail_without_token_approval() {
|
|||
)
|
||||
.await;
|
||||
|
||||
let deposit_info = simple_deposit(
|
||||
let deposit_info = simple_deposit_stake(
|
||||
&mut banks_client,
|
||||
&payer,
|
||||
&recent_blockhash,
|
||||
|
@ -656,7 +717,7 @@ async fn fail_with_low_delegation() {
|
|||
)
|
||||
.await;
|
||||
|
||||
let deposit_info = simple_deposit(
|
||||
let deposit_info = simple_deposit_stake(
|
||||
&mut banks_client,
|
||||
&payer,
|
||||
&recent_blockhash,
|
||||
|
@ -796,7 +857,7 @@ async fn success_with_reserve() {
|
|||
let rent = context.banks_client.get_rent().await.unwrap();
|
||||
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,
|
||||
&context.payer,
|
||||
&context.last_blockhash,
|
||||
|
@ -930,10 +991,12 @@ async fn success_with_reserve() {
|
|||
assert!(error.is_none());
|
||||
|
||||
// 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 =
|
||||
stake_pool_accounts.calculate_withdrawal_fee(deposit_info.pool_tokens);
|
||||
assert_eq!(
|
||||
deposit_info.stake_lamports + stake_rent,
|
||||
deposit_info.stake_lamports + stake_rent - tokens_deposit_fee,
|
||||
deposit_info.pool_tokens,
|
||||
);
|
||||
|
||||
|
@ -954,8 +1017,12 @@ async fn success_with_reserve() {
|
|||
let stake_state =
|
||||
deserialize::<stake_program::StakeState>(&reserve_stake_account.data).unwrap();
|
||||
let meta = stake_state.meta().unwrap();
|
||||
// TODO: these numbers dont add up even with +tokens_deposit_fee
|
||||
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
|
||||
);
|
||||
|
||||
|
@ -964,7 +1031,9 @@ async fn success_with_reserve() {
|
|||
get_account(&mut context.banks_client, &withdraw_destination.pubkey()).await;
|
||||
assert_eq!(
|
||||
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());
|
||||
|
||||
// deposit into preferred, then fail
|
||||
let _preferred_deposit = simple_deposit(
|
||||
let _preferred_deposit = simple_deposit_stake(
|
||||
&mut banks_client,
|
||||
&payer,
|
||||
&recent_blockhash,
|
||||
|
@ -1152,7 +1221,7 @@ async fn success_withdraw_from_transient() {
|
|||
let rent = context.banks_client.get_rent().await.unwrap();
|
||||
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,
|
||||
&context.payer,
|
||||
&context.last_blockhash,
|
||||
|
|
Loading…
Reference in New Issue