From 990d7e05903b338d6baf583a8fb3945801efba66 Mon Sep 17 00:00:00 2001 From: Brooks Prumo Date: Fri, 13 May 2022 12:54:03 -0500 Subject: [PATCH] Add get_stake_minimum_delegation() to rpc_client --- client/src/nonblocking/rpc_client.rs | 39 ++++++++++++++++++++++++- client/src/rpc_client.rs | 43 +++++++++++++++++++++++++++- 2 files changed, 80 insertions(+), 2 deletions(-) diff --git a/client/src/nonblocking/rpc_client.rs b/client/src/nonblocking/rpc_client.rs index 5980beb1f0bb4d..32b458b09e9cbe 100644 --- a/client/src/nonblocking/rpc_client.rs +++ b/client/src/nonblocking/rpc_client.rs @@ -39,10 +39,11 @@ use { epoch_schedule::EpochSchedule, fee_calculator::{FeeCalculator, FeeRateGovernor}, hash::Hash, + instruction::InstructionError, message::Message, pubkey::Pubkey, signature::Signature, - transaction::{self, uses_durable_nonce, Transaction}, + transaction::{self, uses_durable_nonce, Transaction, TransactionError}, }, solana_transaction_status::{ EncodedConfirmedBlock, EncodedConfirmedTransactionWithStatusMeta, TransactionStatus, @@ -4507,6 +4508,42 @@ impl RpcClient { parse_keyed_accounts(accounts, RpcRequest::GetProgramAccounts) } + /// Returns the stake minimum delegation, in lamports. + pub async fn get_stake_minimum_delegation(&self) -> ClientResult { + let instruction = solana_sdk::stake::instruction::get_minimum_delegation(); + let payer = None; + let transaction = Transaction::new_with_payer(&[instruction], payer); + let response = self.simulate_transaction(&transaction).await?; + let return_data = response + .value + .return_data + .ok_or_else(|| ClientErrorKind::Custom("return data was empty".to_string()))?; + let (program_id, data) = (return_data.program_id, return_data.data); + if Pubkey::from_str(&program_id) != Ok(solana_sdk::stake::program::id()) { + return Err(TransactionError::InstructionError( + 0, + InstructionError::IncorrectProgramId, + ) + .into()); + } + if data.1 != ReturnDataEncoding::Base64 { + return Err( + ClientErrorKind::Custom("return data encoding is invalid".to_string()).into(), + ); + } + let data = base64::decode(data.0).map_err(|err| { + ClientErrorKind::Custom(format!("failed to decode return data: {}", err)) + })?; + let minimum_delegation = u64::from_le_bytes(data.try_into().map_err(|data: Vec| { + ClientErrorKind::Custom(format!( + "return data cannot be represented as a u64: expected size: {}, actual size: {}", + std::mem::size_of::(), + data.len() + )) + })?); + Ok(minimum_delegation) + } + /// Request the transaction count. pub async fn get_transaction_count(&self) -> ClientResult { self.get_transaction_count_with_commitment(self.commitment()) diff --git a/client/src/rpc_client.rs b/client/src/rpc_client.rs index 2985c0ca757aea..c0e53b3b3f9cf8 100644 --- a/client/src/rpc_client.rs +++ b/client/src/rpc_client.rs @@ -3711,6 +3711,11 @@ impl RpcClient { ) } + /// Returns the stake minimum delegation, in lamports. + pub fn get_stake_minimum_delegation(&self) -> ClientResult { + self.invoke(self.rpc_client.get_stake_minimum_delegation()) + } + /// Request the transaction count. pub fn get_transaction_count(&self) -> ClientResult { self.invoke(self.rpc_client.get_transaction_count()) @@ -4097,7 +4102,7 @@ mod tests { system_transaction, transaction::TransactionError, }, - std::{io, thread}, + std::{collections::HashMap, io, thread}, }; #[test] @@ -4313,4 +4318,40 @@ mod tests { let is_err = rpc_client.get_latest_blockhash().is_err(); assert!(is_err); } + + #[test] + fn test_get_stake_minimum_delegation() { + let expected_minimum_delegation: u64 = 123_456_789; + let rpc_client = { + let mocks = { + let rpc_response = { + let program_id = solana_sdk::stake::program::id().to_string(); + let data = ( + base64::encode(expected_minimum_delegation.to_le_bytes()), + ReturnDataEncoding::Base64, + ); + serde_json::to_value(Response { + context: RpcResponseContext { slot: 1 }, + value: RpcSimulateTransactionResult { + err: None, + logs: None, + accounts: None, + units_consumed: None, + return_data: Some(RpcTransactionReturnData { program_id, data }), + }, + }) + .unwrap() + }; + let mut mocks = HashMap::new(); + mocks.insert(RpcRequest::SimulateTransaction, rpc_response); + mocks + }; + RpcClient::new_mock_with_mocks("succeeds".to_string(), mocks) + }; + + let client_result = rpc_client.get_stake_minimum_delegation(); + assert!(client_result.is_ok()); + let actual_minimum_delegation = client_result.unwrap(); + assert_eq!(actual_minimum_delegation, expected_minimum_delegation); + } }