From bcb68b64a409b23701dd6542ad039c0b75e22c41 Mon Sep 17 00:00:00 2001 From: Guillermo Bescos Date: Mon, 11 Nov 2024 19:52:16 +0000 Subject: [PATCH] checkpoint --- staking/Cargo.lock | 1 + staking/cli/Cargo.toml | 1 + staking/cli/src/cli.rs | 1 + staking/cli/src/instructions.rs | 410 +++++++++++++++++- staking/cli/src/main.rs | 4 + staking/programs/staking/src/lib.rs | 2 +- .../staking/src/utils/voter_weight.rs | 2 +- 7 files changed, 416 insertions(+), 5 deletions(-) diff --git a/staking/Cargo.lock b/staking/Cargo.lock index 7e1c4523..7aaf4f9d 100644 --- a/staking/Cargo.lock +++ b/staking/Cargo.lock @@ -5289,6 +5289,7 @@ dependencies = [ "serde_json", "serde_wormhole", "shellexpand", + "solana-account-decoder", "solana-client", "solana-remote-wallet", "solana-sdk", diff --git a/staking/cli/Cargo.toml b/staking/cli/Cargo.toml index 47302437..3b562f35 100644 --- a/staking/cli/Cargo.toml +++ b/staking/cli/Cargo.toml @@ -25,3 +25,4 @@ reqwest = "0.11" serde_json = "1.0.128" uriparse = "0.6.4" solana-remote-wallet = "1.18.16" +solana-account-decoder = "1.18.16" diff --git a/staking/cli/src/cli.rs b/staking/cli/src/cli.rs index 10fdb75a..1c1a8108 100644 --- a/staking/cli/src/cli.rs +++ b/staking/cli/src/cli.rs @@ -109,6 +109,7 @@ pub enum Action { #[clap(long, help = "Publisher caps")] publisher_caps: Pubkey, }, + GetAllAccounts {}, } pub enum SignerSource { diff --git a/staking/cli/src/instructions.rs b/staking/cli/src/instructions.rs index 277ab620..df0303b1 100644 --- a/staking/cli/src/instructions.rs +++ b/staking/cli/src/instructions.rs @@ -1,14 +1,22 @@ use { anchor_lang::{ + accounts::account, AccountDeserialize, + Discriminator, InstructionData, ToAccountMetas, }, anchor_spl::{ associated_token::spl_associated_token_account, - token::spl_token, + token::{ + spl_token, + TokenAccount, + }, + }, + base64::{ + encode, + Engine, }, - base64::Engine, integration_tests::{ integrity_pool::pda::{ get_delegation_record_address, @@ -42,9 +50,19 @@ use { }, reqwest::blocking::Client, serde_wormhole::RawMessage, + solana_account_decoder::UiAccountEncoding, solana_client::{ rpc_client::RpcClient, - rpc_config::RpcSendTransactionConfig, + rpc_config::{ + RpcAccountInfoConfig, + RpcProgramAccountsConfig, + RpcSendTransactionConfig, + }, + rpc_filter::{ + Memcmp, + MemcmpEncodedBytes, + RpcFilterType, + }, }, solana_sdk::{ commitment_config::CommitmentConfig, @@ -67,10 +85,31 @@ use { TransactionError, }, }, + staking::{ + state::{ + global_config::GlobalConfig, + max_voter_weight_record::MAX_VOTER_WEIGHT, + positions::{ + DynamicPositionArray, + DynamicPositionArrayAccount, + PositionData, + PositionState, + }, + stake_account::StakeAccountMetadataV2, + vesting::VestingSchedule, + }, + utils::voter_weight::compute_voter_weight, + }, std::{ cmp::min, + collections::{ + HashMap, + HashSet, + }, convert::TryInto, mem::size_of, + str::FromStr, + thread::current, }, wormhole_core_bridge_solana::sdk::{ WriteEncodedVaaArgs, @@ -498,6 +537,12 @@ pub fn initialize_pool( .unwrap(); } +pub fn get_current_time(rpc_client: &RpcClient) -> i64 { + let slot = rpc_client.get_slot().unwrap(); + let blocktime = rpc_client.get_block_time(slot).unwrap(); + blocktime +} + pub fn get_current_epoch(rpc_client: &RpcClient) -> u64 { let slot = rpc_client.get_slot().unwrap(); let blocktime = rpc_client.get_block_time(slot).unwrap(); @@ -825,3 +870,362 @@ pub fn update_y(rpc_client: &RpcClient, signer: &dyn Signer, y: u64) { process_transaction(rpc_client, &[instruction], &[signer]).unwrap(); } + +pub fn get_all_accounts(rpc_client: &RpcClient) { + let data: Vec<(Pubkey, DynamicPositionArrayAccount, Pubkey, Pubkey)> = rpc_client + .get_program_accounts_with_config( + &staking::ID, + RpcProgramAccountsConfig { + filters: Some(vec![RpcFilterType::Memcmp(Memcmp::new( + 0, + MemcmpEncodedBytes::Bytes(PositionData::discriminator().to_vec()), + ))]), + account_config: RpcAccountInfoConfig { + encoding: Some(UiAccountEncoding::Base64Zstd), + data_slice: None, + commitment: None, + min_context_slot: None, + }, + with_context: None, + }, + ) + .unwrap() + .into_iter() + .map(|(pubkey, account)| { + ( + pubkey, + DynamicPositionArrayAccount { + key: pubkey, + lamports: account.lamports, + data: account.data.clone(), + }, + get_stake_account_metadata_address(pubkey), + get_stake_account_custody_address(pubkey), + ) + }) + .collect::>(); + + let metadata_accounts_data = rpc_client + .get_program_accounts_with_config( + &staking::ID, + RpcProgramAccountsConfig { + filters: Some(vec![RpcFilterType::Memcmp(Memcmp::new( + 0, + MemcmpEncodedBytes::Bytes(StakeAccountMetadataV2::discriminator().to_vec()), + ))]), + account_config: RpcAccountInfoConfig { + encoding: Some(UiAccountEncoding::Base64Zstd), + data_slice: None, + commitment: None, + min_context_slot: None, + }, + with_context: None, + }, + ) + .unwrap() + .into_iter() + .map(|(pubkey, account)| { + ( + pubkey, + StakeAccountMetadataV2::try_deserialize(&mut account.data.as_slice()).unwrap(), + ) + }) + .collect::>(); + + let config = GlobalConfig::try_deserialize( + &mut rpc_client + .get_account_data(&get_config_address()) + .unwrap() + .as_slice(), + ) + .unwrap(); + let current_time = get_current_time(rpc_client); + + let locked_metadata_account_data: HashMap = metadata_accounts_data + .iter() + .map(|(pubkey, metadata)| { + ( + *pubkey, + metadata + .lock + .get_unvested_balance(current_time, config.pyth_token_list_time) + .unwrap(), + ) + }) + .collect::>(); + + let data = data + .into_iter() + .map(|(pubkey, account, metadata_pubkey, custody_pubkey)| { + ( + pubkey, + account, + metadata_pubkey, + custody_pubkey, + *locked_metadata_account_data.get(&custody_pubkey).unwrap(), + ) + }) + .collect::>(); + + + let locked_token_accounts_pubkeys = data + .iter() + .filter(|(_, _, _, _, locked_amount)| *locked_amount > 0u64) + .map(|(_, _, _, token_account_pubkey, _)| *token_account_pubkey) + .collect::>(); + + let mut actual_amounts: HashMap = HashMap::new(); + for chunk in locked_token_accounts_pubkeys.chunks(100) { + rpc_client + .get_multiple_accounts(chunk) + .unwrap() + .into_iter() + .enumerate() + .for_each(|(index, account)| { + actual_amounts.insert( + chunk[index], + TokenAccount::try_deserialize(&mut account.unwrap().data.as_slice()) + .unwrap() + .amount, + ); + }); + } + + let data = data + .into_iter() + .map( + |(pubkey, account, metadata_pubkey, custody_pubkey, locked_amount)| { + ( + pubkey, + account, + metadata_pubkey, + custody_pubkey, + min( + locked_amount, + *actual_amounts.get(&custody_pubkey).unwrap_or(&0u64), + ), + ) + }, + ) + .collect::>(); + + let current_epoch = get_current_epoch(rpc_client); + let data = data + .into_iter() + .map( + |(pubkey, mut account, metadata_pubkey, custody_pubkey, locked_amount)| { + let dynamic_position_array = account.to_dynamic_position_array(); + let owner = dynamic_position_array.owner().unwrap(); + let staked_in_governance = compute_voter_weight( + &dynamic_position_array, + current_epoch, + MAX_VOTER_WEIGHT, + MAX_VOTER_WEIGHT, + ) + .unwrap(); + + let staked_in_ois = { + let mut amount = 0u64; + for i in 0..dynamic_position_array.get_position_capacity() { + if let Some(position) = dynamic_position_array.read_position(i).unwrap() { + match position.get_current_position(current_epoch).unwrap() { + PositionState::LOCKED | PositionState::PREUNLOCKING => { + if !position.is_voting() { + amount += position.amount; + } + } + _ => {} + } + } + } + amount + }; + ( + pubkey, + metadata_pubkey, + custody_pubkey, + owner, + locked_amount, + staked_in_governance, + staked_in_ois, + ) + }, + ) + .collect::>(); + + use std::{ + fs::File, + io::{ + BufWriter, + Write, + }, + }; + + let file = File::create("output.csv").unwrap(); + let mut writer = BufWriter::new(file); + + // Write the header + writeln!(writer, "pubkey,account,metadata_pubkey,custody_pubkey,owner,locked_amount,staked_in_governance,staked_in_ois").unwrap(); + // Write the data + for ( + pubkey, + metadata_pubkey, + custody_pubkey, + owner, + locked_amount, + staked_in_governance, + staked_in_ois, + ) in data + { + writeln!( + writer, + "{},{},{},{},{},{},{}", + pubkey, + metadata_pubkey, + custody_pubkey, + owner, + locked_amount, + staked_in_governance, + staked_in_ois + ) + .unwrap(); + } + + + // let current_epoch = get_current_epoch(rpc_client); + + // let governance_stake_amounts = position_accounts.iter().enumerate().map(|(index, (pubkey, + // account))| { let mut account = DynamicPositionArrayAccount{ + // key: *pubkey, + // lamports: account.lamports, + // data: account.data.clone(), + // }; + // let dynamic_position_array = account.to_dynamic_position_array(); + + // println!("Account {}", index); + + + // compute_voter_weight( + // &dynamic_position_array, + // current_epoch, + // MAX_VOTER_WEIGHT, + // MAX_VOTER_WEIGHT + // ).unwrap() + // }).collect::>(); + + // let ois_stake_amounts = position_accounts.iter().enumerate().map(|(index, (pubkey, account))| + // { let mut account = DynamicPositionArrayAccount{ + // key: *pubkey, + // lamports: account.lamports, + // data: account.data.clone(), + // }; + // let dynamic_position_array = account.to_dynamic_position_array(); + + // let mut raw_voter_weight = 0u64; + // for i in 0..dynamic_position_array.get_position_capacity() { + // if let Some(position) = dynamic_position_array.read_position(i).unwrap(){ + // match position.get_current_position(current_epoch).unwrap() { + // PositionState::LOCKED | PositionState::PREUNLOCKING => { + // if !position.is_voting() { + // // position.amount is trusted, so I don't think this can overflow, + // // but still probably better to use checked math + // raw_voter_weight = raw_voter_weight + position.amount; + // } + // } + // _ => {} + // } + // } + // } + // raw_voter_weight + // }).collect::>(); + + // let total_capacity: u64 = governance_stake_amounts.iter().sum(); + // println!("Total capacity: {}", total_capacity); + // println!("Total OIS capacity: {}", ois_stake_amounts.iter().sum::()); + + + // let mut frequencies = std::collections::HashMap::new(); + // for capacity in &position_account_capacities { + // *frequencies.entry(*capacity).or_insert(0) += 1; + // } + + // let mut sorted_frequencies: Vec<_> = frequencies.iter().collect(); + // sorted_frequencies.sort_by_key(|&(capacity, _)| capacity); + // for (capacity, count) in sorted_frequencies { + // println!("{}: {}", capacity, count); + // } + + + let stake_account_metadata_accounts = rpc_client + .get_program_accounts_with_config( + &staking::ID, + RpcProgramAccountsConfig { + filters: Some(vec![RpcFilterType::Memcmp(Memcmp::new( + 0, + MemcmpEncodedBytes::Bytes(StakeAccountMetadataV2::discriminator().to_vec()), + ))]), + account_config: RpcAccountInfoConfig { + encoding: Some(UiAccountEncoding::Base64Zstd), + data_slice: None, + commitment: None, + min_context_slot: None, + }, + with_context: None, + }, + ) + .unwrap() + .into_iter() + .map(|(pubkey, account)| { + ( + pubkey, + StakeAccountMetadataV2::try_deserialize(&mut account.data.as_slice()).unwrap(), + ) + }) + .collect::>(); + + let stake_account_metadata_accounts = stake_account_metadata_accounts + .iter() + .filter(|(_, metadata)| !matches!(metadata.lock, VestingSchedule::FullyVested)) + .collect::>(); + println!("{}", stake_account_metadata_accounts.len()); + + let mut custody_accounts: Vec = stake_account_metadata_accounts + .iter() + .map(|(pubkey, _)| get_stake_account_custody_address(*pubkey)) + .collect::>(); + + let current_time = get_current_time(rpc_client); + let stake_account_locked_amount = stake_account_metadata_accounts + .iter() + .map(|(pubkey, metadata)| { + metadata + .lock + .get_unvested_balance(current_time, config.pyth_token_list_time) + .unwrap() + }) + .collect::>(); + + println!("{:?}", stake_account_locked_amount.iter().sum::()); + + let mut token_accounts: Vec = Vec::new(); + for chunk in custody_accounts.chunks(100) { + let batch = rpc_client + .get_multiple_accounts(&chunk) + .unwrap() + .into_iter() + .map(|account| { + TokenAccount::try_deserialize(&mut account.unwrap().data.as_slice()).unwrap() + }) + .collect::>(); + token_accounts.extend_from_slice(batch.as_slice()); + } + + + let custody_accounts = rpc_client.get_token_largest_accounts( + &Pubkey::from_str("HZ1JovNiVvGrGNiiYvEozEVgZ58xaU3RKwX8eACQBCt3").unwrap(), + ); + + // println!("{:?}", positio); + // println!("{:?}", stake_account_metadata_accounts.unwrap().len()); + println!("{:?}", custody_accounts.unwrap().len()); +} diff --git a/staking/cli/src/main.rs b/staking/cli/src/main.rs index 5c6a0cc8..c42d783c 100644 --- a/staking/cli/src/main.rs +++ b/staking/cli/src/main.rs @@ -11,6 +11,7 @@ use { close_publisher_caps, create_slash_event, fetch_publisher_caps_and_advance, + get_all_accounts, initialize_pool, initialize_reward_custody, set_publisher_stake_account, @@ -93,5 +94,8 @@ fn main() { Action::ClosePublisherCaps { publisher_caps } => { close_publisher_caps(&rpc_client, keypair.as_ref(), publisher_caps) } + Action::GetAllAccounts {} => { + get_all_accounts(&rpc_client); + } } } diff --git a/staking/programs/staking/src/lib.rs b/staking/programs/staking/src/lib.rs index fc3226a4..c2412cea 100644 --- a/staking/programs/staking/src/lib.rs +++ b/staking/programs/staking/src/lib.rs @@ -41,7 +41,7 @@ use { pub mod context; pub mod error; pub mod state; -mod utils; +pub mod utils; #[cfg(feature = "wasm")] pub mod wasm; diff --git a/staking/programs/staking/src/utils/voter_weight.rs b/staking/programs/staking/src/utils/voter_weight.rs index 5b14920b..1695514d 100644 --- a/staking/programs/staking/src/utils/voter_weight.rs +++ b/staking/programs/staking/src/utils/voter_weight.rs @@ -24,7 +24,7 @@ pub fn compute_voter_weight( if position.is_voting() { // position.amount is trusted, so I don't think this can overflow, // but still probably better to use checked math - raw_voter_weight = raw_voter_weight.checked_add(position.amount).unwrap(); + raw_voter_weight = raw_voter_weight + position.amount; } } _ => {}