From dc6b82c1178b3339a366a99ea5e95c41d4353603 Mon Sep 17 00:00:00 2001 From: FroVolod Date: Fri, 21 Jun 2024 17:35:45 +0300 Subject: [PATCH] feat: Added loading indicators for "accounts" group commands (#352) --- .../fund_myself_create_account/sign_as/mod.rs | 72 +++--- .../view_storage_balance.rs | 51 ++-- .../profile_args_type/json_args.rs | 2 +- .../profile_args_type/mod.rs | 2 +- .../account/update_social_profile/sign_as.rs | 227 +++++++++++------- src/common.rs | 1 + 6 files changed, 210 insertions(+), 145 deletions(-) diff --git a/src/commands/account/create_account/fund_myself_create_account/sign_as/mod.rs b/src/commands/account/create_account/fund_myself_create_account/sign_as/mod.rs index 28c12e144..db17e63e8 100644 --- a/src/commands/account/create_account/fund_myself_create_account/sign_as/mod.rs +++ b/src/commands/account/create_account/fund_myself_create_account/sign_as/mod.rs @@ -178,49 +178,43 @@ impl SignerAccountId { } } +#[tracing::instrument(name = "Validation new account_id ...", skip_all)] fn validate_new_account_id( network_config: &crate::config::NetworkConfig, account_id: &near_primitives::types::AccountId, ) -> crate::CliResult { - for _ in 0..3 { - let account_state = - tokio::runtime::Runtime::new() - .unwrap() - .block_on(crate::common::get_account_state( - network_config, - account_id, - near_primitives::types::BlockReference::latest(), - )); - if let Err(near_jsonrpc_client::errors::JsonRpcError::TransportError( - near_jsonrpc_client::errors::RpcTransportError::SendError(_), - )) = account_state - { - eprintln!("Transport error.\nPlease wait. The next try to send this query is happening right now ..."); - std::thread::sleep(std::time::Duration::from_millis(100)) - } else { - match account_state { - Ok(_) => { - return color_eyre::eyre::Result::Err(color_eyre::eyre::eyre!( - "\nAccount <{}> already exists in network <{}>. Therefore, it is not possible to create an account with this name.", - account_id, - network_config.network_name - )); - } - Err(near_jsonrpc_client::errors::JsonRpcError::ServerError( - near_jsonrpc_client::errors::JsonRpcServerError::HandlerError( - near_jsonrpc_primitives::types::query::RpcQueryError::UnknownAccount { - .. - }, - ), - )) => { - return Ok(()); - } - Err(err) => { - return color_eyre::eyre::Result::Err(color_eyre::eyre::eyre!(err.to_string())) - } - } + let account_state = + tokio::runtime::Runtime::new() + .unwrap() + .block_on(crate::common::get_account_state( + network_config, + account_id, + near_primitives::types::BlockReference::latest(), + )); + match account_state { + Ok(_) => { + color_eyre::eyre::Result::Err(color_eyre::eyre::eyre!( + "\nAccount <{}> already exists in network <{}>. Therefore, it is not possible to create an account with this name.", + account_id, + network_config.network_name + )) + } + Err(near_jsonrpc_client::errors::JsonRpcError::ServerError( + near_jsonrpc_client::errors::JsonRpcServerError::HandlerError( + near_jsonrpc_primitives::types::query::RpcQueryError::UnknownAccount { + .. + }, + ), + )) => { + eprintln!("\nServer error.\nIt is currently possible to continue creating an account offline.\nYou can sign and send the created transaction later."); + Ok(()) + } + Err(near_jsonrpc_client::errors::JsonRpcError::TransportError(_)) => { + eprintln!("\nTransport error.\nIt is currently possible to continue creating an account offline.\nYou can sign and send the created transaction later."); + Ok(()) + } + Err(err) => { + color_eyre::eyre::Result::Err(color_eyre::eyre::eyre!(err.to_string())) } } - eprintln!("\nTransport error.\nIt is currently possible to continue creating an account offline.\nYou can sign and send the created transaction later."); - Ok(()) } diff --git a/src/commands/account/storage_management/view_storage_balance.rs b/src/commands/account/storage_management/view_storage_balance.rs index bbbc46abd..f490ca728 100644 --- a/src/commands/account/storage_management/view_storage_balance.rs +++ b/src/commands/account/storage_management/view_storage_balance.rs @@ -1,5 +1,7 @@ -use crate::common::{CallResultExt, JsonRpcClientExt}; use color_eyre::eyre::WrapErr; +use tracing_indicatif::span_ext::IndicatifSpanExt; + +use crate::common::{CallResultExt, JsonRpcClientExt}; const STORAGE_COST_PER_BYTE: u128 = 10u128.pow(19); @@ -30,24 +32,7 @@ impl AccountContext { move |network_config, block_reference| { let contract_account_id = (previous_context.get_contract_account_id)(network_config)?; - let storage_balance = network_config - .json_rpc_client() - .blocking_call_view_function( - &contract_account_id, - "storage_balance_of", - serde_json::to_vec(&serde_json::json!({ - "account_id": account_id.to_string(), - }))?, - block_reference.clone(), - ) - .wrap_err_with(|| { - format!("Failed to fetch query for view method: 'storage_balance_of' (contract <{}> on network <{}>)", - contract_account_id, - network_config.network_name - ) - })? - .parse_result_from_json::() - .wrap_err("Failed to parse return value of view function call for StorageBalance.")?; + let storage_balance = get_storage_balance(network_config, &contract_account_id, &account_id, block_reference)?; eprintln!("storage balance for <{account_id}>:"); eprintln!(" {:<13} {:>10} ({} [{:>28} yoctoNEAR])", "available:", @@ -90,3 +75,31 @@ impl Account { ) } } + +#[tracing::instrument(name = "Getting storage balance for", skip_all)] +fn get_storage_balance( + network_config: &crate::config::NetworkConfig, + contract_account_id: &near_primitives::types::AccountId, + account_id: &crate::types::account_id::AccountId, + block_reference: &near_primitives::types::BlockReference, +) -> color_eyre::eyre::Result { + tracing::Span::current().pb_set_message(account_id.as_ref()); + network_config + .json_rpc_client() + .blocking_call_view_function( + contract_account_id, + "storage_balance_of", + serde_json::to_vec(&serde_json::json!({ + "account_id": account_id.to_string(), + }))?, + block_reference.clone(), + ) + .wrap_err_with(|| { + format!("Failed to fetch query for view method: 'storage_balance_of' (contract <{}> on network <{}>)", + contract_account_id, + network_config.network_name + ) + })? + .parse_result_from_json::() + .wrap_err("Failed to parse return value of view function call for StorageBalance.") +} diff --git a/src/commands/account/update_social_profile/profile_args_type/json_args.rs b/src/commands/account/update_social_profile/profile_args_type/json_args.rs index dccf1eeb5..2038d5f27 100644 --- a/src/commands/account/update_social_profile/profile_args_type/json_args.rs +++ b/src/commands/account/update_social_profile/profile_args_type/json_args.rs @@ -2,7 +2,7 @@ #[interactive_clap(input_context = super::super::UpdateSocialProfileContext)] #[interactive_clap(output_context = JsonArgsContext)] pub struct JsonArgs { - /// Enter valid JSON arguments (e.g. {\"token_id\": \"42\"})": + /// Enter valid JSON arguments (e.g. {"name": "NEAR", "description": "NEAR is fun"}): data: crate::types::json::Json, #[interactive_clap(named_arg)] /// Specify signer account ID diff --git a/src/commands/account/update_social_profile/profile_args_type/mod.rs b/src/commands/account/update_social_profile/profile_args_type/mod.rs index 552eb66c9..e038a4227 100644 --- a/src/commands/account/update_social_profile/profile_args_type/mod.rs +++ b/src/commands/account/update_social_profile/profile_args_type/mod.rs @@ -14,7 +14,7 @@ pub enum ProfileArgsType { /// Interactive input of arguments Manually(self::manually::Manually), #[strum_discriminants(strum( - message = "json-args - Valid JSON arguments (e.g. {\"token_id\": \"42\"})" + message = "json-args - Valid JSON arguments (e.g. {\"name\": \"NEAR\", \"description\": \"NEAR is fun\"})" ))] /// Valid JSON arguments (e.g. {"token_id": "42"}) JsonArgs(self::json_args::JsonArgs), diff --git a/src/commands/account/update_social_profile/sign_as.rs b/src/commands/account/update_social_profile/sign_as.rs index a167fcd0f..c3b8645ea 100644 --- a/src/commands/account/update_social_profile/sign_as.rs +++ b/src/commands/account/update_social_profile/sign_as.rs @@ -44,83 +44,12 @@ impl From for crate::commands::ActionContext { fn from(item: SignerContext) -> Self { let account_id = item.account_id.clone(); let signer_id = item.signer_account_id.clone(); - let data = item.data.clone(); + let data = item.data; let get_prepopulated_transaction_after_getting_network_callback: crate::commands::GetPrepopulatedTransactionAfterGettingNetworkCallback = Arc::new({ move |network_config| { - let contract_account_id = - network_config.get_near_social_account_id_from_network()?; - let mut prepopulated_transaction = crate::commands::PrepopulatedTransaction { - signer_id: signer_id.clone(), - receiver_id: contract_account_id.clone(), - actions: vec![], - }; - - let json_rpc_client = network_config.json_rpc_client(); - - let local_profile: serde_json::Value = serde_json::from_slice(&data)?; - let remote_profile = match json_rpc_client - .blocking_call_view_function( - &contract_account_id, - "get", - serde_json::to_vec(&serde_json::json!({ - "keys": vec![format!("{account_id}/profile/**")], - }))?, - near_primitives::types::Finality::Final.into(), - ) - .wrap_err_with(|| { - format!("Failed to fetch query for view method: 'get {account_id}/profile/**' (contract <{}> on network <{}>)", - contract_account_id, - network_config.network_name - ) - })? - .parse_result_from_json::() - .wrap_err_with(|| { - format!("Failed to parse view function call return value for {account_id}/profile.") - })? - .accounts - .get(&account_id) { - Some(account_profile) => serde_json::to_value(account_profile.profile.clone())?, - None => serde_json::Value::Null - }; - - let deposit = tokio::runtime::Runtime::new().unwrap().block_on( - near_socialdb_client::required_deposit( - &json_rpc_client, - &contract_account_id, - &account_id, - &local_profile, - Some(&remote_profile), - ), - )?; - - let new_social_db_state = - near_socialdb_client::types::socialdb_types::SocialDb { - accounts: HashMap::from([( - account_id.clone(), - near_socialdb_client::types::socialdb_types::AccountProfile { - profile: serde_json::from_value(local_profile)?, - }, - )]), - }; - - let args = serde_json::to_string(&super::TransactionFunctionArgs { - data: new_social_db_state, - })? - .into_bytes(); - - prepopulated_transaction.actions = - vec![near_primitives::transaction::Action::FunctionCall( - Box::new(near_primitives::transaction::FunctionCallAction { - method_name: "set".to_string(), - args, - gas: crate::common::NearGas::from_tgas(300).as_gas(), - deposit: deposit.as_yoctonear(), - }), - )]; - - Ok(prepopulated_transaction) + get_prepopulated_transaction(network_config, &account_id, &signer_id, &data) } }); @@ -133,18 +62,16 @@ impl From for crate::commands::ActionContext { if let near_primitives::transaction::Action::FunctionCall(action) = &mut prepopulated_unsigned_transaction.actions[0] { - action.deposit = tokio::runtime::Runtime::new() - .unwrap() - .block_on(near_socialdb_client::get_deposit( - &json_rpc_client, - &signer_account_id, - &prepopulated_unsigned_transaction.public_key, - &account_id, - "profile", - &prepopulated_unsigned_transaction.receiver_id, - near_token::NearToken::from_yoctonear(action.deposit), - ))? - .as_yoctonear(); + action.deposit = get_deposit( + &json_rpc_client, + &signer_account_id, + &prepopulated_unsigned_transaction.public_key, + &account_id, + "profile", + &prepopulated_unsigned_transaction.receiver_id, + near_token::NearToken::from_yoctonear(action.deposit), + )? + .as_yoctonear(); Ok(()) } else { color_eyre::eyre::bail!("Unexpected action to change components",); @@ -216,3 +143,133 @@ impl Signer { } } } + +#[tracing::instrument( + name = "Creating a pre-populated transaction for signature ...", + skip_all +)] +fn get_prepopulated_transaction( + network_config: &crate::config::NetworkConfig, + account_id: &near_primitives::types::AccountId, + signer_id: &near_primitives::types::AccountId, + data: &[u8], +) -> color_eyre::eyre::Result { + let contract_account_id = network_config.get_near_social_account_id_from_network()?; + let mut prepopulated_transaction = crate::commands::PrepopulatedTransaction { + signer_id: signer_id.clone(), + receiver_id: contract_account_id.clone(), + actions: vec![], + }; + + let local_profile: serde_json::Value = serde_json::from_slice(data)?; + let remote_profile = + get_remote_profile(network_config, &contract_account_id, account_id.clone())?; + + let deposit = required_deposit( + &network_config.json_rpc_client(), + &contract_account_id, + account_id, + &local_profile, + Some(&remote_profile), + )?; + + let new_social_db_state = near_socialdb_client::types::socialdb_types::SocialDb { + accounts: HashMap::from([( + account_id.clone(), + near_socialdb_client::types::socialdb_types::AccountProfile { + profile: serde_json::from_value(local_profile)?, + }, + )]), + }; + + let args = serde_json::to_string(&super::TransactionFunctionArgs { + data: new_social_db_state, + })? + .into_bytes(); + + prepopulated_transaction.actions = vec![near_primitives::transaction::Action::FunctionCall( + Box::new(near_primitives::transaction::FunctionCallAction { + method_name: "set".to_string(), + args, + gas: crate::common::NearGas::from_tgas(300).as_gas(), + deposit: deposit.as_yoctonear(), + }), + )]; + + Ok(prepopulated_transaction) +} + +#[tracing::instrument(name = "Calculation of the required deposit ...", skip_all)] +fn required_deposit( + json_rpc_client: &near_jsonrpc_client::JsonRpcClient, + near_social_account_id: &near_primitives::types::AccountId, + account_id: &near_primitives::types::AccountId, + data: &serde_json::Value, + prev_data: Option<&serde_json::Value>, +) -> color_eyre::eyre::Result { + tokio::runtime::Runtime::new() + .unwrap() + .block_on(near_socialdb_client::required_deposit( + json_rpc_client, + near_social_account_id, + account_id, + data, + prev_data, + )) +} + +#[tracing::instrument(name = "Update the required deposit ...", skip_all)] +fn get_deposit( + json_rpc_client: &near_jsonrpc_client::JsonRpcClient, + signer_account_id: &near_primitives::types::AccountId, + signer_public_key: &near_crypto::PublicKey, + account_id: &near_primitives::types::AccountId, + key: &str, + near_social_account_id: &near_primitives::types::AccountId, + required_deposit: near_token::NearToken, +) -> color_eyre::eyre::Result { + tokio::runtime::Runtime::new() + .unwrap() + .block_on(near_socialdb_client::get_deposit( + json_rpc_client, + signer_account_id, + signer_public_key, + account_id, + key, + near_social_account_id, + required_deposit, + )) +} + +#[tracing::instrument(name = "Getting data about a remote profile ...", skip_all)] +fn get_remote_profile( + network_config: &crate::config::NetworkConfig, + near_social_account_id: &near_primitives::types::AccountId, + account_id: near_primitives::types::AccountId, +) -> color_eyre::eyre::Result { + match network_config + .json_rpc_client() + .blocking_call_view_function( + near_social_account_id, + "get", + serde_json::to_vec(&serde_json::json!({ + "keys": vec![format!("{account_id}/profile/**")], + }))?, + near_primitives::types::Finality::Final.into(), + ) + .wrap_err_with(|| { + format!("Failed to fetch query for view method: 'get {account_id}/profile/**' (contract <{}> on network <{}>)", + near_social_account_id, + network_config.network_name + ) + })? + .parse_result_from_json::() + .wrap_err_with(|| { + format!("Failed to parse view function call return value for {account_id}/profile.") + })? + .accounts + .get(&account_id) { + Some(account_profile) => Ok(serde_json::to_value(account_profile.profile.clone())?), + None => Ok(serde_json::Value::Null) + } +} diff --git a/src/common.rs b/src/common.rs index f8515a72b..6dfcda39e 100644 --- a/src/common.rs +++ b/src/common.rs @@ -213,6 +213,7 @@ pub async fn get_account_transfer_allowance( }) } +#[tracing::instrument(name = "Account access key verification ...", skip_all)] pub fn verify_account_access_key( account_id: near_primitives::types::AccountId, public_key: near_crypto::PublicKey,