1409 lines
42 KiB
Rust
1409 lines
42 KiB
Rust
#![allow(dead_code)]
|
|
|
|
use {
|
|
solana_program::{
|
|
borsh::{get_instance_packed_len, get_packed_len, try_from_slice_unchecked},
|
|
hash::Hash,
|
|
program_pack::Pack,
|
|
pubkey::Pubkey,
|
|
system_instruction, system_program,
|
|
},
|
|
solana_program_test::*,
|
|
solana_sdk::{
|
|
account::Account,
|
|
signature::{Keypair, Signer},
|
|
transaction::Transaction,
|
|
transport::TransportError,
|
|
},
|
|
solana_vote_program::{
|
|
self, vote_instruction,
|
|
vote_state::{VoteInit, VoteState},
|
|
},
|
|
spl_stake_pool::{
|
|
find_stake_program_address, find_transient_stake_program_address, id, instruction,
|
|
processor, stake_program, state, state::FeeType,
|
|
},
|
|
};
|
|
|
|
pub const TEST_STAKE_AMOUNT: u64 = 1_500_000_000;
|
|
pub const MAX_TEST_VALIDATORS: u32 = 10_000;
|
|
|
|
pub fn program_test() -> ProgramTest {
|
|
ProgramTest::new(
|
|
"spl_stake_pool",
|
|
id(),
|
|
processor!(processor::Processor::process),
|
|
)
|
|
}
|
|
|
|
pub async fn get_account(banks_client: &mut BanksClient, pubkey: &Pubkey) -> Account {
|
|
banks_client
|
|
.get_account(*pubkey)
|
|
.await
|
|
.expect("account not found")
|
|
.expect("account empty")
|
|
}
|
|
|
|
pub async fn create_mint(
|
|
banks_client: &mut BanksClient,
|
|
payer: &Keypair,
|
|
recent_blockhash: &Hash,
|
|
pool_mint: &Keypair,
|
|
manager: &Pubkey,
|
|
) -> Result<(), TransportError> {
|
|
let rent = banks_client.get_rent().await.unwrap();
|
|
let mint_rent = rent.minimum_balance(spl_token::state::Mint::LEN);
|
|
|
|
let mut transaction = Transaction::new_with_payer(
|
|
&[
|
|
system_instruction::create_account(
|
|
&payer.pubkey(),
|
|
&pool_mint.pubkey(),
|
|
mint_rent,
|
|
spl_token::state::Mint::LEN as u64,
|
|
&spl_token::id(),
|
|
),
|
|
spl_token::instruction::initialize_mint(
|
|
&spl_token::id(),
|
|
&pool_mint.pubkey(),
|
|
&manager,
|
|
None,
|
|
0,
|
|
)
|
|
.unwrap(),
|
|
],
|
|
Some(&payer.pubkey()),
|
|
);
|
|
transaction.sign(&[payer, pool_mint], *recent_blockhash);
|
|
banks_client.process_transaction(transaction).await?;
|
|
Ok(())
|
|
}
|
|
|
|
pub async fn transfer(
|
|
banks_client: &mut BanksClient,
|
|
payer: &Keypair,
|
|
recent_blockhash: &Hash,
|
|
recipient: &Pubkey,
|
|
amount: u64,
|
|
) {
|
|
let transaction = Transaction::new_signed_with_payer(
|
|
&[system_instruction::transfer(
|
|
&payer.pubkey(),
|
|
recipient,
|
|
amount,
|
|
)],
|
|
Some(&payer.pubkey()),
|
|
&[payer],
|
|
*recent_blockhash,
|
|
);
|
|
banks_client.process_transaction(transaction).await.unwrap();
|
|
}
|
|
|
|
pub async fn transfer_spl_tokens(
|
|
banks_client: &mut BanksClient,
|
|
payer: &Keypair,
|
|
recent_blockhash: &Hash,
|
|
source: &Pubkey,
|
|
destination: &Pubkey,
|
|
authority: &Keypair,
|
|
amount: u64,
|
|
) {
|
|
let transaction = Transaction::new_signed_with_payer(
|
|
&[spl_token::instruction::transfer(
|
|
&spl_token::id(),
|
|
source,
|
|
destination,
|
|
&authority.pubkey(),
|
|
&[],
|
|
amount,
|
|
)
|
|
.unwrap()],
|
|
Some(&payer.pubkey()),
|
|
&[payer, authority],
|
|
*recent_blockhash,
|
|
);
|
|
banks_client.process_transaction(transaction).await.unwrap();
|
|
}
|
|
|
|
pub async fn create_token_account(
|
|
banks_client: &mut BanksClient,
|
|
payer: &Keypair,
|
|
recent_blockhash: &Hash,
|
|
account: &Keypair,
|
|
pool_mint: &Pubkey,
|
|
manager: &Pubkey,
|
|
) -> Result<(), TransportError> {
|
|
let rent = banks_client.get_rent().await.unwrap();
|
|
let account_rent = rent.minimum_balance(spl_token::state::Account::LEN);
|
|
|
|
let mut transaction = Transaction::new_with_payer(
|
|
&[
|
|
system_instruction::create_account(
|
|
&payer.pubkey(),
|
|
&account.pubkey(),
|
|
account_rent,
|
|
spl_token::state::Account::LEN as u64,
|
|
&spl_token::id(),
|
|
),
|
|
spl_token::instruction::initialize_account(
|
|
&spl_token::id(),
|
|
&account.pubkey(),
|
|
pool_mint,
|
|
manager,
|
|
)
|
|
.unwrap(),
|
|
],
|
|
Some(&payer.pubkey()),
|
|
);
|
|
transaction.sign(&[payer, account], *recent_blockhash);
|
|
banks_client.process_transaction(transaction).await?;
|
|
Ok(())
|
|
}
|
|
|
|
pub async fn close_token_account(
|
|
banks_client: &mut BanksClient,
|
|
payer: &Keypair,
|
|
recent_blockhash: &Hash,
|
|
account: &Pubkey,
|
|
lamports_destination: &Pubkey,
|
|
manager: &Keypair,
|
|
) -> Result<(), TransportError> {
|
|
let mut transaction = Transaction::new_with_payer(
|
|
&[spl_token::instruction::close_account(
|
|
&spl_token::id(),
|
|
&account,
|
|
&lamports_destination,
|
|
&manager.pubkey(),
|
|
&[],
|
|
)
|
|
.unwrap()],
|
|
Some(&payer.pubkey()),
|
|
);
|
|
transaction.sign(&[payer, manager], *recent_blockhash);
|
|
banks_client.process_transaction(transaction).await?;
|
|
Ok(())
|
|
}
|
|
|
|
pub async fn freeze_token_account(
|
|
banks_client: &mut BanksClient,
|
|
payer: &Keypair,
|
|
recent_blockhash: &Hash,
|
|
account: &Pubkey,
|
|
pool_mint: &Pubkey,
|
|
manager: &Keypair,
|
|
) -> Result<(), TransportError> {
|
|
let mut transaction = Transaction::new_with_payer(
|
|
&[spl_token::instruction::freeze_account(
|
|
&spl_token::id(),
|
|
&account,
|
|
pool_mint,
|
|
&manager.pubkey(),
|
|
&[],
|
|
)
|
|
.unwrap()],
|
|
Some(&payer.pubkey()),
|
|
);
|
|
transaction.sign(&[payer, manager], *recent_blockhash);
|
|
banks_client.process_transaction(transaction).await?;
|
|
Ok(())
|
|
}
|
|
|
|
pub async fn mint_tokens(
|
|
banks_client: &mut BanksClient,
|
|
payer: &Keypair,
|
|
recent_blockhash: &Hash,
|
|
mint: &Pubkey,
|
|
account: &Pubkey,
|
|
mint_authority: &Keypair,
|
|
amount: u64,
|
|
) -> Result<(), TransportError> {
|
|
let transaction = Transaction::new_signed_with_payer(
|
|
&[spl_token::instruction::mint_to(
|
|
&spl_token::id(),
|
|
mint,
|
|
account,
|
|
&mint_authority.pubkey(),
|
|
&[],
|
|
amount,
|
|
)
|
|
.unwrap()],
|
|
Some(&payer.pubkey()),
|
|
&[payer, mint_authority],
|
|
*recent_blockhash,
|
|
);
|
|
banks_client.process_transaction(transaction).await?;
|
|
Ok(())
|
|
}
|
|
|
|
pub async fn burn_tokens(
|
|
banks_client: &mut BanksClient,
|
|
payer: &Keypair,
|
|
recent_blockhash: &Hash,
|
|
mint: &Pubkey,
|
|
account: &Pubkey,
|
|
authority: &Keypair,
|
|
amount: u64,
|
|
) -> Result<(), TransportError> {
|
|
let transaction = Transaction::new_signed_with_payer(
|
|
&[spl_token::instruction::burn(
|
|
&spl_token::id(),
|
|
account,
|
|
mint,
|
|
&authority.pubkey(),
|
|
&[],
|
|
amount,
|
|
)
|
|
.unwrap()],
|
|
Some(&payer.pubkey()),
|
|
&[payer, authority],
|
|
*recent_blockhash,
|
|
);
|
|
banks_client.process_transaction(transaction).await?;
|
|
Ok(())
|
|
}
|
|
|
|
pub async fn get_token_balance(banks_client: &mut BanksClient, token: &Pubkey) -> u64 {
|
|
let token_account = banks_client.get_account(*token).await.unwrap().unwrap();
|
|
let account_info: spl_token::state::Account =
|
|
spl_token::state::Account::unpack_from_slice(token_account.data.as_slice()).unwrap();
|
|
account_info.amount
|
|
}
|
|
|
|
pub async fn get_token_supply(banks_client: &mut BanksClient, mint: &Pubkey) -> u64 {
|
|
let mint_account = banks_client.get_account(*mint).await.unwrap().unwrap();
|
|
let account_info =
|
|
spl_token::state::Mint::unpack_from_slice(mint_account.data.as_slice()).unwrap();
|
|
account_info.supply
|
|
}
|
|
|
|
pub async fn delegate_tokens(
|
|
banks_client: &mut BanksClient,
|
|
payer: &Keypair,
|
|
recent_blockhash: &Hash,
|
|
account: &Pubkey,
|
|
manager: &Keypair,
|
|
delegate: &Pubkey,
|
|
amount: u64,
|
|
) {
|
|
let transaction = Transaction::new_signed_with_payer(
|
|
&[spl_token::instruction::approve(
|
|
&spl_token::id(),
|
|
&account,
|
|
&delegate,
|
|
&manager.pubkey(),
|
|
&[],
|
|
amount,
|
|
)
|
|
.unwrap()],
|
|
Some(&payer.pubkey()),
|
|
&[payer, manager],
|
|
*recent_blockhash,
|
|
);
|
|
banks_client.process_transaction(transaction).await.unwrap();
|
|
}
|
|
|
|
#[allow(clippy::too_many_arguments)]
|
|
pub async fn create_stake_pool(
|
|
banks_client: &mut BanksClient,
|
|
payer: &Keypair,
|
|
recent_blockhash: &Hash,
|
|
stake_pool: &Keypair,
|
|
validator_list: &Keypair,
|
|
reserve_stake: &Pubkey,
|
|
pool_mint: &Pubkey,
|
|
pool_token_account: &Pubkey,
|
|
manager: &Keypair,
|
|
staker: &Pubkey,
|
|
stake_deposit_authority: &Option<Keypair>,
|
|
fee: &state::Fee,
|
|
withdrawal_fee: &state::Fee,
|
|
deposit_fee: &state::Fee,
|
|
referral_fee: u8,
|
|
sol_deposit_fee: &state::Fee,
|
|
sol_referral_fee: u8,
|
|
max_validators: u32,
|
|
) -> Result<(), TransportError> {
|
|
let rent = banks_client.get_rent().await.unwrap();
|
|
let rent_stake_pool = rent.minimum_balance(get_packed_len::<state::StakePool>());
|
|
let validator_list_size =
|
|
get_instance_packed_len(&state::ValidatorList::new(max_validators)).unwrap();
|
|
let rent_validator_list = rent.minimum_balance(validator_list_size);
|
|
|
|
let mut transaction = Transaction::new_with_payer(
|
|
&[
|
|
system_instruction::create_account(
|
|
&payer.pubkey(),
|
|
&stake_pool.pubkey(),
|
|
rent_stake_pool,
|
|
get_packed_len::<state::StakePool>() as u64,
|
|
&id(),
|
|
),
|
|
system_instruction::create_account(
|
|
&payer.pubkey(),
|
|
&validator_list.pubkey(),
|
|
rent_validator_list,
|
|
validator_list_size as u64,
|
|
&id(),
|
|
),
|
|
instruction::initialize(
|
|
&id(),
|
|
&stake_pool.pubkey(),
|
|
&manager.pubkey(),
|
|
staker,
|
|
&validator_list.pubkey(),
|
|
reserve_stake,
|
|
pool_mint,
|
|
pool_token_account,
|
|
&spl_token::id(),
|
|
stake_deposit_authority.as_ref().map(|k| k.pubkey()),
|
|
*fee,
|
|
*withdrawal_fee,
|
|
*deposit_fee,
|
|
referral_fee,
|
|
max_validators,
|
|
),
|
|
instruction::set_fee(
|
|
&id(),
|
|
&stake_pool.pubkey(),
|
|
&manager.pubkey(),
|
|
FeeType::SolDeposit(*sol_deposit_fee),
|
|
),
|
|
instruction::set_fee(
|
|
&id(),
|
|
&stake_pool.pubkey(),
|
|
&manager.pubkey(),
|
|
FeeType::SolReferral(sol_referral_fee),
|
|
),
|
|
],
|
|
Some(&payer.pubkey()),
|
|
);
|
|
let mut signers = vec![payer, stake_pool, validator_list, manager];
|
|
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?;
|
|
Ok(())
|
|
}
|
|
|
|
pub async fn create_vote(
|
|
banks_client: &mut BanksClient,
|
|
payer: &Keypair,
|
|
recent_blockhash: &Hash,
|
|
validator: &Keypair,
|
|
vote: &Keypair,
|
|
) {
|
|
let rent = banks_client.get_rent().await.unwrap();
|
|
let rent_voter = rent.minimum_balance(VoteState::size_of());
|
|
|
|
let mut instructions = vec![system_instruction::create_account(
|
|
&payer.pubkey(),
|
|
&validator.pubkey(),
|
|
42,
|
|
0,
|
|
&system_program::id(),
|
|
)];
|
|
instructions.append(&mut vote_instruction::create_account(
|
|
&payer.pubkey(),
|
|
&vote.pubkey(),
|
|
&VoteInit {
|
|
node_pubkey: validator.pubkey(),
|
|
authorized_voter: validator.pubkey(),
|
|
..VoteInit::default()
|
|
},
|
|
rent_voter,
|
|
));
|
|
|
|
let transaction = Transaction::new_signed_with_payer(
|
|
&instructions,
|
|
Some(&payer.pubkey()),
|
|
&[validator, vote, payer],
|
|
*recent_blockhash,
|
|
);
|
|
banks_client.process_transaction(transaction).await.unwrap();
|
|
}
|
|
|
|
pub async fn create_independent_stake_account(
|
|
banks_client: &mut BanksClient,
|
|
payer: &Keypair,
|
|
recent_blockhash: &Hash,
|
|
stake: &Keypair,
|
|
authorized: &stake_program::Authorized,
|
|
lockup: &stake_program::Lockup,
|
|
stake_amount: u64,
|
|
) -> u64 {
|
|
let rent = banks_client.get_rent().await.unwrap();
|
|
let lamports =
|
|
rent.minimum_balance(std::mem::size_of::<stake_program::StakeState>()) + stake_amount;
|
|
|
|
let transaction = Transaction::new_signed_with_payer(
|
|
&stake_program::create_account(
|
|
&payer.pubkey(),
|
|
&stake.pubkey(),
|
|
authorized,
|
|
lockup,
|
|
lamports,
|
|
),
|
|
Some(&payer.pubkey()),
|
|
&[payer, stake],
|
|
*recent_blockhash,
|
|
);
|
|
banks_client.process_transaction(transaction).await.unwrap();
|
|
|
|
lamports
|
|
}
|
|
|
|
pub async fn create_blank_stake_account(
|
|
banks_client: &mut BanksClient,
|
|
payer: &Keypair,
|
|
recent_blockhash: &Hash,
|
|
stake: &Keypair,
|
|
) -> u64 {
|
|
let rent = banks_client.get_rent().await.unwrap();
|
|
let lamports = rent.minimum_balance(std::mem::size_of::<stake_program::StakeState>()) + 1;
|
|
|
|
let transaction = Transaction::new_signed_with_payer(
|
|
&[system_instruction::create_account(
|
|
&payer.pubkey(),
|
|
&stake.pubkey(),
|
|
lamports,
|
|
std::mem::size_of::<stake_program::StakeState>() as u64,
|
|
&stake_program::id(),
|
|
)],
|
|
Some(&payer.pubkey()),
|
|
&[payer, stake],
|
|
*recent_blockhash,
|
|
);
|
|
banks_client.process_transaction(transaction).await.unwrap();
|
|
|
|
lamports
|
|
}
|
|
|
|
pub async fn create_validator_stake_account(
|
|
banks_client: &mut BanksClient,
|
|
payer: &Keypair,
|
|
recent_blockhash: &Hash,
|
|
stake_pool: &Pubkey,
|
|
staker: &Keypair,
|
|
stake_account: &Pubkey,
|
|
validator: &Pubkey,
|
|
) {
|
|
let transaction = Transaction::new_signed_with_payer(
|
|
&[instruction::create_validator_stake_account(
|
|
&id(),
|
|
&stake_pool,
|
|
&staker.pubkey(),
|
|
&payer.pubkey(),
|
|
&stake_account,
|
|
&validator,
|
|
)],
|
|
Some(&payer.pubkey()),
|
|
&[payer, staker],
|
|
*recent_blockhash,
|
|
);
|
|
banks_client.process_transaction(transaction).await.unwrap();
|
|
}
|
|
|
|
pub async fn delegate_stake_account(
|
|
banks_client: &mut BanksClient,
|
|
payer: &Keypair,
|
|
recent_blockhash: &Hash,
|
|
stake: &Pubkey,
|
|
authorized: &Keypair,
|
|
vote: &Pubkey,
|
|
) {
|
|
let mut transaction = Transaction::new_with_payer(
|
|
&[stake_program::delegate_stake(
|
|
&stake,
|
|
&authorized.pubkey(),
|
|
&vote,
|
|
)],
|
|
Some(&payer.pubkey()),
|
|
);
|
|
transaction.sign(&[payer, authorized], *recent_blockhash);
|
|
banks_client.process_transaction(transaction).await.unwrap();
|
|
}
|
|
|
|
pub async fn authorize_stake_account(
|
|
banks_client: &mut BanksClient,
|
|
payer: &Keypair,
|
|
recent_blockhash: &Hash,
|
|
stake: &Pubkey,
|
|
authorized: &Keypair,
|
|
new_authorized: &Pubkey,
|
|
stake_authorize: stake_program::StakeAuthorize,
|
|
) {
|
|
let mut transaction = Transaction::new_with_payer(
|
|
&[stake_program::authorize(
|
|
&stake,
|
|
&authorized.pubkey(),
|
|
&new_authorized,
|
|
stake_authorize,
|
|
)],
|
|
Some(&payer.pubkey()),
|
|
);
|
|
transaction.sign(&[payer, authorized], *recent_blockhash);
|
|
banks_client.process_transaction(transaction).await.unwrap();
|
|
}
|
|
|
|
pub struct ValidatorStakeAccount {
|
|
pub stake_account: Pubkey,
|
|
pub transient_stake_account: Pubkey,
|
|
pub vote: Keypair,
|
|
pub validator: Keypair,
|
|
pub stake_pool: Pubkey,
|
|
}
|
|
|
|
impl ValidatorStakeAccount {
|
|
pub fn new(stake_pool: &Pubkey) -> Self {
|
|
let validator = Keypair::new();
|
|
let vote = Keypair::new();
|
|
let (stake_account, _) = find_stake_program_address(&id(), &vote.pubkey(), stake_pool);
|
|
let (transient_stake_account, _) =
|
|
find_transient_stake_program_address(&id(), &vote.pubkey(), stake_pool);
|
|
ValidatorStakeAccount {
|
|
stake_account,
|
|
transient_stake_account,
|
|
vote,
|
|
validator,
|
|
stake_pool: *stake_pool,
|
|
}
|
|
}
|
|
|
|
pub async fn create_and_delegate(
|
|
&self,
|
|
mut banks_client: &mut BanksClient,
|
|
payer: &Keypair,
|
|
recent_blockhash: &Hash,
|
|
staker: &Keypair,
|
|
) {
|
|
create_vote(
|
|
&mut banks_client,
|
|
&payer,
|
|
&recent_blockhash,
|
|
&self.validator,
|
|
&self.vote,
|
|
)
|
|
.await;
|
|
|
|
create_validator_stake_account(
|
|
&mut banks_client,
|
|
&payer,
|
|
&recent_blockhash,
|
|
&self.stake_pool,
|
|
staker,
|
|
&self.stake_account,
|
|
&self.vote.pubkey(),
|
|
)
|
|
.await;
|
|
}
|
|
}
|
|
|
|
pub struct StakePoolAccounts {
|
|
pub stake_pool: Keypair,
|
|
pub validator_list: Keypair,
|
|
pub reserve_stake: Keypair,
|
|
pub pool_mint: Keypair,
|
|
pub pool_fee_account: Keypair,
|
|
pub manager: Keypair,
|
|
pub staker: Keypair,
|
|
pub withdraw_authority: Pubkey,
|
|
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 sol_deposit_fee: state::Fee,
|
|
pub sol_referral_fee: u8,
|
|
pub max_validators: u32,
|
|
}
|
|
|
|
impl StakePoolAccounts {
|
|
pub fn new() -> Self {
|
|
let stake_pool = Keypair::new();
|
|
let validator_list = Keypair::new();
|
|
let stake_pool_address = &stake_pool.pubkey();
|
|
let (withdraw_authority, _) = Pubkey::find_program_address(
|
|
&[&stake_pool_address.to_bytes()[..32], b"withdraw"],
|
|
&id(),
|
|
);
|
|
let (stake_deposit_authority, _) = Pubkey::find_program_address(
|
|
&[&stake_pool_address.to_bytes()[..32], b"deposit"],
|
|
&id(),
|
|
);
|
|
let reserve_stake = Keypair::new();
|
|
let pool_mint = Keypair::new();
|
|
let pool_fee_account = Keypair::new();
|
|
let manager = Keypair::new();
|
|
let staker = Keypair::new();
|
|
|
|
Self {
|
|
stake_pool,
|
|
validator_list,
|
|
reserve_stake,
|
|
pool_mint,
|
|
pool_fee_account,
|
|
manager,
|
|
staker,
|
|
withdraw_authority,
|
|
stake_deposit_authority,
|
|
stake_deposit_authority_keypair: None,
|
|
fee: state::Fee {
|
|
numerator: 1,
|
|
denominator: 100,
|
|
},
|
|
withdrawal_fee: state::Fee {
|
|
numerator: 3,
|
|
denominator: 1000,
|
|
},
|
|
deposit_fee: state::Fee {
|
|
numerator: 1,
|
|
denominator: 1000,
|
|
},
|
|
referral_fee: 25,
|
|
sol_deposit_fee: state::Fee {
|
|
numerator: 3,
|
|
denominator: 100,
|
|
},
|
|
sol_referral_fee: 50,
|
|
max_validators: MAX_TEST_VALIDATORS,
|
|
}
|
|
}
|
|
|
|
pub fn new_with_stake_deposit_authority(stake_deposit_authority: Keypair) -> Self {
|
|
let mut stake_pool_accounts = Self::new();
|
|
stake_pool_accounts.stake_deposit_authority = stake_deposit_authority.pubkey();
|
|
stake_pool_accounts.stake_deposit_authority_keypair = Some(stake_deposit_authority);
|
|
stake_pool_accounts
|
|
}
|
|
|
|
pub fn calculate_fee(&self, amount: u64) -> u64 {
|
|
amount * self.fee.numerator / self.fee.denominator
|
|
}
|
|
|
|
pub fn calculate_withdrawal_fee(&self, pool_tokens: u64) -> u64 {
|
|
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 fn calculate_sol_deposit_fee(&self, pool_tokens: u64) -> u64 {
|
|
pool_tokens * self.sol_deposit_fee.numerator / self.sol_deposit_fee.denominator
|
|
}
|
|
|
|
pub fn calculate_sol_referral_fee(&self, deposit_fee_collected: u64) -> u64 {
|
|
deposit_fee_collected * self.sol_referral_fee as u64 / 100
|
|
}
|
|
|
|
pub async fn initialize_stake_pool(
|
|
&self,
|
|
mut banks_client: &mut BanksClient,
|
|
payer: &Keypair,
|
|
recent_blockhash: &Hash,
|
|
reserve_lamports: u64,
|
|
) -> Result<(), TransportError> {
|
|
create_mint(
|
|
&mut banks_client,
|
|
&payer,
|
|
&recent_blockhash,
|
|
&self.pool_mint,
|
|
&self.withdraw_authority,
|
|
)
|
|
.await?;
|
|
create_token_account(
|
|
&mut banks_client,
|
|
&payer,
|
|
&recent_blockhash,
|
|
&self.pool_fee_account,
|
|
&self.pool_mint.pubkey(),
|
|
&self.manager.pubkey(),
|
|
)
|
|
.await?;
|
|
create_independent_stake_account(
|
|
&mut banks_client,
|
|
&payer,
|
|
&recent_blockhash,
|
|
&self.reserve_stake,
|
|
&stake_program::Authorized {
|
|
staker: self.withdraw_authority,
|
|
withdrawer: self.withdraw_authority,
|
|
},
|
|
&stake_program::Lockup::default(),
|
|
reserve_lamports,
|
|
)
|
|
.await;
|
|
create_stake_pool(
|
|
&mut banks_client,
|
|
&payer,
|
|
&recent_blockhash,
|
|
&self.stake_pool,
|
|
&self.validator_list,
|
|
&self.reserve_stake.pubkey(),
|
|
&self.pool_mint.pubkey(),
|
|
&self.pool_fee_account.pubkey(),
|
|
&self.manager,
|
|
&self.staker.pubkey(),
|
|
&self.stake_deposit_authority_keypair,
|
|
&self.fee,
|
|
&self.withdrawal_fee,
|
|
&self.deposit_fee,
|
|
self.referral_fee,
|
|
&self.sol_deposit_fee,
|
|
self.sol_referral_fee,
|
|
self.max_validators,
|
|
)
|
|
.await?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[allow(clippy::too_many_arguments)]
|
|
pub async fn deposit_stake(
|
|
&self,
|
|
banks_client: &mut BanksClient,
|
|
payer: &Keypair,
|
|
recent_blockhash: &Hash,
|
|
stake: &Pubkey,
|
|
pool_account: &Pubkey,
|
|
validator_stake_account: &Pubkey,
|
|
current_staker: &Keypair,
|
|
) -> Option<TransportError> {
|
|
self.deposit_stake_with_referral(
|
|
banks_client,
|
|
payer,
|
|
recent_blockhash,
|
|
stake,
|
|
pool_account,
|
|
validator_stake_account,
|
|
current_staker,
|
|
&self.pool_fee_account.pubkey(),
|
|
)
|
|
.await
|
|
}
|
|
|
|
#[allow(clippy::too_many_arguments)]
|
|
pub async fn deposit_stake_with_referral(
|
|
&self,
|
|
banks_client: &mut BanksClient,
|
|
payer: &Keypair,
|
|
recent_blockhash: &Hash,
|
|
stake: &Pubkey,
|
|
pool_account: &Pubkey,
|
|
validator_stake_account: &Pubkey,
|
|
current_staker: &Keypair,
|
|
referrer: &Pubkey,
|
|
) -> Option<TransportError> {
|
|
let mut signers = vec![payer, current_staker];
|
|
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(),
|
|
referrer,
|
|
&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(),
|
|
&referrer,
|
|
&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(),
|
|
&sol_deposit_authority.pubkey(),
|
|
&self.withdraw_authority,
|
|
&self.reserve_stake.pubkey(),
|
|
&payer.pubkey(),
|
|
pool_account,
|
|
&self.pool_fee_account.pubkey(),
|
|
&self.pool_fee_account.pubkey(),
|
|
&self.pool_mint.pubkey(),
|
|
&spl_token::id(),
|
|
amount,
|
|
)
|
|
} else {
|
|
instruction::deposit_sol(
|
|
&id(),
|
|
&self.stake_pool.pubkey(),
|
|
&self.withdraw_authority,
|
|
&self.reserve_stake.pubkey(),
|
|
&payer.pubkey(),
|
|
pool_account,
|
|
&self.pool_fee_account.pubkey(),
|
|
&self.pool_fee_account.pubkey(),
|
|
&self.pool_mint.pubkey(),
|
|
&spl_token::id(),
|
|
amount,
|
|
)
|
|
};
|
|
let transaction = Transaction::new_signed_with_payer(
|
|
&instructions,
|
|
Some(&payer.pubkey()),
|
|
&signers,
|
|
*recent_blockhash,
|
|
);
|
|
banks_client.process_transaction(transaction).await.err()
|
|
}
|
|
|
|
#[allow(clippy::too_many_arguments)]
|
|
pub async fn withdraw_stake(
|
|
&self,
|
|
banks_client: &mut BanksClient,
|
|
payer: &Keypair,
|
|
recent_blockhash: &Hash,
|
|
stake_recipient: &Pubkey,
|
|
user_transfer_authority: &Keypair,
|
|
pool_account: &Pubkey,
|
|
validator_stake_account: &Pubkey,
|
|
recipient_new_authority: &Pubkey,
|
|
amount: u64,
|
|
) -> Option<TransportError> {
|
|
let transaction = Transaction::new_signed_with_payer(
|
|
&[instruction::withdraw_stake(
|
|
&id(),
|
|
&self.stake_pool.pubkey(),
|
|
&self.validator_list.pubkey(),
|
|
&self.withdraw_authority,
|
|
validator_stake_account,
|
|
stake_recipient,
|
|
recipient_new_authority,
|
|
&user_transfer_authority.pubkey(),
|
|
pool_account,
|
|
&self.pool_fee_account.pubkey(),
|
|
&self.pool_mint.pubkey(),
|
|
&spl_token::id(),
|
|
amount,
|
|
)],
|
|
Some(&payer.pubkey()),
|
|
&[payer, user_transfer_authority],
|
|
*recent_blockhash,
|
|
);
|
|
banks_client.process_transaction(transaction).await.err()
|
|
}
|
|
|
|
pub async fn update_validator_list_balance(
|
|
&self,
|
|
banks_client: &mut BanksClient,
|
|
payer: &Keypair,
|
|
recent_blockhash: &Hash,
|
|
validator_vote_accounts: &[Pubkey],
|
|
no_merge: bool,
|
|
) -> Option<TransportError> {
|
|
let transaction = Transaction::new_signed_with_payer(
|
|
&[instruction::update_validator_list_balance(
|
|
&id(),
|
|
&self.stake_pool.pubkey(),
|
|
&self.withdraw_authority,
|
|
&self.validator_list.pubkey(),
|
|
&self.reserve_stake.pubkey(),
|
|
validator_vote_accounts,
|
|
0,
|
|
no_merge,
|
|
)],
|
|
Some(&payer.pubkey()),
|
|
&[payer],
|
|
*recent_blockhash,
|
|
);
|
|
banks_client.process_transaction(transaction).await.err()
|
|
}
|
|
|
|
pub async fn update_stake_pool_balance(
|
|
&self,
|
|
banks_client: &mut BanksClient,
|
|
payer: &Keypair,
|
|
recent_blockhash: &Hash,
|
|
) -> Option<TransportError> {
|
|
let transaction = Transaction::new_signed_with_payer(
|
|
&[instruction::update_stake_pool_balance(
|
|
&id(),
|
|
&self.stake_pool.pubkey(),
|
|
&self.withdraw_authority,
|
|
&self.validator_list.pubkey(),
|
|
&self.reserve_stake.pubkey(),
|
|
&self.pool_fee_account.pubkey(),
|
|
&self.pool_mint.pubkey(),
|
|
)],
|
|
Some(&payer.pubkey()),
|
|
&[payer],
|
|
*recent_blockhash,
|
|
);
|
|
banks_client.process_transaction(transaction).await.err()
|
|
}
|
|
|
|
pub async fn cleanup_removed_validator_entries(
|
|
&self,
|
|
banks_client: &mut BanksClient,
|
|
payer: &Keypair,
|
|
recent_blockhash: &Hash,
|
|
) -> Option<TransportError> {
|
|
let transaction = Transaction::new_signed_with_payer(
|
|
&[instruction::cleanup_removed_validator_entries(
|
|
&id(),
|
|
&self.stake_pool.pubkey(),
|
|
&self.validator_list.pubkey(),
|
|
)],
|
|
Some(&payer.pubkey()),
|
|
&[payer],
|
|
*recent_blockhash,
|
|
);
|
|
banks_client.process_transaction(transaction).await.err()
|
|
}
|
|
|
|
pub async fn update_all(
|
|
&self,
|
|
banks_client: &mut BanksClient,
|
|
payer: &Keypair,
|
|
recent_blockhash: &Hash,
|
|
validator_vote_accounts: &[Pubkey],
|
|
no_merge: bool,
|
|
) -> Option<TransportError> {
|
|
let transaction = Transaction::new_signed_with_payer(
|
|
&[
|
|
instruction::update_validator_list_balance(
|
|
&id(),
|
|
&self.stake_pool.pubkey(),
|
|
&self.withdraw_authority,
|
|
&self.validator_list.pubkey(),
|
|
&self.reserve_stake.pubkey(),
|
|
validator_vote_accounts,
|
|
0,
|
|
no_merge,
|
|
),
|
|
instruction::update_stake_pool_balance(
|
|
&id(),
|
|
&self.stake_pool.pubkey(),
|
|
&self.withdraw_authority,
|
|
&self.validator_list.pubkey(),
|
|
&self.reserve_stake.pubkey(),
|
|
&self.pool_fee_account.pubkey(),
|
|
&self.pool_mint.pubkey(),
|
|
),
|
|
instruction::cleanup_removed_validator_entries(
|
|
&id(),
|
|
&self.stake_pool.pubkey(),
|
|
&self.validator_list.pubkey(),
|
|
),
|
|
],
|
|
Some(&payer.pubkey()),
|
|
&[payer],
|
|
*recent_blockhash,
|
|
);
|
|
banks_client.process_transaction(transaction).await.err()
|
|
}
|
|
|
|
pub async fn add_validator_to_pool(
|
|
&self,
|
|
banks_client: &mut BanksClient,
|
|
payer: &Keypair,
|
|
recent_blockhash: &Hash,
|
|
stake: &Pubkey,
|
|
) -> Option<TransportError> {
|
|
let transaction = Transaction::new_signed_with_payer(
|
|
&[instruction::add_validator_to_pool(
|
|
&id(),
|
|
&self.stake_pool.pubkey(),
|
|
&self.staker.pubkey(),
|
|
&self.withdraw_authority,
|
|
&self.validator_list.pubkey(),
|
|
stake,
|
|
)],
|
|
Some(&payer.pubkey()),
|
|
&[payer, &self.staker],
|
|
*recent_blockhash,
|
|
);
|
|
banks_client.process_transaction(transaction).await.err()
|
|
}
|
|
|
|
pub async fn remove_validator_from_pool(
|
|
&self,
|
|
banks_client: &mut BanksClient,
|
|
payer: &Keypair,
|
|
recent_blockhash: &Hash,
|
|
new_authority: &Pubkey,
|
|
validator_stake: &Pubkey,
|
|
transient_stake: &Pubkey,
|
|
) -> Option<TransportError> {
|
|
let transaction = Transaction::new_signed_with_payer(
|
|
&[instruction::remove_validator_from_pool(
|
|
&id(),
|
|
&self.stake_pool.pubkey(),
|
|
&self.staker.pubkey(),
|
|
&self.withdraw_authority,
|
|
&new_authority,
|
|
&self.validator_list.pubkey(),
|
|
validator_stake,
|
|
transient_stake,
|
|
)],
|
|
Some(&payer.pubkey()),
|
|
&[payer, &self.staker],
|
|
*recent_blockhash,
|
|
);
|
|
banks_client.process_transaction(transaction).await.err()
|
|
}
|
|
|
|
pub async fn decrease_validator_stake(
|
|
&self,
|
|
banks_client: &mut BanksClient,
|
|
payer: &Keypair,
|
|
recent_blockhash: &Hash,
|
|
validator_stake: &Pubkey,
|
|
transient_stake: &Pubkey,
|
|
lamports: u64,
|
|
) -> Option<TransportError> {
|
|
let transaction = Transaction::new_signed_with_payer(
|
|
&[instruction::decrease_validator_stake(
|
|
&id(),
|
|
&self.stake_pool.pubkey(),
|
|
&self.staker.pubkey(),
|
|
&self.withdraw_authority,
|
|
&self.validator_list.pubkey(),
|
|
validator_stake,
|
|
transient_stake,
|
|
lamports,
|
|
)],
|
|
Some(&payer.pubkey()),
|
|
&[payer, &self.staker],
|
|
*recent_blockhash,
|
|
);
|
|
banks_client.process_transaction(transaction).await.err()
|
|
}
|
|
|
|
pub async fn increase_validator_stake(
|
|
&self,
|
|
banks_client: &mut BanksClient,
|
|
payer: &Keypair,
|
|
recent_blockhash: &Hash,
|
|
transient_stake: &Pubkey,
|
|
validator: &Pubkey,
|
|
lamports: u64,
|
|
) -> Option<TransportError> {
|
|
let transaction = Transaction::new_signed_with_payer(
|
|
&[instruction::increase_validator_stake(
|
|
&id(),
|
|
&self.stake_pool.pubkey(),
|
|
&self.staker.pubkey(),
|
|
&self.withdraw_authority,
|
|
&self.validator_list.pubkey(),
|
|
&self.reserve_stake.pubkey(),
|
|
transient_stake,
|
|
validator,
|
|
lamports,
|
|
)],
|
|
Some(&payer.pubkey()),
|
|
&[payer, &self.staker],
|
|
*recent_blockhash,
|
|
);
|
|
banks_client.process_transaction(transaction).await.err()
|
|
}
|
|
|
|
pub async fn set_preferred_validator(
|
|
&self,
|
|
banks_client: &mut BanksClient,
|
|
payer: &Keypair,
|
|
recent_blockhash: &Hash,
|
|
validator_type: instruction::PreferredValidatorType,
|
|
validator: Option<Pubkey>,
|
|
) -> Option<TransportError> {
|
|
let transaction = Transaction::new_signed_with_payer(
|
|
&[instruction::set_preferred_validator(
|
|
&id(),
|
|
&self.stake_pool.pubkey(),
|
|
&self.staker.pubkey(),
|
|
&self.validator_list.pubkey(),
|
|
validator_type,
|
|
validator,
|
|
)],
|
|
Some(&payer.pubkey()),
|
|
&[payer, &self.staker],
|
|
*recent_blockhash,
|
|
);
|
|
banks_client.process_transaction(transaction).await.err()
|
|
}
|
|
}
|
|
|
|
pub async fn simple_add_validator_to_pool(
|
|
banks_client: &mut BanksClient,
|
|
payer: &Keypair,
|
|
recent_blockhash: &Hash,
|
|
stake_pool_accounts: &StakePoolAccounts,
|
|
) -> ValidatorStakeAccount {
|
|
let validator_stake = ValidatorStakeAccount::new(&stake_pool_accounts.stake_pool.pubkey());
|
|
validator_stake
|
|
.create_and_delegate(
|
|
banks_client,
|
|
&payer,
|
|
&recent_blockhash,
|
|
&stake_pool_accounts.staker,
|
|
)
|
|
.await;
|
|
|
|
let error = stake_pool_accounts
|
|
.add_validator_to_pool(
|
|
banks_client,
|
|
&payer,
|
|
&recent_blockhash,
|
|
&validator_stake.stake_account,
|
|
)
|
|
.await;
|
|
assert!(error.is_none());
|
|
|
|
validator_stake
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct DepositStakeAccount {
|
|
pub authority: Keypair,
|
|
pub stake: Keypair,
|
|
pub pool_account: Keypair,
|
|
pub stake_lamports: u64,
|
|
pub pool_tokens: u64,
|
|
pub vote_account: Pubkey,
|
|
pub validator_stake_account: Pubkey,
|
|
}
|
|
|
|
impl DepositStakeAccount {
|
|
pub fn new_with_vote(
|
|
vote_account: Pubkey,
|
|
validator_stake_account: Pubkey,
|
|
stake_lamports: u64,
|
|
) -> Self {
|
|
let authority = Keypair::new();
|
|
let stake = Keypair::new();
|
|
let pool_account = Keypair::new();
|
|
Self {
|
|
authority,
|
|
stake,
|
|
pool_account,
|
|
vote_account,
|
|
validator_stake_account,
|
|
stake_lamports,
|
|
pool_tokens: 0,
|
|
}
|
|
}
|
|
|
|
pub async fn create_and_delegate(
|
|
&self,
|
|
banks_client: &mut BanksClient,
|
|
payer: &Keypair,
|
|
recent_blockhash: &Hash,
|
|
) {
|
|
let lockup = stake_program::Lockup::default();
|
|
let authorized = stake_program::Authorized {
|
|
staker: self.authority.pubkey(),
|
|
withdrawer: self.authority.pubkey(),
|
|
};
|
|
create_independent_stake_account(
|
|
banks_client,
|
|
payer,
|
|
recent_blockhash,
|
|
&self.stake,
|
|
&authorized,
|
|
&lockup,
|
|
self.stake_lamports,
|
|
)
|
|
.await;
|
|
delegate_stake_account(
|
|
banks_client,
|
|
payer,
|
|
recent_blockhash,
|
|
&self.stake.pubkey(),
|
|
&self.authority,
|
|
&self.vote_account,
|
|
)
|
|
.await;
|
|
}
|
|
|
|
pub async fn deposit_stake(
|
|
&mut self,
|
|
banks_client: &mut BanksClient,
|
|
payer: &Keypair,
|
|
recent_blockhash: &Hash,
|
|
stake_pool_accounts: &StakePoolAccounts,
|
|
) {
|
|
// make pool token account
|
|
create_token_account(
|
|
banks_client,
|
|
payer,
|
|
recent_blockhash,
|
|
&self.pool_account,
|
|
&stake_pool_accounts.pool_mint.pubkey(),
|
|
&self.authority.pubkey(),
|
|
)
|
|
.await
|
|
.unwrap();
|
|
|
|
let error = stake_pool_accounts
|
|
.deposit_stake(
|
|
banks_client,
|
|
payer,
|
|
recent_blockhash,
|
|
&self.stake.pubkey(),
|
|
&self.pool_account.pubkey(),
|
|
&self.validator_stake_account,
|
|
&self.authority,
|
|
)
|
|
.await;
|
|
self.pool_tokens = get_token_balance(banks_client, &self.pool_account.pubkey()).await;
|
|
assert!(error.is_none());
|
|
}
|
|
}
|
|
|
|
pub async fn simple_deposit_stake(
|
|
banks_client: &mut BanksClient,
|
|
payer: &Keypair,
|
|
recent_blockhash: &Hash,
|
|
stake_pool_accounts: &StakePoolAccounts,
|
|
validator_stake_account: &ValidatorStakeAccount,
|
|
stake_lamports: u64,
|
|
) -> Option<DepositStakeAccount> {
|
|
let authority = Keypair::new();
|
|
// make stake account
|
|
let stake = Keypair::new();
|
|
let lockup = stake_program::Lockup::default();
|
|
let authorized = stake_program::Authorized {
|
|
staker: authority.pubkey(),
|
|
withdrawer: authority.pubkey(),
|
|
};
|
|
create_independent_stake_account(
|
|
banks_client,
|
|
payer,
|
|
recent_blockhash,
|
|
&stake,
|
|
&authorized,
|
|
&lockup,
|
|
stake_lamports,
|
|
)
|
|
.await;
|
|
let vote_account = validator_stake_account.vote.pubkey();
|
|
delegate_stake_account(
|
|
banks_client,
|
|
payer,
|
|
recent_blockhash,
|
|
&stake.pubkey(),
|
|
&authority,
|
|
&vote_account,
|
|
)
|
|
.await;
|
|
// make pool token account
|
|
let pool_account = Keypair::new();
|
|
create_token_account(
|
|
banks_client,
|
|
payer,
|
|
recent_blockhash,
|
|
&pool_account,
|
|
&stake_pool_accounts.pool_mint.pubkey(),
|
|
&authority.pubkey(),
|
|
)
|
|
.await
|
|
.unwrap();
|
|
|
|
let validator_stake_account = validator_stake_account.stake_account;
|
|
let error = stake_pool_accounts
|
|
.deposit_stake(
|
|
banks_client,
|
|
payer,
|
|
recent_blockhash,
|
|
&stake.pubkey(),
|
|
&pool_account.pubkey(),
|
|
&validator_stake_account,
|
|
&authority,
|
|
)
|
|
.await;
|
|
// backwards, but oh well!
|
|
if error.is_some() {
|
|
return None;
|
|
}
|
|
|
|
let pool_tokens = get_token_balance(banks_client, &pool_account.pubkey()).await;
|
|
|
|
Some(DepositStakeAccount {
|
|
authority,
|
|
stake,
|
|
pool_account,
|
|
stake_lamports,
|
|
pool_tokens,
|
|
vote_account,
|
|
validator_stake_account,
|
|
})
|
|
}
|
|
|
|
pub async fn get_validator_list_sum(
|
|
banks_client: &mut BanksClient,
|
|
reserve_stake: &Pubkey,
|
|
validator_list: &Pubkey,
|
|
) -> u64 {
|
|
let validator_list = banks_client
|
|
.get_account(*validator_list)
|
|
.await
|
|
.unwrap()
|
|
.unwrap();
|
|
let validator_list =
|
|
try_from_slice_unchecked::<state::ValidatorList>(validator_list.data.as_slice()).unwrap();
|
|
let reserve_stake = banks_client
|
|
.get_account(*reserve_stake)
|
|
.await
|
|
.unwrap()
|
|
.unwrap();
|
|
|
|
let validator_sum: u64 = validator_list
|
|
.validators
|
|
.iter()
|
|
.map(|info| info.stake_lamports())
|
|
.sum();
|
|
let rent = banks_client.get_rent().await.unwrap();
|
|
let rent = rent.minimum_balance(std::mem::size_of::<stake_program::StakeState>());
|
|
validator_sum + reserve_stake.lamports - rent - 1
|
|
}
|