From 3af4648e997b6ac0c328b4aa864d2535a76c3584 Mon Sep 17 00:00:00 2001 From: ZiminDmitriy Date: Tue, 8 Feb 2022 01:00:02 +0300 Subject: [PATCH] added command for change stake pool structure --- stake-pool/cli/src/main.rs | 470 +++++++++++++------------- stake-pool/program/src/instruction.rs | 69 ++-- stake-pool/program/src/processor.rs | 161 +++++---- stake-pool/program/src/state.rs | 127 +++++++ 4 files changed, 476 insertions(+), 351 deletions(-) diff --git a/stake-pool/cli/src/main.rs b/stake-pool/cli/src/main.rs index d1c05aad26e..8da136fc483 100755 --- a/stake-pool/cli/src/main.rs +++ b/stake-pool/cli/src/main.rs @@ -39,8 +39,8 @@ use { transaction::Transaction, }, spl_associated_token_account::get_associated_token_address, - spl_stake_pool::state::ValidatorStakeInfo, spl_stake_pool::state::StakeStatus, + spl_stake_pool::state::ValidatorStakeInfo, spl_stake_pool::{ self, find_stake_program_address, find_transient_stake_program_address, find_withdraw_authority_program_address, @@ -56,6 +56,7 @@ use { // use instruction::create_associated_token_account once ATA 1.0.5 is released #[allow(deprecated)] use spl_associated_token_account::create_associated_token_account; +use spl_stake_pool::state::StakePoolChanged; pub(crate) struct Config { rpc_client: RpcClient, @@ -238,9 +239,10 @@ pub struct ValidatorComparableParameters { fn check_validator(validator_comparable_parameters: &ValidatorComparableParameters) -> bool { return validator_comparable_parameters.fee <= VALIDATOR_MAXIMUM_FEE - && validator_comparable_parameters.skipped_slots <= VALIDATOR_MAXIMUM_SKIPPED_SLOTS - && validator_comparable_parameters.apy >= VALIDATOR_MINIMUM_APY - && validator_comparable_parameters.total_active_stake >= VALIDATOR_MINIMUM_TOTAL_ACTIVE_STAKE + && validator_comparable_parameters.skipped_slots <= VALIDATOR_MAXIMUM_SKIPPED_SLOTS + && validator_comparable_parameters.apy >= VALIDATOR_MINIMUM_APY + && validator_comparable_parameters.total_active_stake + >= VALIDATOR_MINIMUM_TOTAL_ACTIVE_STAKE; } // DTO for https://api.stakesolana.app/v1/validators @@ -248,7 +250,7 @@ fn check_validator(validator_comparable_parameters: &ValidatorComparableParamete pub struct ValidatorsApiResponse { data: Vec, #[allow(dead_code)] - meta_data: ValidatorsMetaData + meta_data: ValidatorsMetaData, } // DTO for https://api.stakesolana.app/v1/validators @@ -277,29 +279,34 @@ pub struct ValidatorsData { pub struct ValidatorsMetaData { limit: i64, offset: i64, - total_amount: u64 + total_amount: u64, } fn get_necessary_validators_vote_account_pubkey() -> Result, Error> { - let response = reqwest::blocking::get("https://api.stakesolana.app/v1/validators?sort=stake&desc=true&offset=25&limit=700")?; + let response = reqwest::blocking::get( + "https://api.stakesolana.app/v1/validators?sort=stake&desc=true&offset=25&limit=700", + )?; - let mut validator_api_response = serde_json::from_slice::<'_, ValidatorsApiResponse>(&response.bytes()?[..])?; + let mut validator_api_response = + serde_json::from_slice::<'_, ValidatorsApiResponse>(&response.bytes()?[..])?; let mut result: Vec = vec![]; - validator_api_response.data.sort_by(|a: &'_ ValidatorsData, b: &'_ ValidatorsData| -> Ordering { - if a.apy > b.apy { - return Ordering::Less; - } else { - if a.apy < b.apy { - return Ordering::Greater; + validator_api_response.data.sort_by( + |a: &'_ ValidatorsData, b: &'_ ValidatorsData| -> Ordering { + if a.apy > b.apy { + return Ordering::Less; } else { - return Ordering::Equal; + if a.apy < b.apy { + return Ordering::Greater; + } else { + return Ordering::Equal; + } } - } - }); + }, + ); - for validtor_data in validator_api_response.data.into_iter() { + for validtor_data in validator_api_response.data.into_iter() { if result.len() == VALIDATORS_QUANTITY { return Ok(result); } @@ -308,11 +315,10 @@ fn get_necessary_validators_vote_account_pubkey() -> Result, Error> fee: validtor_data.fee, skipped_slots: validtor_data.skipped_slots, apy: validtor_data.apy, - total_active_stake: validtor_data.total_active_stake + total_active_stake: validtor_data.total_active_stake, }; - if check_validator(&validator_comparable_parameters) - { + if check_validator(&validator_comparable_parameters) { result.push(Pubkey::from_str(validtor_data.vote_pk.as_str())?); } } @@ -730,11 +736,15 @@ fn increase_validator_stake( .find(vote_account) .ok_or("Vote account not found in validator list")?; - let stake_rent = config.rpc_client.get_minimum_balance_for_rent_exemption(std::mem::size_of::())?; - if let None = config.rpc_client + let stake_rent = config + .rpc_client + .get_minimum_balance_for_rent_exemption(std::mem::size_of::())?; + if let None = config + .rpc_client .get_balance(&stake_pool.reserve_stake)? .saturating_sub(stake_rent) - .checked_sub(stake_pool.total_lamports_liquidity) { + .checked_sub(stake_pool.total_lamports_liquidity) + { return Err("The number of sol on the stake pool's reserve account is less than the number of liquidity sol".into()); } @@ -1298,8 +1308,8 @@ fn command_list(config: &Config, stake_pool_address: &Pubkey) -> CommandResult { .collect(); let total_pool_tokens = spl_token::amount_to_ui_amount(stake_pool.pool_token_supply, pool_mint.decimals); - - let total_liquidity_lamports = stake_pool.total_lamports_liquidity; + + let total_liquidity_lamports = stake_pool.total_lamports_liquidity; let mut cli_stake_pool = CliStakePool::from(( *stake_pool_address, @@ -1975,83 +1985,6 @@ fn command_list_all_pools(config: &Config) -> CommandResult { Ok(()) } -fn command_reallocate_stake_pool_account_space( - config: &Config, - stake_pool_address: &Pubkey, - from: &Option, -) -> CommandResult { - todo!(); - - // Check withdraw_from balance - let from_pubkey = from - .as_ref() - .map_or_else(|| config.fee_payer.pubkey(), |keypair| keypair.pubkey()); - let from_balance = config.rpc_client.get_balance(&from_pubkey)?; - - let amount: u64 = 10000000; // TODO - - 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 new_space_size = get_packed_len::(); // TODO подсчитать Солы для рентексемпт - let new_space_size = 760; - - - - - - - - - let stake_pool = get_stake_pool(&config.rpc_client, stake_pool_address)?; - - let mut instructions: Vec = 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.manager.as_ref()]; //solana program deploy /solana-program-library/source/target/deploy/spl_stake_pool.so - if let Some(keypair) = from.as_ref() { - signers.push(keypair) - } - - // // Create the ephemeral SOL account - // instructions.push(system_instruction::transfer( - // &from_pubkey, - // &user_sol_transfer.pubkey(), - // amount, - // )); - - let reallocate_stake_pool_account_space_instruction = spl_stake_pool::instruction::reallocate_stake_pool_account_space( - &spl_stake_pool::id(), - &stake_pool_address, - &config.manager.pubkey(), - &from_pubkey, // &user_sol_transfer.pubkey(), - amount, - new_space_size as u64 - ); - - instructions.push(reallocate_stake_pool_account_space_instruction); - - 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, - fee_calculator.calculate_fee(transaction.message()), - )?; - unique_signers!(signers); - transaction.sign(&signers, recent_blockhash); - send_transaction(config, transaction)?; - Ok(()) -} - fn command_deposit_liquidity_sol( config: &Config, stake_pool_address: &Pubkey, @@ -2091,7 +2024,11 @@ fn command_deposit_liquidity_sol( let mut instructions: Vec = vec![]; let user_sol_transfer = Keypair::new(); - let mut signers = vec![config.fee_payer.as_ref(), &user_sol_transfer, config.manager.as_ref()]; + let mut signers = vec![ + config.fee_payer.as_ref(), + &user_sol_transfer, + config.manager.as_ref(), + ]; if let Some(keypair) = from.as_ref() { signers.push(keypair) } @@ -2147,10 +2084,7 @@ fn command_deposit_liquidity_sol( 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, - fee_calculator.calculate_fee(transaction.message()), - )?; + check_fee_payer_balance(config, fee_calculator.calculate_fee(transaction.message()))?; unique_signers!(signers); transaction.sign(&signers, recent_blockhash); send_transaction(config, transaction)?; @@ -2179,22 +2113,23 @@ fn command_withdraw_liquidity_sol( .into()); } - let stake_rent = config.rpc_client.get_minimum_balance_for_rent_exemption(std::mem::size_of::())?; - if let None = config.rpc_client + let stake_rent = config + .rpc_client + .get_minimum_balance_for_rent_exemption(std::mem::size_of::())?; + if let None = config + .rpc_client .get_balance(&stake_pool.reserve_stake)? .saturating_sub(stake_rent) - .checked_sub(amount) { + .checked_sub(amount) + { return Err(format!( "Not enough balance to withdraw {} SOL. Please, at first restore the sol liquidity balance on the stake pool reserve account.", Sol(amount) ).into()); } - let mut signers = vec![ - config.fee_payer.as_ref(), - config.manager.as_ref() - ]; - + let mut signers = vec![config.fee_payer.as_ref(), config.manager.as_ref()]; + let mut instructions = vec![]; let pool_withdraw_authority = @@ -2269,37 +2204,51 @@ fn command_distribute_stake( return Ok(()); } - let stake_rent = config.rpc_client.get_minimum_balance_for_rent_exemption(std::mem::size_of::())?; - if let None = config.rpc_client + let stake_rent = config + .rpc_client + .get_minimum_balance_for_rent_exemption(std::mem::size_of::())?; + if let None = config + .rpc_client .get_balance(&stake_pool.reserve_stake)? .saturating_sub(stake_rent) - .checked_sub(stake_pool.total_lamports_liquidity) { + .checked_sub(stake_pool.total_lamports_liquidity) + { return Err("The number of sol on the stake pool's reserve account is less than the number of liquidity sol".into()); } - // TODO DELEYE - println!("can distrivute: {}", config.rpc_client // TODO DELEYE - .get_balance(&stake_pool.reserve_stake)? - .saturating_sub(stake_rent)); + // TODO DELEYE + println!( + "can distrivute: {}", + config + .rpc_client // TODO DELEYE + .get_balance(&stake_pool.reserve_stake)? + .saturating_sub(stake_rent) + ); - // TODO Score по API + // TODO Score по API let validators_quantity = validator_list.validators.len() as u64; - let amount = config.rpc_client // TODO считать аккуратно // MINIMUM_ACTIVE_STAKE нельзя класть меньше этого значения // Сюда еще идет +RentExcempt!! + let amount = config + .rpc_client // TODO считать аккуратно // MINIMUM_ACTIVE_STAKE нельзя класть меньше этого значения // Сюда еще идет +RentExcempt!! .get_balance(&stake_pool.reserve_stake)? .saturating_sub(stake_rent) .saturating_sub(stake_pool.total_lamports_liquidity) - .checked_div(validators_quantity).unwrap(); + .checked_div(validators_quantity) + .unwrap(); for validator_stake_info in validator_list.validators.into_iter() { if validator_stake_info.last_update_epoch == epoch && validator_stake_info.status == StakeStatus::Active - && validator_stake_info.transient_stake_lamports == 0 { - increase_validator_stake( - config, stake_pool_address, &validator_stake_info.vote_account_address, amount - )?; - } + && validator_stake_info.transient_stake_lamports == 0 + { + increase_validator_stake( + config, + stake_pool_address, + &validator_stake_info.vote_account_address, + amount, + )?; + } } todo!(); @@ -2307,13 +2256,11 @@ fn command_distribute_stake( Ok(()) } -fn command_change_validators( - config: &Config, - stake_pool_address: &Pubkey, -) -> CommandResult { +fn command_change_validators(config: &Config, stake_pool_address: &Pubkey) -> CommandResult { let stake_pool = get_stake_pool(&config.rpc_client, stake_pool_address)?; let new_validators_vote_accounts = get_necessary_validators_vote_account_pubkey()?; - let old_validators_vote_accounts = get_existing_validators_vote_account_pubkey(config, &stake_pool.validator_list)?; + let old_validators_vote_accounts = + get_existing_validators_vote_account_pubkey(config, &stake_pool.validator_list)?; let mut validators_to_be_added: Vec<&Pubkey> = vec![]; let mut validators_to_be_removed: Vec<&Pubkey> = vec![]; @@ -2353,9 +2300,9 @@ fn command_withdraw_stake_for_subsequent_removing_validator( ) -> CommandResult { // Simulate result: Response { context: RpcResponseContext { slot: 118734932 }, value: RpcSimulateTransactionResult { // err: Some(InstructionError(0, AccountNotRentExempt)), logs: Some([“Program EverSFw9uN5t1V8kS3ficHUcKffSjwpGzUSGd7mgmSks invoke [1]“, - // “Program log: Instruction: DecreaseValidatorStake”, “Program log: Need more than 2282880 lamports for transient stake to be rent-exempt, - // 4607 provided”, “Program log: Error: AccountNotRentExempt”, “Program EverSFw9uN5t1V8kS3ficHUcKffSjwpGzUSGd7mgmSks consumed 16077 of 200000 - // compute units”, “Program EverSFw9uN5t1V8kS3ficHUcKffSjwpGzUSGd7mgmSks failed: An account does not have enough lamports to be rent-exempt”]), + // “Program log: Instruction: DecreaseValidatorStake”, “Program log: Need more than 2282880 lamports for transient stake to be rent-exempt, + // 4607 provided”, “Program log: Error: AccountNotRentExempt”, “Program EverSFw9uN5t1V8kS3ficHUcKffSjwpGzUSGd7mgmSks consumed 16077 of 200000 + // compute units”, “Program EverSFw9uN5t1V8kS3ficHUcKffSjwpGzUSGd7mgmSks failed: An account does not have enough lamports to be rent-exempt”]), // accounts: None, units_consumed: None } } let stake_pool = get_stake_pool(&config.rpc_client, stake_pool_address)?; @@ -2365,7 +2312,12 @@ fn command_withdraw_stake_for_subsequent_removing_validator( .find(vote_account) .ok_or("Vote account not found in validator list")?; - decrease_validator_stake(config, stake_pool_address, vote_account, validator_stake_info.active_stake_lamports) + decrease_validator_stake( + config, + stake_pool_address, + vote_account, + validator_stake_info.active_stake_lamports, + ) } fn command_check_accounts_for_rent_exempt( @@ -2374,44 +2326,104 @@ fn command_check_accounts_for_rent_exempt( ) -> CommandResult { let stake_pool = get_stake_pool(&config.rpc_client, stake_pool_address)?; - if config.rpc_client.get_balance(stake_pool_address)? < - config.rpc_client.get_minimum_balance_for_rent_exemption(config.rpc_client.get_account_data(stake_pool_address)?.len())? { - println!("Stake pool account with address {} is not rent-exempt", stake_pool_address.to_string()); + if config.rpc_client.get_balance(stake_pool_address)? + < config.rpc_client.get_minimum_balance_for_rent_exemption( + config + .rpc_client + .get_account_data(stake_pool_address)? + .len(), + )? + { + println!( + "Stake pool account with address {} is not rent-exempt", + stake_pool_address.to_string() + ); } else { println!("Stake pool account is rent-exempt"); } - if config.rpc_client.get_balance(&stake_pool.reserve_stake)? < - config.rpc_client.get_minimum_balance_for_rent_exemption(config.rpc_client.get_account_data(&stake_pool.reserve_stake)?.len())? { - println!("Reserve stake account with address {} is not rent-exempt", &stake_pool.reserve_stake.to_string()); + if config.rpc_client.get_balance(&stake_pool.reserve_stake)? + < config.rpc_client.get_minimum_balance_for_rent_exemption( + config + .rpc_client + .get_account_data(&stake_pool.reserve_stake)? + .len(), + )? + { + println!( + "Reserve stake account with address {} is not rent-exempt", + &stake_pool.reserve_stake.to_string() + ); } else { println!("Reserve stake account is rent-exempt"); } - if config.rpc_client.get_balance(&stake_pool.pool_mint)? < - config.rpc_client.get_minimum_balance_for_rent_exemption(config.rpc_client.get_account_data(&stake_pool.pool_mint)?.len())? { - println!("Pool mint account account with address {} is not rent-exempt", &stake_pool.pool_mint.to_string()); + if config.rpc_client.get_balance(&stake_pool.pool_mint)? + < config.rpc_client.get_minimum_balance_for_rent_exemption( + config + .rpc_client + .get_account_data(&stake_pool.pool_mint)? + .len(), + )? + { + println!( + "Pool mint account account with address {} is not rent-exempt", + &stake_pool.pool_mint.to_string() + ); } else { println!("Pool mint account is rent-exempt"); } - if config.rpc_client.get_balance(&stake_pool.treasury_fee_account)? < - config.rpc_client.get_minimum_balance_for_rent_exemption(config.rpc_client.get_account_data(&stake_pool.treasury_fee_account)?.len())? { - println!("Treasury fee account account with address {} is not rent-exempt", &stake_pool.treasury_fee_account.to_string()); + if config + .rpc_client + .get_balance(&stake_pool.treasury_fee_account)? + < config.rpc_client.get_minimum_balance_for_rent_exemption( + config + .rpc_client + .get_account_data(&stake_pool.treasury_fee_account)? + .len(), + )? + { + println!( + "Treasury fee account account with address {} is not rent-exempt", + &stake_pool.treasury_fee_account.to_string() + ); } else { println!("Treasury fee account is rent-exempt"); } - if config.rpc_client.get_balance(&stake_pool.validator_fee_account)? < - config.rpc_client.get_minimum_balance_for_rent_exemption(config.rpc_client.get_account_data(&stake_pool.validator_fee_account)?.len())? { - println!("Validator`s fee account account with address {} is not rent-exempt", &stake_pool.validator_fee_account.to_string()); + if config + .rpc_client + .get_balance(&stake_pool.validator_fee_account)? + < config.rpc_client.get_minimum_balance_for_rent_exemption( + config + .rpc_client + .get_account_data(&stake_pool.validator_fee_account)? + .len(), + )? + { + println!( + "Validator`s fee account account with address {} is not rent-exempt", + &stake_pool.validator_fee_account.to_string() + ); } else { println!("Validator`s fee account is rent-exempt"); } - if config.rpc_client.get_balance(&stake_pool.manager_fee_account)? < - config.rpc_client.get_minimum_balance_for_rent_exemption(config.rpc_client.get_account_data(&stake_pool.manager_fee_account)?.len())? { - println!("Manager fee account account with address {} is not rent-exempt", &stake_pool.manager_fee_account.to_string()); + if config + .rpc_client + .get_balance(&stake_pool.manager_fee_account)? + < config.rpc_client.get_minimum_balance_for_rent_exemption( + config + .rpc_client + .get_account_data(&stake_pool.manager_fee_account)? + .len(), + )? + { + println!( + "Manager fee account account with address {} is not rent-exempt", + &stake_pool.manager_fee_account.to_string() + ); } else { println!("Manager fee account is rent-exempt"); } @@ -2424,7 +2436,7 @@ fn command_check_accounts_for_rent_exempt( pub struct PoolValidatorsApiResponse { data: Vec, #[allow(dead_code)] - meta_data: PoolValidatorsMetaData + meta_data: PoolValidatorsMetaData, } // DTO for https://api.stakesolana.app/v1/pool-validators/{pname} @@ -2455,35 +2467,48 @@ pub struct PoolValidatorsData { pub struct PoolValidatorsMetaData { limit: i64, offset: i64, - total_amount: u64 + total_amount: u64, } fn command_check_existing_validators() -> CommandResult { - let response = reqwest::blocking::get("https://api.stakesolana.app/v1/pool-validators/EverSOL?offset=0&limit=50")?; + let response = reqwest::blocking::get( + "https://api.stakesolana.app/v1/pool-validators/EverSOL?offset=0&limit=50", + )?; - let pool_validator_api_response = serde_json::from_slice::<'_, PoolValidatorsApiResponse>(&response.bytes()?[..])?; + let pool_validator_api_response = + serde_json::from_slice::<'_, PoolValidatorsApiResponse>(&response.bytes()?[..])?; let mut invalid_validators: Vec<(Pubkey, ValidatorComparableParameters)> = vec![]; - for pool_validtor_data in pool_validator_api_response.data.into_iter() { + for pool_validtor_data in pool_validator_api_response.data.into_iter() { let validator_comparable_parameters = ValidatorComparableParameters { fee: pool_validtor_data.fee, skipped_slots: pool_validtor_data.skipped_slots, apy: pool_validtor_data.apy, - total_active_stake: pool_validtor_data.total_active_stake + total_active_stake: pool_validtor_data.total_active_stake, }; - if !check_validator(&validator_comparable_parameters) - { - invalid_validators.push((Pubkey::from_str(pool_validtor_data.vote_pk.as_str())?, validator_comparable_parameters)); + if !check_validator(&validator_comparable_parameters) { + invalid_validators.push(( + Pubkey::from_str(pool_validtor_data.vote_pk.as_str())?, + validator_comparable_parameters, + )); } } if !invalid_validators.is_empty() { - let mut message: String = "".to_string(); + let mut message: String = "Invalid validators: \n".to_string(); - for (vote_account_pubkey, validator_comparable_parameters) in invalid_validators.into_iter() { - message = message + format!("{} : {:?}", vote_account_pubkey.to_string(), validator_comparable_parameters).as_str() + "\n"; + for (vote_account_pubkey, validator_comparable_parameters) in invalid_validators.into_iter() + { + message = message + + format!( + "{} : {:?}", + vote_account_pubkey.to_string(), + validator_comparable_parameters + ) + .as_str() + + "\n"; } return Err(message.into()); @@ -2492,6 +2517,32 @@ fn command_check_existing_validators() -> CommandResult { Ok(()) } +fn command_change_structure(config: &Config, stake_pool_address: &Pubkey) -> CommandResult { + if !config.no_update { + command_update(config, stake_pool_address, false, false)?; + } + + let stake_pool = get_stake_pool(&config.rpc_client, stake_pool_address)?; + + let mut signers = vec![config.fee_payer.as_ref(), config.manager.as_ref()]; + + let instructions = vec![spl_stake_pool::instruction::change_structure( + &spl_stake_pool::id(), + stake_pool_address, + &config.manager.pubkey(), + )]; + + 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, fee_calculator.calculate_fee(transaction.message()))?; + unique_signers!(signers); + transaction.sign(&signers, recent_blockhash); + send_transaction(config, transaction)?; + Ok(()) +} + fn main() { solana_logger::setup_with_default("solana=info"); @@ -3357,26 +3408,6 @@ fn main() { .subcommand(SubCommand::with_name("list-all") .about("List information about all stake pools") ) - .subcommand(SubCommand::with_name("reallocate-stake-pool-account-space") - .about("Reallocate space for stake pool account") - .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("from") - .long("from") - .validator(is_valid_signer) - .value_name("KEYPAIR") - .takes_value(true) - .help("Source account of funds. [default: cli config keypair]"), - ) - ) .subcommand(SubCommand::with_name("deposit-liquidity-sol") .about("Deposit SOL into the stake pool liquidity") .arg( @@ -3501,6 +3532,18 @@ fn main() { .subcommand(SubCommand::with_name("check-existing-validators") .about("Check existing in stake pool validator`s list validators") ) + .subcommand(SubCommand::with_name("change-structure") + .about("Change old StakePool for new") + .arg( + Arg::with_name("pool") + .index(1) + .validator(is_pubkey) + .value_name("POOL_ADDRESS") + .takes_value(true) + .required(true) + .help("Stake pool address."), + ) + ) .get_matches(); let mut wallet_manager = None; @@ -3886,25 +3929,11 @@ fn main() { &referrer, ) } - ("reallocate-stake-pool-account-space", Some(arg_matches)) => { - let stake_pool_address = pubkey_of(arg_matches, "pool").unwrap(); - let from = keypair_of(arg_matches, "from"); - command_reallocate_stake_pool_account_space( - &config, - &stake_pool_address, - &from, - ) - } ("deposit-liquidity-sol", Some(arg_matches)) => { let stake_pool_address = pubkey_of(arg_matches, "pool").unwrap(); let from = keypair_of(arg_matches, "from"); let amount = value_t_or_exit!(arg_matches, "amount", f64); - command_deposit_liquidity_sol( - &config, - &stake_pool_address, - &from, - amount, - ) + command_deposit_liquidity_sol(&config, &stake_pool_address, &from, amount) } ("withdraw-liquidity-sol", Some(arg_matches)) => { let stake_pool_address = pubkey_of(arg_matches, "pool").unwrap(); @@ -3919,29 +3948,17 @@ fn main() { }, ) .pubkey(); - command_withdraw_liquidity_sol( - &config, - &stake_pool_address, - &sol_receiver, - amount, - ) + command_withdraw_liquidity_sol(&config, &stake_pool_address, &sol_receiver, amount) } ("distribute-stake", Some(arg_matches)) => { let stake_pool_address = pubkey_of(arg_matches, "pool").unwrap(); let only_from_reserve = arg_matches.is_present("only-from-reserve"); - command_distribute_stake( - &config, - &stake_pool_address, - only_from_reserve, - ) + command_distribute_stake(&config, &stake_pool_address, only_from_reserve) } ("change-validators", Some(arg_matches)) => { let stake_pool_address = pubkey_of(arg_matches, "pool").unwrap(); - command_change_validators( - &config, - &stake_pool_address, - ) + command_change_validators(&config, &stake_pool_address) } ("withdraw-stake-for-subsequent-removing-validator", Some(arg_matches)) => { let stake_pool_address = pubkey_of(arg_matches, "pool").unwrap(); @@ -3954,13 +3971,12 @@ fn main() { } ("check-accounts-for-rent-exempt", Some(arg_matches)) => { let stake_pool_address = pubkey_of(arg_matches, "pool").unwrap(); - command_check_accounts_for_rent_exempt( - &config, - &stake_pool_address, - ) + command_check_accounts_for_rent_exempt(&config, &stake_pool_address) } - ("check-existing-validators", Some(_arg_matches)) => { - command_check_existing_validators() + ("check-existing-validators", Some(_arg_matches)) => command_check_existing_validators(), + ("change-structure", Some(arg_matches)) => { + let stake_pool_address = pubkey_of(arg_matches, "pool").unwrap(); + command_change_structure(&config, &stake_pool_address) } _ => unreachable!(), } @@ -3968,4 +3984,4 @@ fn main() { eprintln!("{}", err); exit(1); }); -} \ No newline at end of file +} diff --git a/stake-pool/program/src/instruction.rs b/stake-pool/program/src/instruction.rs index 70bb92bc24c..575b48be572 100755 --- a/stake-pool/program/src/instruction.rs +++ b/stake-pool/program/src/instruction.rs @@ -383,20 +383,6 @@ pub enum StakePoolInstruction { /// 11. `[]` Token program id /// 12. `[s]` (Optional) Stake pool sol withdraw authority WithdrawSol(u64), - - /// Reallocate space in stake pool account - /// - /// 0. `[w]` Stake pool - /// 1. `[s]` Manager - /// 2. `[s]` Account providing the lamports to be deposited into the pool - /// 3. `[]` System program account - /// 4. `[]` System program account - ReallocateStakePoolAccountSpace { - /// Number of lamports to transfer to the new account - lamports: u64, - /// Number of bytes of memory to allocate - space: u64, - }, /// Deposit SOL directly into the pool's reserve account to increase liquidity /// @@ -421,6 +407,15 @@ pub enum StakePoolInstruction { /// 7. `[]` Stake program account /// 8. `[s]` (Optional) Stake pool sol withdraw authority WithdrawLiquiditySol(u64), + + + + + /// DELETE AFTER EXECUTION + /// + /// 0. `[w]` Stake pool + /// 1. `[s]` Manager + ChangeStructure, } /// Creates an 'initialize' instruction. @@ -1333,33 +1328,6 @@ pub fn set_funding_authority( } } -/// Creates instruction required to reallocate space in stake pool account -pub fn reallocate_stake_pool_account_space( - program_id: &Pubkey, - stake_pool: &Pubkey, - manager: &Pubkey, - lamports_from: &Pubkey, - lamports: u64, - space: u64 -) -> Instruction { - let accounts = vec![ - AccountMeta::new(*stake_pool, false), - AccountMeta::new_readonly(*manager, true), - AccountMeta::new(*lamports_from, true), - AccountMeta::new_readonly(*program_id, false), - ]; - Instruction { - program_id: *program_id, - accounts, - data: StakePoolInstruction::ReallocateStakePoolAccountSpace { - lamports, - space, - } - .try_to_vec() - .unwrap(), - } -} - /// Creates instructions required to deposit SOL directly into a stake pool liquidity. pub fn deposit_liquidity_sol( program_id: &Pubkey, @@ -1478,4 +1446,23 @@ pub fn withdraw_liquidity_sol_with_authority( .try_to_vec() .unwrap(), } +} + +/// DELETE +pub fn change_structure( + program_id: &Pubkey, + stake_pool: &Pubkey, + manager: &Pubkey, +) -> Instruction { + let accounts = vec![ + AccountMeta::new(*stake_pool, false), + AccountMeta::new_readonly(*manager, true), + ]; + Instruction { + program_id: *program_id, + accounts, + data: StakePoolInstruction::ChangeStructure + .try_to_vec() + .unwrap(), + } } \ No newline at end of file diff --git a/stake-pool/program/src/processor.rs b/stake-pool/program/src/processor.rs index 92da7455196..3ec2b2d45aa 100755 --- a/stake-pool/program/src/processor.rs +++ b/stake-pool/program/src/processor.rs @@ -7,7 +7,7 @@ use { instruction::{FundingType, PreferredValidatorType, StakePoolInstruction}, minimum_reserve_lamports, minimum_stake_lamports, state::{ - AccountType, Fee, FeeType, RateOfExchange, StakePool, StakeStatus, ValidatorList, + AccountType, Fee, FeeType, RateOfExchange, StakePool, StakePoolChanged, StakeStatus, ValidatorList, ValidatorListHeader, ValidatorStakeInfo, }, AUTHORITY_DEPOSIT, AUTHORITY_WITHDRAW, MINIMUM_ACTIVE_STAKE, TRANSIENT_STAKE_SEED_PREFIX, @@ -18,6 +18,7 @@ use { account_info::next_account_info, account_info::AccountInfo, borsh::try_from_slice_unchecked, + borsh::get_instance_packed_len, clock::{Clock, Epoch}, decode_error::DecodeError, entrypoint::ProgramResult, @@ -2801,81 +2802,6 @@ impl Processor { Ok(()) } - /// Processes [ReallocateStakePoolAccountSpace](enum.Instruction.html). - #[inline(never)] // needed to avoid stack size violation - fn process_reallocate_stake_pool_account_space( - program_id: &Pubkey, - accounts: &[AccountInfo], - lamports: u64, - new_space: u64, - ) -> ProgramResult { // TODO Проверка, что новы спейс точно больше предыдущег и на максимальное значение - 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 from_user_lamports_info = next_account_info(account_info_iter)?; - let system_program_info = next_account_info(account_info_iter)?; - - - msg!("QQQQQQQQQQQQQQQQQQQQQ"); - msg!("{:?}", system_program_info.key); - msg!("{:?}", stake_pool_info.owner); - msg!("QQQQQQQQQQQQQQQQQQQQQ"); - - - msg!("{:?}", stake_pool_info.data_len()); - stake_pool_info.realloc(new_space as usize, false)?; - msg!("{:?}", stake_pool_info.data_len()); - - - - - // check_account_owner(stake_pool_info, program_id)?; - // let mut stake_pool = try_from_slice_unchecked::(&stake_pool_info.data.borrow())?; - // if !stake_pool.is_valid() { - // return Err(StakePoolError::InvalidState.into()); - // } - - // if stake_pool.last_update_epoch < Clock::get()?.epoch { - // return Err(StakePoolError::StakeListAndPoolOutOfDate.into()); - // } - - // stake_pool.check_authority_withdraw( - // withdraw_authority_info.key, - // program_id, - // stake_pool_info.key, - // )?; - // stake_pool.check_sol_deposit_authority(sol_deposit_authority_info)?; - // stake_pool.check_reserve_stake(reserve_stake_account_info)?; - - // stake_pool.check_manager(manager_info)?; - - // check_system_program(system_program_info.key)?; - - // if deposit_lamports < MINIMUM_DEPOSIT { - // return Err(StakePoolError::DepositTooSmall.into()); - // } - - // Self::sol_transfer( - // from_user_lamports_info.clone(), - // stake_pool_info.clone(), - // system_program_info.clone(), - // lamports, - // )?; - - // stake_pool.total_lamports = stake_pool - // .total_lamports - // .checked_add(deposit_lamports) - // .ok_or(StakePoolError::CalculationFailure)?; - // stake_pool.total_lamports_liquidity = stake_pool - // .total_lamports_liquidity - // .checked_add(deposit_lamports) - // .ok_or(StakePoolError::CalculationFailure)?; - - // stake_pool.serialize(&mut *stake_pool_info.data.borrow_mut())?; - - Ok(()) - } - /// Processes [DepositLiquiditySol](enum.Instruction.html). #[inline(never)] // needed to avoid stack size violation fn process_deposit_liquidity_sol( @@ -3007,6 +2933,78 @@ impl Processor { Ok(()) } + /// DELETE + #[inline(never)] // needed to avoid stack size violation + fn process_change_structure( + program_id: &Pubkey, + accounts: &[AccountInfo], + ) -> 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)?; + + check_account_owner(stake_pool_info, program_id)?; + let stake_pool = try_from_slice_unchecked::(&stake_pool_info.data.borrow())?; + if !stake_pool.is_valid() { + return Err(StakePoolError::InvalidState.into()); + } + + if stake_pool.last_update_epoch < Clock::get()?.epoch { + return Err(StakePoolError::StakeListAndPoolOutOfDate.into()); + } + + stake_pool.check_manager(manager_info)?; + + let stake_pool_instance_length = get_instance_packed_len(&stake_pool)?; + + let stake_pool_changed = StakePoolChanged { + account_type: stake_pool.account_type, + manager: stake_pool.manager, + staker: stake_pool.staker, + stake_deposit_authority: stake_pool.stake_deposit_authority, + stake_withdraw_bump_seed: stake_pool.stake_withdraw_bump_seed, + validator_list: stake_pool.validator_list, + reserve_stake: stake_pool.reserve_stake, + pool_mint: stake_pool.pool_mint, + manager_fee_account: stake_pool.manager_fee_account, + token_program_id: stake_pool.token_program_id, + total_lamports: stake_pool.total_lamports, + pool_token_supply: stake_pool.pool_token_supply, + last_update_epoch: stake_pool.last_update_epoch, + lockup: stake_pool.lockup, + epoch_fee: stake_pool.epoch_fee, + next_epoch_fee: stake_pool.next_epoch_fee, + preferred_deposit_validator_vote_address: stake_pool.preferred_deposit_validator_vote_address, + preferred_withdraw_validator_vote_address: stake_pool.preferred_withdraw_validator_vote_address, + stake_deposit_fee: stake_pool.stake_deposit_fee, + stake_withdrawal_fee: stake_pool.stake_withdrawal_fee, + next_stake_withdrawal_fee: stake_pool.next_stake_withdrawal_fee, + stake_referral_fee: stake_pool.stake_referral_fee, + sol_deposit_authority: stake_pool.sol_deposit_authority, + sol_deposit_fee: stake_pool.sol_deposit_fee, + sol_referral_fee: stake_pool.sol_referral_fee, + sol_withdraw_authority: stake_pool.sol_withdraw_authority, + sol_withdrawal_fee: stake_pool.sol_withdrawal_fee, + next_sol_withdrawal_fee: stake_pool.next_sol_withdrawal_fee, + last_epoch_pool_token_supply: stake_pool.last_epoch_pool_token_supply, + last_epoch_total_lamports: stake_pool.last_epoch_total_lamports, + rate_of_exchange: stake_pool.rate_of_exchange, + treasury_fee_account: stake_pool.treasury_fee_account, + treasury_fee: stake_pool.treasury_fee, + total_lamports_liquidity: 0 + }; + + if stake_pool_instance_length < get_instance_packed_len(&stake_pool_changed)? { + msg!("New structure size is bigger"); + + return Err(StakePoolError::InvalidState.into()); + } + + stake_pool_changed.serialize(&mut *stake_pool_info.data.borrow_mut())?; + + Ok(()) + } + /// Processes [Instruction](enum.Instruction.html). pub fn process(program_id: &Pubkey, accounts: &[AccountInfo], input: &[u8]) -> ProgramResult { let instruction = StakePoolInstruction::try_from_slice(input)?; @@ -3129,13 +3127,6 @@ impl Processor { msg!("Instruction: WithdrawSol"); Self::process_withdraw_sol(program_id, accounts, pool_tokens) } - StakePoolInstruction::ReallocateStakePoolAccountSpace { - lamports, - space, - } => { - msg!("Instruction: ReallocateStakePoolAccountSpace"); - Self::process_reallocate_stake_pool_account_space(program_id, accounts, lamports, space) - } StakePoolInstruction::DepositLiquiditySol(lamports) => { msg!("Instruction: DepositLiquiditySol"); Self::process_deposit_liquidity_sol(program_id, accounts, lamports) @@ -3144,6 +3135,10 @@ impl Processor { msg!("Instruction: WithdrawLiquiditySol"); Self::process_withdraw_liquidity_sol(program_id, accounts, lamports) } + StakePoolInstruction::ChangeStructure => { + msg!("Instruction: ChnageStructure"); + Self::process_change_structure(program_id, accounts) + } } } } diff --git a/stake-pool/program/src/state.rs b/stake-pool/program/src/state.rs index 11402f75403..5fda39f7175 100755 --- a/stake-pool/program/src/state.rs +++ b/stake-pool/program/src/state.rs @@ -39,6 +39,133 @@ impl Default for AccountType { } } +/// Initialized program details. +#[repr(C)] +#[derive(Clone, Debug, Default, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] +pub struct StakePoolChanged { + /// Account type, must be StakePool currently + pub account_type: AccountType, + + /// Manager authority, allows for updating the staker, manager, and fee account + pub manager: Pubkey, + + /// Staker authority, allows for adding and removing validators, and managing stake + /// distribution + pub staker: Pubkey, + + /// 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, + /// then the stake pool will default to the result of: + /// `Pubkey::find_program_address( + /// &[&stake_pool_address.to_bytes()[..32], b"deposit"], + /// program_id, + /// )` + pub stake_deposit_authority: Pubkey, + + /// Stake withdrawal authority bump seed + /// for `create_program_address(&[state::StakePool account, "withdrawal"])` + pub stake_withdraw_bump_seed: u8, + + /// Validator stake list storage account + pub validator_list: Pubkey, + + /// Reserve stake account, holds deactivated stake + pub reserve_stake: Pubkey, + + /// Pool Mint + pub pool_mint: Pubkey, + + /// Manager fee account + pub manager_fee_account: Pubkey, + + /// Pool token program id + pub token_program_id: Pubkey, + + /// Total stake under management. + /// Note that if `last_update_epoch` does not match the current epoch then + /// this field may not be accurate + pub total_lamports: u64, + + /// Total supply of pool tokens (should always match the supply in the Pool Mint) + pub pool_token_supply: u64, + + /// Last epoch the `total_lamports` field was updated + pub last_update_epoch: u64, + + /// Lockup that all stakes in the pool must have + pub lockup: Lockup, + + /// Fee taken as a proportion of rewards each epoch + pub epoch_fee: Fee, + + /// Fee for next epoch + pub next_epoch_fee: Option, + + /// Preferred deposit validator vote account pubkey + pub preferred_deposit_validator_vote_address: Option, + + /// Preferred withdraw validator vote account pubkey + pub preferred_withdraw_validator_vote_address: Option, + + /// Fee assessed on stake deposits + pub stake_deposit_fee: Fee, + + /// Fee assessed on withdrawals + pub stake_withdrawal_fee: Fee, + + /// Future stake withdrawal fee, to be set for the following epoch + pub next_stake_withdrawal_fee: Option, + + /// Fees paid out to referrers on referred stake deposits. + /// Expressed as a percentage (0 - 100) of deposit fees. + /// 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 + /// this `sol_deposit_authority` + pub sol_deposit_authority: Option, + + /// 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, + + /// Toggles whether the `WithdrawSol` instruction requires a signature from + /// the `deposit_authority` + pub sol_withdraw_authority: Option, + + /// Fee assessed on SOL withdrawals + pub sol_withdrawal_fee: Fee, + + /// Future SOL withdrawal fee, to be set for the following epoch + pub next_sol_withdrawal_fee: Option, + + /// Last epoch's total pool tokens, used only for APR estimation + pub last_epoch_pool_token_supply: u64, + + /// Last epoch's total lamports, used only for APR estimation + pub last_epoch_total_lamports: u64, + + /// Last epoch's exchange rate for SOL deposit and withdraw + pub rate_of_exchange: Option, + + /// Treasury fee account + pub treasury_fee_account: Pubkey, + + /// Fee assessed on taking rewards for treasury + pub treasury_fee: Fee, + + /// Total liquidity in Sol equivalent under management. + pub total_lamports_liquidity: u64, +} + /// Initialized program details. #[repr(C)] #[derive(Clone, Debug, Default, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)]