From ea9bd09e4d1489653c3c25902a94d01e4bfa6fbc Mon Sep 17 00:00:00 2001 From: FroVolod Date: Fri, 1 Sep 2023 12:35:43 +0300 Subject: [PATCH 01/17] start --- src/commands/contract/mod.rs | 6 +- src/commands/contract/view_state/mod.rs | 81 +++++++++++++++++++++++++ 2 files changed, 86 insertions(+), 1 deletion(-) create mode 100644 src/commands/contract/view_state/mod.rs diff --git a/src/commands/contract/mod.rs b/src/commands/contract/mod.rs index b2f6a6b98..3617759c9 100644 --- a/src/commands/contract/mod.rs +++ b/src/commands/contract/mod.rs @@ -3,6 +3,7 @@ use strum::{EnumDiscriminants, EnumIter, EnumMessage}; pub mod call_function; mod deploy; mod download_wasm; +mod view_state; #[derive(Debug, Clone, interactive_clap::InteractiveClap)] #[interactive_clap(context = crate::GlobalContext)] @@ -15,7 +16,7 @@ pub struct ContractCommands { #[interactive_clap(context = crate::GlobalContext)] #[strum_discriminants(derive(EnumMessage, EnumIter))] #[non_exhaustive] -/// Сhoose action for account: +/// Choose a contract action: pub enum ContractActions { #[strum_discriminants(strum( message = "call-function - Execute function (contract method)" @@ -28,4 +29,7 @@ pub enum ContractActions { #[strum_discriminants(strum(message = "download-wasm - Download wasm"))] /// Download wasm DownloadWasm(self::download_wasm::ContractAccount), + #[strum_discriminants(strum(message = "view-state - View contract state"))] + /// View contract state + ViewState(self::view_state::ViewState), } diff --git a/src/commands/contract/view_state/mod.rs b/src/commands/contract/view_state/mod.rs new file mode 100644 index 000000000..0404f8ba7 --- /dev/null +++ b/src/commands/contract/view_state/mod.rs @@ -0,0 +1,81 @@ +use color_eyre::eyre::Context; + +use crate::common::JsonRpcClientExt; + +#[derive(Debug, Clone, interactive_clap::InteractiveClap)] +#[interactive_clap(input_context = crate::GlobalContext)] +#[interactive_clap(output_context = ViewStateContext)] +pub struct ViewState { + #[interactive_clap(skip_default_input_arg)] + /// What is the contract account ID? + contract_account_id: crate::types::account_id::AccountId, + #[interactive_clap(named_arg)] + /// Select network + network_config: crate::network_view_at_block::NetworkViewAtBlockArgs, +} + +#[derive(Clone)] +pub struct ViewStateContext(crate::network_view_at_block::ArgsForViewContext); + +impl ViewStateContext { + pub fn from_previous_context( + previous_context: crate::GlobalContext, + scope: &::InteractiveClapContextScope, + ) -> color_eyre::eyre::Result { + let on_after_getting_block_reference_callback: crate::network_view_at_block::OnAfterGettingBlockReferenceCallback = std::sync::Arc::new({ + let contract_account_id: near_primitives::types::AccountId = scope.contract_account_id.clone().into(); + + move |network_config, block_reference| { + let query_view_method_response = network_config + .json_rpc_client() + .blocking_call(near_jsonrpc_client::methods::query::RpcQueryRequest { + block_reference: block_reference.clone(), + request: near_primitives::views::QueryRequest::ViewState { + account_id: contract_account_id.clone(), + prefix: near_primitives::types::StoreKey::from(Vec::new()), + include_proof: false, + }, + }) + .wrap_err_with(|| format!("Failed to fetch query ViewState for <{contract_account_id}>"))?; + if let near_jsonrpc_primitives::types::query::QueryResponseKind::ViewState(result) = + query_view_method_response.kind + { + eprintln!( + "\nContract state (values):\n{:#?}\n", + &result.values + ); + eprintln!( + "\nContract state (proof):\n{:#?}\n", + &result.proof + ); + } else { + return Err(color_eyre::Report::msg("Error call result".to_string())); + }; + Ok(()) + } + }); + + Ok(Self(crate::network_view_at_block::ArgsForViewContext { + config: previous_context.config, + interacting_with_account_ids: vec![scope.contract_account_id.clone().into()], + on_after_getting_block_reference_callback, + })) + } +} + +impl From for crate::network_view_at_block::ArgsForViewContext { + fn from(item: ViewStateContext) -> Self { + item.0 + } +} + +impl ViewState { + pub fn input_contract_account_id( + context: &crate::GlobalContext, + ) -> color_eyre::eyre::Result> { + crate::common::input_non_signer_account_id_from_used_account_list( + &context.config.credentials_home_dir, + "What is the contract account ID?", + ) + } +} From 2b272ea2f3fa961fec82e6e0da19ccc37f758b9d Mon Sep 17 00:00:00 2001 From: FroVolod Date: Fri, 1 Sep 2023 12:38:52 +0300 Subject: [PATCH 02/17] clippy --- src/commands/contract/download_wasm/mod.rs | 2 +- src/commands/tokens/send_ft/mod.rs | 4 ++-- src/commands/transaction/sign_transaction/mod.rs | 4 ++-- src/commands/transaction/view_status/mod.rs | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/commands/contract/download_wasm/mod.rs b/src/commands/contract/download_wasm/mod.rs index c3aff3c0b..9c393aa1e 100644 --- a/src/commands/contract/download_wasm/mod.rs +++ b/src/commands/contract/download_wasm/mod.rs @@ -76,7 +76,7 @@ impl DownloadContractContext { .blocking_call(near_jsonrpc_client::methods::query::RpcQueryRequest { block_reference: block_reference.clone(), request: near_primitives::views::QueryRequest::ViewCode { - account_id: account_id.clone().into(), + account_id: account_id.clone(), }, }) .wrap_err_with(|| format!("Failed to fetch query ViewCode for <{}>", &account_id))?; diff --git a/src/commands/tokens/send_ft/mod.rs b/src/commands/tokens/send_ft/mod.rs index 17efee964..c74477360 100644 --- a/src/commands/tokens/send_ft/mod.rs +++ b/src/commands/tokens/send_ft/mod.rs @@ -66,8 +66,8 @@ impl From for crate::commands::ActionContext { move |_network_config| { Ok(crate::commands::PrepopulatedTransaction { - signer_id: signer_account_id.clone().into(), - receiver_id: ft_contract_account_id.clone().into(), + signer_id: signer_account_id.clone(), + receiver_id: ft_contract_account_id.clone(), actions: vec![near_primitives::transaction::Action::FunctionCall( near_primitives::transaction::FunctionCallAction { method_name: "ft_transfer".to_string(), diff --git a/src/commands/transaction/sign_transaction/mod.rs b/src/commands/transaction/sign_transaction/mod.rs index 01575d0ff..5feba7312 100644 --- a/src/commands/transaction/sign_transaction/mod.rs +++ b/src/commands/transaction/sign_transaction/mod.rs @@ -34,8 +34,8 @@ impl SignTransactionContext { Ok(Self(crate::commands::ActionContext { global_context: previous_context, interacting_with_account_ids: vec![ - scope.unsigned_transaction.inner.signer_id.clone().into(), - scope.unsigned_transaction.inner.receiver_id.clone().into(), + scope.unsigned_transaction.inner.signer_id.clone(), + scope.unsigned_transaction.inner.receiver_id.clone(), ], on_after_getting_network_callback, on_before_signing_callback: std::sync::Arc::new( diff --git a/src/commands/transaction/view_status/mod.rs b/src/commands/transaction/view_status/mod.rs index a9f90be88..426557e8a 100644 --- a/src/commands/transaction/view_status/mod.rs +++ b/src/commands/transaction/view_status/mod.rs @@ -27,7 +27,7 @@ impl TransactionInfoContext { let on_after_getting_network_callback: crate::network::OnAfterGettingNetworkCallback = std::sync::Arc::new({ let signer_account_id = scope.signer_account_id.clone(); - let transaction_hash = scope.transaction_hash.clone(); + let transaction_hash = scope.transaction_hash; move |network_config| { let query_view_transaction_status = network_config From ca8268f5e7720d2838b526e7dc4583195a1baaf6 Mon Sep 17 00:00:00 2001 From: FroVolod Date: Fri, 1 Sep 2023 12:35:43 +0300 Subject: [PATCH 03/17] start --- src/commands/contract/mod.rs | 6 +- src/commands/contract/view_state/mod.rs | 81 +++++++++++++++++++++++++ 2 files changed, 86 insertions(+), 1 deletion(-) create mode 100644 src/commands/contract/view_state/mod.rs diff --git a/src/commands/contract/mod.rs b/src/commands/contract/mod.rs index b2f6a6b98..3617759c9 100644 --- a/src/commands/contract/mod.rs +++ b/src/commands/contract/mod.rs @@ -3,6 +3,7 @@ use strum::{EnumDiscriminants, EnumIter, EnumMessage}; pub mod call_function; mod deploy; mod download_wasm; +mod view_state; #[derive(Debug, Clone, interactive_clap::InteractiveClap)] #[interactive_clap(context = crate::GlobalContext)] @@ -15,7 +16,7 @@ pub struct ContractCommands { #[interactive_clap(context = crate::GlobalContext)] #[strum_discriminants(derive(EnumMessage, EnumIter))] #[non_exhaustive] -/// Сhoose action for account: +/// Choose a contract action: pub enum ContractActions { #[strum_discriminants(strum( message = "call-function - Execute function (contract method)" @@ -28,4 +29,7 @@ pub enum ContractActions { #[strum_discriminants(strum(message = "download-wasm - Download wasm"))] /// Download wasm DownloadWasm(self::download_wasm::ContractAccount), + #[strum_discriminants(strum(message = "view-state - View contract state"))] + /// View contract state + ViewState(self::view_state::ViewState), } diff --git a/src/commands/contract/view_state/mod.rs b/src/commands/contract/view_state/mod.rs new file mode 100644 index 000000000..0404f8ba7 --- /dev/null +++ b/src/commands/contract/view_state/mod.rs @@ -0,0 +1,81 @@ +use color_eyre::eyre::Context; + +use crate::common::JsonRpcClientExt; + +#[derive(Debug, Clone, interactive_clap::InteractiveClap)] +#[interactive_clap(input_context = crate::GlobalContext)] +#[interactive_clap(output_context = ViewStateContext)] +pub struct ViewState { + #[interactive_clap(skip_default_input_arg)] + /// What is the contract account ID? + contract_account_id: crate::types::account_id::AccountId, + #[interactive_clap(named_arg)] + /// Select network + network_config: crate::network_view_at_block::NetworkViewAtBlockArgs, +} + +#[derive(Clone)] +pub struct ViewStateContext(crate::network_view_at_block::ArgsForViewContext); + +impl ViewStateContext { + pub fn from_previous_context( + previous_context: crate::GlobalContext, + scope: &::InteractiveClapContextScope, + ) -> color_eyre::eyre::Result { + let on_after_getting_block_reference_callback: crate::network_view_at_block::OnAfterGettingBlockReferenceCallback = std::sync::Arc::new({ + let contract_account_id: near_primitives::types::AccountId = scope.contract_account_id.clone().into(); + + move |network_config, block_reference| { + let query_view_method_response = network_config + .json_rpc_client() + .blocking_call(near_jsonrpc_client::methods::query::RpcQueryRequest { + block_reference: block_reference.clone(), + request: near_primitives::views::QueryRequest::ViewState { + account_id: contract_account_id.clone(), + prefix: near_primitives::types::StoreKey::from(Vec::new()), + include_proof: false, + }, + }) + .wrap_err_with(|| format!("Failed to fetch query ViewState for <{contract_account_id}>"))?; + if let near_jsonrpc_primitives::types::query::QueryResponseKind::ViewState(result) = + query_view_method_response.kind + { + eprintln!( + "\nContract state (values):\n{:#?}\n", + &result.values + ); + eprintln!( + "\nContract state (proof):\n{:#?}\n", + &result.proof + ); + } else { + return Err(color_eyre::Report::msg("Error call result".to_string())); + }; + Ok(()) + } + }); + + Ok(Self(crate::network_view_at_block::ArgsForViewContext { + config: previous_context.config, + interacting_with_account_ids: vec![scope.contract_account_id.clone().into()], + on_after_getting_block_reference_callback, + })) + } +} + +impl From for crate::network_view_at_block::ArgsForViewContext { + fn from(item: ViewStateContext) -> Self { + item.0 + } +} + +impl ViewState { + pub fn input_contract_account_id( + context: &crate::GlobalContext, + ) -> color_eyre::eyre::Result> { + crate::common::input_non_signer_account_id_from_used_account_list( + &context.config.credentials_home_dir, + "What is the contract account ID?", + ) + } +} From 02632f5858c40bd42cc3b1a18d7d218eb79a8077 Mon Sep 17 00:00:00 2001 From: FroVolod Date: Tue, 5 Sep 2023 18:24:16 +0300 Subject: [PATCH 04/17] draft --- src/commands/contract/view_state/mod.rs | 47 +++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/src/commands/contract/view_state/mod.rs b/src/commands/contract/view_state/mod.rs index 0404f8ba7..aaab0f1c8 100644 --- a/src/commands/contract/view_state/mod.rs +++ b/src/commands/contract/view_state/mod.rs @@ -1,5 +1,9 @@ use color_eyre::eyre::Context; +use near_primitives::serialize::base64_display; +use prettytable::Table; + + use crate::common::JsonRpcClientExt; #[derive(Debug, Clone, interactive_clap::InteractiveClap)] @@ -40,14 +44,53 @@ impl ViewStateContext { if let near_jsonrpc_primitives::types::query::QueryResponseKind::ViewState(result) = query_view_method_response.kind { + eprintln!("Contract state (values):"); eprintln!( - "\nContract state (values):\n{:#?}\n", - &result.values + "{}", + // result.values[0] + // serde_json::to_string_pretty(&result.values[0].key)? + serde_json::to_string_pretty(&result.values)? + // base64_display(&result.values[0].key.as_slice()) ); + + + + for value in &result.values { + eprintln!("{}: {}", String::from_utf8_lossy(&value.key), String::from_utf8_lossy(&value.value)); + // // let qwe = serde_json::from_slice::(&result.values[0].key)?; + // eprintln!( + // "{}: {}", + // // result.values[0] + // // serde_json::to_string_pretty(&result.values[0].key)? + // // serde_json::to_string_pretty(value)? + // base64_display(&value.key.as_slice()), + // base64_display(&value.value.as_slice()) + // ); + } + + + let mut table = Table::new(); + table.set_titles(prettytable::row![Fg=>"key", "value"]); + for value in &result.values { + table.add_row(prettytable::row![ + Fg->String::from_utf8_lossy(&value.key), + String::from_utf8_lossy(&value.value) + ]); + + // eprintln!("{}: {}", String::from_utf8_lossy(&value.key), String::from_utf8_lossy(&value.value)); + } + table.set_format(*prettytable::format::consts::FORMAT_NO_COLSEP); + table.printstd(); + + + + + eprintln!( "\nContract state (proof):\n{:#?}\n", &result.proof ); + println!("{}", "'".to_string()); } else { return Err(color_eyre::Report::msg("Error call result".to_string())); }; From 0114e933c96dfcfe204bab4b3feea8b89cd1461d Mon Sep 17 00:00:00 2001 From: FroVolod Date: Wed, 6 Sep 2023 12:14:45 +0300 Subject: [PATCH 05/17] added AllKeys to view contract storage --- src/commands/contract/mod.rs | 8 +- src/commands/contract/view_state/mod.rs | 124 ------------------ .../view_storage/keys_to_view/all_keys.rs | 29 ++++ .../contract/view_storage/keys_to_view/mod.rs | 32 +++++ src/commands/contract/view_storage/mod.rs | 42 ++++++ .../view_storage/output_format/as_json.rs | 68 ++++++++++ .../view_storage/output_format/as_table.rs | 76 +++++++++++ .../view_storage/output_format/mod.rs | 17 +++ 8 files changed, 268 insertions(+), 128 deletions(-) delete mode 100644 src/commands/contract/view_state/mod.rs create mode 100644 src/commands/contract/view_storage/keys_to_view/all_keys.rs create mode 100644 src/commands/contract/view_storage/keys_to_view/mod.rs create mode 100644 src/commands/contract/view_storage/mod.rs create mode 100644 src/commands/contract/view_storage/output_format/as_json.rs create mode 100644 src/commands/contract/view_storage/output_format/as_table.rs create mode 100644 src/commands/contract/view_storage/output_format/mod.rs diff --git a/src/commands/contract/mod.rs b/src/commands/contract/mod.rs index 3617759c9..446d15ed0 100644 --- a/src/commands/contract/mod.rs +++ b/src/commands/contract/mod.rs @@ -3,7 +3,7 @@ use strum::{EnumDiscriminants, EnumIter, EnumMessage}; pub mod call_function; mod deploy; mod download_wasm; -mod view_state; +mod view_storage; #[derive(Debug, Clone, interactive_clap::InteractiveClap)] #[interactive_clap(context = crate::GlobalContext)] @@ -29,7 +29,7 @@ pub enum ContractActions { #[strum_discriminants(strum(message = "download-wasm - Download wasm"))] /// Download wasm DownloadWasm(self::download_wasm::ContractAccount), - #[strum_discriminants(strum(message = "view-state - View contract state"))] - /// View contract state - ViewState(self::view_state::ViewState), + #[strum_discriminants(strum(message = "view-storage - View contract storage"))] + /// View contract storage + ViewStorage(self::view_storage::ViewStorage), } diff --git a/src/commands/contract/view_state/mod.rs b/src/commands/contract/view_state/mod.rs deleted file mode 100644 index aaab0f1c8..000000000 --- a/src/commands/contract/view_state/mod.rs +++ /dev/null @@ -1,124 +0,0 @@ -use color_eyre::eyre::Context; - -use near_primitives::serialize::base64_display; -use prettytable::Table; - - -use crate::common::JsonRpcClientExt; - -#[derive(Debug, Clone, interactive_clap::InteractiveClap)] -#[interactive_clap(input_context = crate::GlobalContext)] -#[interactive_clap(output_context = ViewStateContext)] -pub struct ViewState { - #[interactive_clap(skip_default_input_arg)] - /// What is the contract account ID? - contract_account_id: crate::types::account_id::AccountId, - #[interactive_clap(named_arg)] - /// Select network - network_config: crate::network_view_at_block::NetworkViewAtBlockArgs, -} - -#[derive(Clone)] -pub struct ViewStateContext(crate::network_view_at_block::ArgsForViewContext); - -impl ViewStateContext { - pub fn from_previous_context( - previous_context: crate::GlobalContext, - scope: &::InteractiveClapContextScope, - ) -> color_eyre::eyre::Result { - let on_after_getting_block_reference_callback: crate::network_view_at_block::OnAfterGettingBlockReferenceCallback = std::sync::Arc::new({ - let contract_account_id: near_primitives::types::AccountId = scope.contract_account_id.clone().into(); - - move |network_config, block_reference| { - let query_view_method_response = network_config - .json_rpc_client() - .blocking_call(near_jsonrpc_client::methods::query::RpcQueryRequest { - block_reference: block_reference.clone(), - request: near_primitives::views::QueryRequest::ViewState { - account_id: contract_account_id.clone(), - prefix: near_primitives::types::StoreKey::from(Vec::new()), - include_proof: false, - }, - }) - .wrap_err_with(|| format!("Failed to fetch query ViewState for <{contract_account_id}>"))?; - if let near_jsonrpc_primitives::types::query::QueryResponseKind::ViewState(result) = - query_view_method_response.kind - { - eprintln!("Contract state (values):"); - eprintln!( - "{}", - // result.values[0] - // serde_json::to_string_pretty(&result.values[0].key)? - serde_json::to_string_pretty(&result.values)? - // base64_display(&result.values[0].key.as_slice()) - ); - - - - for value in &result.values { - eprintln!("{}: {}", String::from_utf8_lossy(&value.key), String::from_utf8_lossy(&value.value)); - // // let qwe = serde_json::from_slice::(&result.values[0].key)?; - // eprintln!( - // "{}: {}", - // // result.values[0] - // // serde_json::to_string_pretty(&result.values[0].key)? - // // serde_json::to_string_pretty(value)? - // base64_display(&value.key.as_slice()), - // base64_display(&value.value.as_slice()) - // ); - } - - - let mut table = Table::new(); - table.set_titles(prettytable::row![Fg=>"key", "value"]); - for value in &result.values { - table.add_row(prettytable::row![ - Fg->String::from_utf8_lossy(&value.key), - String::from_utf8_lossy(&value.value) - ]); - - // eprintln!("{}: {}", String::from_utf8_lossy(&value.key), String::from_utf8_lossy(&value.value)); - } - table.set_format(*prettytable::format::consts::FORMAT_NO_COLSEP); - table.printstd(); - - - - - - eprintln!( - "\nContract state (proof):\n{:#?}\n", - &result.proof - ); - println!("{}", "'".to_string()); - } else { - return Err(color_eyre::Report::msg("Error call result".to_string())); - }; - Ok(()) - } - }); - - Ok(Self(crate::network_view_at_block::ArgsForViewContext { - config: previous_context.config, - interacting_with_account_ids: vec![scope.contract_account_id.clone().into()], - on_after_getting_block_reference_callback, - })) - } -} - -impl From for crate::network_view_at_block::ArgsForViewContext { - fn from(item: ViewStateContext) -> Self { - item.0 - } -} - -impl ViewState { - pub fn input_contract_account_id( - context: &crate::GlobalContext, - ) -> color_eyre::eyre::Result> { - crate::common::input_non_signer_account_id_from_used_account_list( - &context.config.credentials_home_dir, - "What is the contract account ID?", - ) - } -} diff --git a/src/commands/contract/view_storage/keys_to_view/all_keys.rs b/src/commands/contract/view_storage/keys_to_view/all_keys.rs new file mode 100644 index 000000000..155035357 --- /dev/null +++ b/src/commands/contract/view_storage/keys_to_view/all_keys.rs @@ -0,0 +1,29 @@ +#[derive(Debug, Clone, interactive_clap::InteractiveClap)] +#[interactive_clap(input_context = super::super::ViewStorageContext)] +#[interactive_clap(output_context = AllKeysContext)] +pub struct AllKeys { + #[interactive_clap(subcommand)] + output_format: super::super::output_format::OutputFormat, +} + +#[derive(Debug, Clone)] +pub struct AllKeysContext(super::KeysContext); + +impl AllKeysContext { + pub fn from_previous_context( + previous_context: super::super::ViewStorageContext, + _scope: &::InteractiveClapContextScope, + ) -> color_eyre::eyre::Result { + Ok(Self(super::KeysContext { + global_context: previous_context.global_context, + contract_account_id: previous_context.contract_account_id, + prefix: near_primitives::types::StoreKey::from(Vec::new()), + })) + } +} + +impl From for super::KeysContext { + fn from(item: AllKeysContext) -> Self { + item.0 + } +} diff --git a/src/commands/contract/view_storage/keys_to_view/mod.rs b/src/commands/contract/view_storage/keys_to_view/mod.rs new file mode 100644 index 000000000..b14fad669 --- /dev/null +++ b/src/commands/contract/view_storage/keys_to_view/mod.rs @@ -0,0 +1,32 @@ +use strum::{EnumDiscriminants, EnumIter, EnumMessage}; + +mod all_keys; + +#[derive(Debug, EnumDiscriminants, Clone, interactive_clap::InteractiveClap)] +#[interactive_clap(context = super::ViewStorageContext)] +#[strum_discriminants(derive(EnumMessage, EnumIter))] +/// Select keys to view contract storage: +pub enum KeysToView { + #[strum_discriminants(strum( + message = "all - View contract storage for all keys" + ))] + /// View contract storage for all keys + All(self::all_keys::AllKeys), + #[strum_discriminants(strum( + message = "keys-start-with-string - View contract storage for keys that start with a string (for example, \"S\")" + ))] + /// View contract storage for keys that start with a string (for example, "S") + KeysStartWithString, + #[strum_discriminants(strum( + message = "keys-start-with-bytes-as-base64 - View the contract storage for keys that start with Base64 bytes (for example, \"Uw==\")" + ))] + /// View the contract storage for keys that start with Base64 bytes (for example, "Uw==") + KeysStartWithBytesAsBase64, +} + +#[derive(Debug, Clone)] +pub struct KeysContext { + pub global_context: crate::GlobalContext, + pub contract_account_id: near_primitives::types::AccountId, + pub prefix: near_primitives::types::StoreKey, +} diff --git a/src/commands/contract/view_storage/mod.rs b/src/commands/contract/view_storage/mod.rs new file mode 100644 index 000000000..6c8b29d40 --- /dev/null +++ b/src/commands/contract/view_storage/mod.rs @@ -0,0 +1,42 @@ +mod keys_to_view; +mod output_format; + +#[derive(Debug, Clone, interactive_clap::InteractiveClap)] +#[interactive_clap(input_context = crate::GlobalContext)] +#[interactive_clap(output_context = ViewStorageContext)] +pub struct ViewStorage { + #[interactive_clap(skip_default_input_arg)] + /// What is the contract account ID? + contract_account_id: crate::types::account_id::AccountId, + #[interactive_clap(subcommand)] + keys_to_view: self::keys_to_view::KeysToView, +} + +#[derive(Debug, Clone)] +pub struct ViewStorageContext { + global_context: crate::GlobalContext, + contract_account_id: near_primitives::types::AccountId, +} + +impl ViewStorageContext { + pub fn from_previous_context( + previous_context: crate::GlobalContext, + scope: &::InteractiveClapContextScope, + ) -> color_eyre::eyre::Result { + Ok(Self { + global_context: previous_context, + contract_account_id: scope.contract_account_id.clone().into(), + }) + } +} + +impl ViewStorage { + pub fn input_contract_account_id( + context: &crate::GlobalContext, + ) -> color_eyre::eyre::Result> { + crate::common::input_non_signer_account_id_from_used_account_list( + &context.config.credentials_home_dir, + "What is the contract account ID?", + ) + } +} diff --git a/src/commands/contract/view_storage/output_format/as_json.rs b/src/commands/contract/view_storage/output_format/as_json.rs new file mode 100644 index 000000000..9228ff75c --- /dev/null +++ b/src/commands/contract/view_storage/output_format/as_json.rs @@ -0,0 +1,68 @@ +use crate::common::JsonRpcClientExt; +use color_eyre::eyre::Context; + +#[derive(Debug, Clone, interactive_clap::InteractiveClap)] +#[interactive_clap(input_context = super::super::keys_to_view::KeysContext)] +#[interactive_clap(output_context = AsJsonContext)] +pub struct AsJson { + #[interactive_clap(named_arg)] + /// Select network + network_config: crate::network_view_at_block::NetworkViewAtBlockArgs, +} + +#[derive(Clone)] +pub struct AsJsonContext(crate::network_view_at_block::ArgsForViewContext); + +impl AsJsonContext { + pub fn from_previous_context( + previous_context: super::super::keys_to_view::KeysContext, + _scope: &::InteractiveClapContextScope, + ) -> color_eyre::eyre::Result { + let on_after_getting_block_reference_callback: crate::network_view_at_block::OnAfterGettingBlockReferenceCallback = std::sync::Arc::new({ + let contract_account_id = previous_context.contract_account_id.clone(); + let prefix = previous_context.prefix; + + move |network_config, block_reference| { + let query_view_method_response = network_config + .json_rpc_client() + .blocking_call(near_jsonrpc_client::methods::query::RpcQueryRequest { + block_reference: block_reference.clone(), + request: near_primitives::views::QueryRequest::ViewState { + account_id: contract_account_id.clone(), + prefix: prefix.clone(), + include_proof: false, + }, + }) + .wrap_err_with(|| format!("Failed to fetch query ViewState for <{contract_account_id}>"))?; + if let near_jsonrpc_primitives::types::query::QueryResponseKind::ViewState(result) = + query_view_method_response.kind + { + eprintln!("Contract state (values):"); + eprintln!( + "{}", + serde_json::to_string_pretty(&result.values)? + ); + eprintln!( + "\nContract state (proof):\n{:#?}\n", + &result.proof + ); + } else { + return Err(color_eyre::Report::msg("Error call result".to_string())); + }; + Ok(()) + } + }); + + Ok(Self(crate::network_view_at_block::ArgsForViewContext { + config: previous_context.global_context.config, + interacting_with_account_ids: vec![previous_context.contract_account_id], + on_after_getting_block_reference_callback, + })) + } +} + +impl From for crate::network_view_at_block::ArgsForViewContext { + fn from(item: AsJsonContext) -> Self { + item.0 + } +} diff --git a/src/commands/contract/view_storage/output_format/as_table.rs b/src/commands/contract/view_storage/output_format/as_table.rs new file mode 100644 index 000000000..9e81d250a --- /dev/null +++ b/src/commands/contract/view_storage/output_format/as_table.rs @@ -0,0 +1,76 @@ +use color_eyre::eyre::Context; +use prettytable::Table; + +use crate::common::JsonRpcClientExt; + +#[derive(Debug, Clone, interactive_clap::InteractiveClap)] +#[interactive_clap(input_context = super::super::keys_to_view::KeysContext)] +#[interactive_clap(output_context = AsTableContext)] +pub struct AsTable { + #[interactive_clap(named_arg)] + /// Select network + network_config: crate::network_view_at_block::NetworkViewAtBlockArgs, +} + +#[derive(Clone)] +pub struct AsTableContext(crate::network_view_at_block::ArgsForViewContext); + +impl AsTableContext { + pub fn from_previous_context( + previous_context: super::super::keys_to_view::KeysContext, + _scope: &::InteractiveClapContextScope, + ) -> color_eyre::eyre::Result { + let on_after_getting_block_reference_callback: crate::network_view_at_block::OnAfterGettingBlockReferenceCallback = std::sync::Arc::new({ + let contract_account_id = previous_context.contract_account_id.clone(); + let prefix = previous_context.prefix; + + move |network_config, block_reference| { + let query_view_method_response = network_config + .json_rpc_client() + .blocking_call(near_jsonrpc_client::methods::query::RpcQueryRequest { + block_reference: block_reference.clone(), + request: near_primitives::views::QueryRequest::ViewState { + account_id: contract_account_id.clone(), + prefix: prefix.clone(), + include_proof: false, + }, + }) + .wrap_err_with(|| format!("Failed to fetch query ViewState for <{contract_account_id}>"))?; + if let near_jsonrpc_primitives::types::query::QueryResponseKind::ViewState(result) = + query_view_method_response.kind + { + eprintln!("Contract state (values):"); + let mut table = Table::new(); + table.set_titles(prettytable::row![Fg=>"key", "value"]); + for value in &result.values { + table.add_row(prettytable::row![ + Fg->String::from_utf8_lossy(&value.key), + String::from_utf8_lossy(&value.value) + ]); + } + table.set_format(*prettytable::format::consts::FORMAT_NO_COLSEP); + table.printstd(); + eprintln!( + "\nContract state (proof):\n{:#?}\n", + &result.proof + ); + } else { + return Err(color_eyre::Report::msg("Error call result".to_string())); + }; + Ok(()) + } + }); + + Ok(Self(crate::network_view_at_block::ArgsForViewContext { + config: previous_context.global_context.config, + interacting_with_account_ids: vec![previous_context.contract_account_id], + on_after_getting_block_reference_callback, + })) + } +} + +impl From for crate::network_view_at_block::ArgsForViewContext { + fn from(item: AsTableContext) -> Self { + item.0 + } +} diff --git a/src/commands/contract/view_storage/output_format/mod.rs b/src/commands/contract/view_storage/output_format/mod.rs new file mode 100644 index 000000000..cabfd1e36 --- /dev/null +++ b/src/commands/contract/view_storage/output_format/mod.rs @@ -0,0 +1,17 @@ +use strum::{EnumDiscriminants, EnumIter, EnumMessage}; + +mod as_json; +mod as_table; + +#[derive(Debug, EnumDiscriminants, Clone, interactive_clap::InteractiveClap)] +#[interactive_clap(context = super::keys_to_view::KeysContext)] +#[strum_discriminants(derive(EnumMessage, EnumIter))] +/// Choose a format to view contract storage: +pub enum OutputFormat { + #[strum_discriminants(strum(message = "as-json - View contract storage in JSON format"))] + /// View contract storage in JSON format + AsJson(self::as_json::AsJson), + #[strum_discriminants(strum(message = "as-table - View contract storage in the table"))] + /// View contract storage in the table + AsTable(self::as_table::AsTable), +} From 7e0629ed30fc055eebb47e93bcaff54cb892251f Mon Sep 17 00:00:00 2001 From: FroVolod Date: Wed, 6 Sep 2023 12:19:16 +0300 Subject: [PATCH 06/17] fmt --- src/commands/contract/mod.rs | 4 ++-- .../contract/view_storage/keys_to_view/mod.rs | 14 +++++++------- .../contract/view_storage/output_format/mod.rs | 10 +++++----- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/commands/contract/mod.rs b/src/commands/contract/mod.rs index 446d15ed0..72d2d3bed 100644 --- a/src/commands/contract/mod.rs +++ b/src/commands/contract/mod.rs @@ -29,7 +29,7 @@ pub enum ContractActions { #[strum_discriminants(strum(message = "download-wasm - Download wasm"))] /// Download wasm DownloadWasm(self::download_wasm::ContractAccount), - #[strum_discriminants(strum(message = "view-storage - View contract storage"))] - /// View contract storage + #[strum_discriminants(strum(message = "view-storage - View contract storage state"))] + /// View contract storage state ViewStorage(self::view_storage::ViewStorage), } diff --git a/src/commands/contract/view_storage/keys_to_view/mod.rs b/src/commands/contract/view_storage/keys_to_view/mod.rs index b14fad669..34576d628 100644 --- a/src/commands/contract/view_storage/keys_to_view/mod.rs +++ b/src/commands/contract/view_storage/keys_to_view/mod.rs @@ -5,22 +5,22 @@ mod all_keys; #[derive(Debug, EnumDiscriminants, Clone, interactive_clap::InteractiveClap)] #[interactive_clap(context = super::ViewStorageContext)] #[strum_discriminants(derive(EnumMessage, EnumIter))] -/// Select keys to view contract storage: +/// Select keys to view contract storage state: pub enum KeysToView { #[strum_discriminants(strum( - message = "all - View contract storage for all keys" + message = "all - View contract storage state for all keys" ))] - /// View contract storage for all keys + /// View contract storage state for all keys All(self::all_keys::AllKeys), #[strum_discriminants(strum( - message = "keys-start-with-string - View contract storage for keys that start with a string (for example, \"S\")" + message = "keys-start-with-string - View contract storage state for keys that start with a string (for example, \"S\")" ))] - /// View contract storage for keys that start with a string (for example, "S") + /// View contract storage state for keys that start with a string (for example, "S") KeysStartWithString, #[strum_discriminants(strum( - message = "keys-start-with-bytes-as-base64 - View the contract storage for keys that start with Base64 bytes (for example, \"Uw==\")" + message = "keys-start-with-bytes-as-base64 - View contract storage state for keys that start with Base64 bytes (for example, \"Uw==\")" ))] - /// View the contract storage for keys that start with Base64 bytes (for example, "Uw==") + /// View contract storage state for keys that start with Base64 bytes (for example, "Uw==") KeysStartWithBytesAsBase64, } diff --git a/src/commands/contract/view_storage/output_format/mod.rs b/src/commands/contract/view_storage/output_format/mod.rs index cabfd1e36..9ebd88e66 100644 --- a/src/commands/contract/view_storage/output_format/mod.rs +++ b/src/commands/contract/view_storage/output_format/mod.rs @@ -6,12 +6,12 @@ mod as_table; #[derive(Debug, EnumDiscriminants, Clone, interactive_clap::InteractiveClap)] #[interactive_clap(context = super::keys_to_view::KeysContext)] #[strum_discriminants(derive(EnumMessage, EnumIter))] -/// Choose a format to view contract storage: +/// Choose a format to view contract storage state: pub enum OutputFormat { - #[strum_discriminants(strum(message = "as-json - View contract storage in JSON format"))] - /// View contract storage in JSON format + #[strum_discriminants(strum(message = "as-json - View contract storage state in JSON format"))] + /// View contract storage state in JSON format AsJson(self::as_json::AsJson), - #[strum_discriminants(strum(message = "as-table - View contract storage in the table"))] - /// View contract storage in the table + #[strum_discriminants(strum(message = "as-table - View contract storage state in the table"))] + /// View contract storage state in the table AsTable(self::as_table::AsTable), } From bd84067a860a1d9e1aee9522c20a5ef7d5ae04b6 Mon Sep 17 00:00:00 2001 From: FroVolod Date: Wed, 6 Sep 2023 12:32:33 +0300 Subject: [PATCH 07/17] fmt --- src/commands/contract/view_storage/output_format/mod.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/commands/contract/view_storage/output_format/mod.rs b/src/commands/contract/view_storage/output_format/mod.rs index 9ebd88e66..953e4f28a 100644 --- a/src/commands/contract/view_storage/output_format/mod.rs +++ b/src/commands/contract/view_storage/output_format/mod.rs @@ -8,10 +8,14 @@ mod as_table; #[strum_discriminants(derive(EnumMessage, EnumIter))] /// Choose a format to view contract storage state: pub enum OutputFormat { - #[strum_discriminants(strum(message = "as-json - View contract storage state in JSON format"))] + #[strum_discriminants(strum( + message = "as-json - View contract storage state in JSON format" + ))] /// View contract storage state in JSON format AsJson(self::as_json::AsJson), - #[strum_discriminants(strum(message = "as-table - View contract storage state in the table"))] + #[strum_discriminants(strum( + message = "as-table - View contract storage state in the table" + ))] /// View contract storage state in the table AsTable(self::as_table::AsTable), } From 1ff0063b910a4ac12f4dd908b2d38074304fe2da Mon Sep 17 00:00:00 2001 From: FroVolod Date: Thu, 7 Sep 2023 11:47:00 +0300 Subject: [PATCH 08/17] added KeysStartWithString to view contract storage --- .../keys_to_view/keys_start_with_string.rs | 33 +++++++++++++++++++ .../contract/view_storage/keys_to_view/mod.rs | 3 +- 2 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 src/commands/contract/view_storage/keys_to_view/keys_start_with_string.rs diff --git a/src/commands/contract/view_storage/keys_to_view/keys_start_with_string.rs b/src/commands/contract/view_storage/keys_to_view/keys_start_with_string.rs new file mode 100644 index 000000000..02fe69879 --- /dev/null +++ b/src/commands/contract/view_storage/keys_to_view/keys_start_with_string.rs @@ -0,0 +1,33 @@ +#[derive(Debug, Clone, interactive_clap::InteractiveClap)] +#[interactive_clap(input_context = super::super::ViewStorageContext)] +#[interactive_clap(output_context = KeysStartWithStringContext)] +pub struct KeysStartWithString { + /// Enter the string that the keys begin with (for example, "S"): + keys_begin_with: String, + #[interactive_clap(subcommand)] + output_format: super::super::output_format::OutputFormat, +} + +#[derive(Debug, Clone)] +pub struct KeysStartWithStringContext(super::KeysContext); + +impl KeysStartWithStringContext { + pub fn from_previous_context( + previous_context: super::super::ViewStorageContext, + scope: &::InteractiveClapContextScope, + ) -> color_eyre::eyre::Result { + Ok(Self(super::KeysContext { + global_context: previous_context.global_context, + contract_account_id: previous_context.contract_account_id, + prefix: near_primitives::types::StoreKey::from( + scope.keys_begin_with.clone().into_bytes(), + ), + })) + } +} + +impl From for super::KeysContext { + fn from(item: KeysStartWithStringContext) -> Self { + item.0 + } +} diff --git a/src/commands/contract/view_storage/keys_to_view/mod.rs b/src/commands/contract/view_storage/keys_to_view/mod.rs index 34576d628..29d1d531a 100644 --- a/src/commands/contract/view_storage/keys_to_view/mod.rs +++ b/src/commands/contract/view_storage/keys_to_view/mod.rs @@ -1,6 +1,7 @@ use strum::{EnumDiscriminants, EnumIter, EnumMessage}; mod all_keys; +mod keys_start_with_string; #[derive(Debug, EnumDiscriminants, Clone, interactive_clap::InteractiveClap)] #[interactive_clap(context = super::ViewStorageContext)] @@ -16,7 +17,7 @@ pub enum KeysToView { message = "keys-start-with-string - View contract storage state for keys that start with a string (for example, \"S\")" ))] /// View contract storage state for keys that start with a string (for example, "S") - KeysStartWithString, + KeysStartWithString(self::keys_start_with_string::KeysStartWithString), #[strum_discriminants(strum( message = "keys-start-with-bytes-as-base64 - View contract storage state for keys that start with Base64 bytes (for example, \"Uw==\")" ))] From ff00d9c7dc251582fd702a9a150e51b0b5f268dd Mon Sep 17 00:00:00 2001 From: FroVolod Date: Thu, 7 Sep 2023 20:28:57 +0300 Subject: [PATCH 09/17] added KeysStartWithBytesAsBase64 to view --- .../keys_start_with_bytes_as_base64.rs | 33 +++++++++++++++++++ .../contract/view_storage/keys_to_view/mod.rs | 3 +- 2 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 src/commands/contract/view_storage/keys_to_view/keys_start_with_bytes_as_base64.rs diff --git a/src/commands/contract/view_storage/keys_to_view/keys_start_with_bytes_as_base64.rs b/src/commands/contract/view_storage/keys_to_view/keys_start_with_bytes_as_base64.rs new file mode 100644 index 000000000..dbd8ec434 --- /dev/null +++ b/src/commands/contract/view_storage/keys_to_view/keys_start_with_bytes_as_base64.rs @@ -0,0 +1,33 @@ +#[derive(Debug, Clone, interactive_clap::InteractiveClap)] +#[interactive_clap(input_context = super::super::ViewStorageContext)] +#[interactive_clap(output_context = KeysStartWithBytesAsBase64Context)] +pub struct KeysStartWithBytesAsBase64 { + /// Enter the string that the keys begin with Base64 bytes (for example, "Uw=="): + keys_begin_with: String, + #[interactive_clap(subcommand)] + output_format: super::super::output_format::OutputFormat, +} + +#[derive(Debug, Clone)] +pub struct KeysStartWithBytesAsBase64Context(super::KeysContext); + +impl KeysStartWithBytesAsBase64Context { + pub fn from_previous_context( + previous_context: super::super::ViewStorageContext, + scope: &::InteractiveClapContextScope, + ) -> color_eyre::eyre::Result { + Ok(Self(super::KeysContext { + global_context: previous_context.global_context, + contract_account_id: previous_context.contract_account_id, + prefix: near_primitives::types::StoreKey::from( + near_primitives::serialize::from_base64(&scope.keys_begin_with)?, + ), + })) + } +} + +impl From for super::KeysContext { + fn from(item: KeysStartWithBytesAsBase64Context) -> Self { + item.0 + } +} diff --git a/src/commands/contract/view_storage/keys_to_view/mod.rs b/src/commands/contract/view_storage/keys_to_view/mod.rs index 29d1d531a..8e66bc4dd 100644 --- a/src/commands/contract/view_storage/keys_to_view/mod.rs +++ b/src/commands/contract/view_storage/keys_to_view/mod.rs @@ -1,6 +1,7 @@ use strum::{EnumDiscriminants, EnumIter, EnumMessage}; mod all_keys; +mod keys_start_with_bytes_as_base64; mod keys_start_with_string; #[derive(Debug, EnumDiscriminants, Clone, interactive_clap::InteractiveClap)] @@ -22,7 +23,7 @@ pub enum KeysToView { message = "keys-start-with-bytes-as-base64 - View contract storage state for keys that start with Base64 bytes (for example, \"Uw==\")" ))] /// View contract storage state for keys that start with Base64 bytes (for example, "Uw==") - KeysStartWithBytesAsBase64, + KeysStartWithBytesAsBase64(self::keys_start_with_bytes_as_base64::KeysStartWithBytesAsBase64), } #[derive(Debug, Clone)] From d7d6bb5315d2b6283622806a8ea6b216e7f5dd41 Mon Sep 17 00:00:00 2001 From: FroVolod Date: Fri, 8 Sep 2023 12:31:30 +0300 Subject: [PATCH 10/17] refactored --- Cargo.lock | 38 ++++++++++++++++ Cargo.toml | 1 + .../output_format/{as_table.rs => as_text.rs} | 45 ++++++++++++------- .../view_storage/output_format/mod.rs | 10 ++--- 4 files changed, 73 insertions(+), 21 deletions(-) rename src/commands/contract/view_storage/output_format/{as_table.rs => as_text.rs} (62%) diff --git a/Cargo.lock b/Cargo.lock index fbe63c42e..b46da4da7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -686,6 +686,27 @@ dependencies = [ "tracing-error", ] +[[package]] +name = "color-print" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a858372ff14bab9b1b30ea504f2a4bc534582aee3e42ba2d41d2a7baba63d5d" +dependencies = [ + "color-print-proc-macro", +] + +[[package]] +name = "color-print-proc-macro" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57e37866456a721d0a404439a1adae37a31be4e0055590d053dfe6981e05003f" +dependencies = [ + "nom", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "color-spantrace" version = "0.2.0" @@ -1971,6 +1992,12 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "miniz_oxide" version = "0.6.2" @@ -2076,6 +2103,7 @@ dependencies = [ "cargo-util", "clap", "color-eyre", + "color-print", "derive_more", "dirs", "easy-ext 1.0.1", @@ -2363,6 +2391,16 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b93853da6d84c2e3c7d730d6473e8817692dd89be387eb01b94d7f108ecb5b8c" +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + [[package]] name = "nu-ansi-term" version = "0.46.0" diff --git a/Cargo.toml b/Cargo.toml index 8037acbb5..a7706a9f5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -56,6 +56,7 @@ thiserror = "1" bytesize = "1.1.0" prettytable = "0.10.0" +color-print = "0.3.5" near-ledger = { version = "0.2.0", optional = true } diff --git a/src/commands/contract/view_storage/output_format/as_table.rs b/src/commands/contract/view_storage/output_format/as_text.rs similarity index 62% rename from src/commands/contract/view_storage/output_format/as_table.rs rename to src/commands/contract/view_storage/output_format/as_text.rs index 9e81d250a..07773111b 100644 --- a/src/commands/contract/view_storage/output_format/as_table.rs +++ b/src/commands/contract/view_storage/output_format/as_text.rs @@ -1,24 +1,24 @@ use color_eyre::eyre::Context; -use prettytable::Table; +use color_print::cprintln; use crate::common::JsonRpcClientExt; #[derive(Debug, Clone, interactive_clap::InteractiveClap)] #[interactive_clap(input_context = super::super::keys_to_view::KeysContext)] -#[interactive_clap(output_context = AsTableContext)] -pub struct AsTable { +#[interactive_clap(output_context = AsTextContext)] +pub struct AsText { #[interactive_clap(named_arg)] /// Select network network_config: crate::network_view_at_block::NetworkViewAtBlockArgs, } #[derive(Clone)] -pub struct AsTableContext(crate::network_view_at_block::ArgsForViewContext); +pub struct AsTextContext(crate::network_view_at_block::ArgsForViewContext); -impl AsTableContext { +impl AsTextContext { pub fn from_previous_context( previous_context: super::super::keys_to_view::KeysContext, - _scope: &::InteractiveClapContextScope, + _scope: &::InteractiveClapContextScope, ) -> color_eyre::eyre::Result { let on_after_getting_block_reference_callback: crate::network_view_at_block::OnAfterGettingBlockReferenceCallback = std::sync::Arc::new({ let contract_account_id = previous_context.contract_account_id.clone(); @@ -40,16 +40,29 @@ impl AsTableContext { query_view_method_response.kind { eprintln!("Contract state (values):"); - let mut table = Table::new(); - table.set_titles(prettytable::row![Fg=>"key", "value"]); for value in &result.values { - table.add_row(prettytable::row![ - Fg->String::from_utf8_lossy(&value.key), - String::from_utf8_lossy(&value.value) - ]); + let key = String::from_utf8( + value.key + .as_slice() + .iter() + .flat_map(|b| std::ascii::escape_default(*b)) + .collect::>(), + ) + .wrap_err("Wrong format. utf-8 is expected.")?; + cprintln!("key:\n{}", key); + + let val = String::from_utf8( + value.value + .as_slice() + .iter() + .flat_map(|b| std::ascii::escape_default(*b)) + .collect::>(), + ) + .wrap_err("Wrong format. utf-8 is expected.")?; + cprintln!("value:\n{}", val); + + eprintln!("--------------------------------"); } - table.set_format(*prettytable::format::consts::FORMAT_NO_COLSEP); - table.printstd(); eprintln!( "\nContract state (proof):\n{:#?}\n", &result.proof @@ -69,8 +82,8 @@ impl AsTableContext { } } -impl From for crate::network_view_at_block::ArgsForViewContext { - fn from(item: AsTableContext) -> Self { +impl From for crate::network_view_at_block::ArgsForViewContext { + fn from(item: AsTextContext) -> Self { item.0 } } diff --git a/src/commands/contract/view_storage/output_format/mod.rs b/src/commands/contract/view_storage/output_format/mod.rs index 953e4f28a..92f527148 100644 --- a/src/commands/contract/view_storage/output_format/mod.rs +++ b/src/commands/contract/view_storage/output_format/mod.rs @@ -1,7 +1,7 @@ use strum::{EnumDiscriminants, EnumIter, EnumMessage}; mod as_json; -mod as_table; +mod as_text; #[derive(Debug, EnumDiscriminants, Clone, interactive_clap::InteractiveClap)] #[interactive_clap(context = super::keys_to_view::KeysContext)] @@ -9,13 +9,13 @@ mod as_table; /// Choose a format to view contract storage state: pub enum OutputFormat { #[strum_discriminants(strum( - message = "as-json - View contract storage state in JSON format" + message = "as-json - View contract storage state in JSON format" ))] /// View contract storage state in JSON format AsJson(self::as_json::AsJson), #[strum_discriminants(strum( - message = "as-table - View contract storage state in the table" + message = "as-text - View contract storage state in the text" ))] - /// View contract storage state in the table - AsTable(self::as_table::AsTable), + /// View contract storage state in the text + AsText(self::as_text::AsText), } From 4deafead661439636ae3e14035692c42db9b83bf Mon Sep 17 00:00:00 2001 From: FroVolod Date: Fri, 8 Sep 2023 13:39:10 +0300 Subject: [PATCH 11/17] updated GUIDE --- docs/GUIDE.en.md | 50 ++++++++++++++++++++++++++++++++++++++++++++++++ docs/GUIDE.ru.md | 50 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 100 insertions(+) diff --git a/docs/GUIDE.en.md b/docs/GUIDE.en.md index 083041592..3ae80d0fb 100644 --- a/docs/GUIDE.en.md +++ b/docs/GUIDE.en.md @@ -1083,6 +1083,7 @@ fro_volod.testnet account has NFT tokens: - [call-function](#call-function---Execute-function-contract-method) - [deploy](#deploy---Add-a-new-contract-code) - [download-wasm](#download-wasm---Download-wasm) +- [view-storage](#view-storage---View-contract-storage-state) #### call-function - Execute function (contract method) @@ -1233,6 +1234,55 @@ The file "/Users/frovolod/Downloads/contract_262_volodymyr_testnet.wasm" was dow +#### view-storage - View contract storage state + +You can view the contract key values at the current moment in time (***now***) and at a certain point in the past by specifying a block (***at-block-height*** or ***at-block-hash***). +Examples of the use of these parameters are discussed in the ([View properties for an account](#view-account-summary---view-properties-for-an-account)). +The keys themselves can be viewed all (***all***) or filtered using ***keys-start-with-string*** or ***keys-start-with-bytes-as-base64***. + +To view contract keys, enter at the terminal command line: + +```txt +near contract \ + view-storage turbo.volodymyr.testnet \ + all \ + as-json \ + network-config testnet \ + now +``` + +
The result of this command will be as follows: +```txt +Contract state (values): +[ + { + "key": "MjF2b2xvZHlteXIudGVzdG5ldA==", + "value": "JwAAAAAAAAAIAAAAAAAAAA==" + }, + { + "key": "U1RBVEU=", + "value": "" + }, + { + "key": "ZnJvX3ZvbG9kLnRlc3RuZXQ=", + "value": "HQAAAAAAAAAGAAAAAAAAAA==" + }, + { + "key": "dm9sb2R5bXlyLnRlc3RuZXQ=", + "value": "QAEAAAAAAABAAAAAAAAAAA==" + } +] +Contract state (proof): +[] +``` +
+ +
Demonstration of the command in interactive mode + + + +
+ ### transaction - Operate transactions - [view-status](#view-status---View-a-transaction-status) diff --git a/docs/GUIDE.ru.md b/docs/GUIDE.ru.md index 21bce288a..568f6b498 100644 --- a/docs/GUIDE.ru.md +++ b/docs/GUIDE.ru.md @@ -1084,6 +1084,7 @@ fro_volod.testnet account has NFT tokens: - [call-function](#call-function---Execute-function-contract-method) - [deploy](#deploy---Add-a-new-contract-code) - [download-wasm](#download-wasm---Download-wasm) +- [view-storage](#view-storage---View-contract-storage-state) #### call-function - Execute function (contract method) @@ -1234,6 +1235,55 @@ The file "/Users/frovolod/Downloads/contract_262_volodymyr_testnet.wasm" was dow +#### view-storage - View contract storage state + +Просмотреть значения ключей контракта возможно на текущий момент времени (***now***) и на определеный момент в прошлом, указав блок (***at-block-height*** или ***at-block-hash***). +Примеры использования этих параметров рассмотрены в разделе [View properties for an account](#view-account-summary---view-properties-for-an-account). +Сами же ключи можно просмотреть все (***all***) или отфильтрованные с помощью ***keys-start-with-string*** или ***keys-start-with-bytes-as-base64***. + +Для просмотра ключей контракта необходимо ввести в командной строке терминала: + +```txt +near contract \ + view-storage turbo.volodymyr.testnet \ + all \ + as-json \ + network-config testnet \ + now +``` + +
Результат выполнения команды +```txt +Contract state (values): +[ + { + "key": "MjF2b2xvZHlteXIudGVzdG5ldA==", + "value": "JwAAAAAAAAAIAAAAAAAAAA==" + }, + { + "key": "U1RBVEU=", + "value": "" + }, + { + "key": "ZnJvX3ZvbG9kLnRlc3RuZXQ=", + "value": "HQAAAAAAAAAGAAAAAAAAAA==" + }, + { + "key": "dm9sb2R5bXlyLnRlc3RuZXQ=", + "value": "QAEAAAAAAABAAAAAAAAAAA==" + } +] +Contract state (proof): +[] +``` +
+ +
Демонстрация работы команды в интерактивном режиме + + + +
+ ### transaction - Operate transactions - [view-status](#view-status---View-a-transaction-status) From d4807039ef7893a5b42809ea86744e9f54445dd2 Mon Sep 17 00:00:00 2001 From: FroVolod Date: Fri, 8 Sep 2023 19:12:02 +0300 Subject: [PATCH 12/17] updated ViewStateArgs for js_command_match --- src/js_command_match/mod.rs | 2 +- src/js_command_match/view_state.rs | 54 +++++++++++++++++++++++++----- 2 files changed, 46 insertions(+), 10 deletions(-) diff --git a/src/js_command_match/mod.rs b/src/js_command_match/mod.rs index 9752c658a..9a4d2bde0 100644 --- a/src/js_command_match/mod.rs +++ b/src/js_command_match/mod.rs @@ -68,7 +68,7 @@ impl JsCmd { Self::DevDeploy(_) => Err("`dev-deploy` command is not implemented, yet. It will be implemented in a dev extension. Meanwhile, consider using the old CLI or a standalone implementation: https://github.com/frolvanya/dev-deploy".to_string()), Self::Call(call_args) => Ok(call_args.to_cli_args(network_config)), Self::View(view_args) => Ok(view_args.to_cli_args(network_config)), - Self::ViewState(_) => Err("`view-state` command is not implemented, yet. It will be implemented in a dev extension. Meanwhile, keep using the old CLI.".to_string()), + Self::ViewState(view_state_args) => Ok(view_state_args.to_cli_args(network_config)), Self::Send(send_args) => Ok(send_args.to_cli_args(network_config)), Self::Clean(_) => Err("`clean` command is not implemented, yet. It will be implemented in a dev extension. Meanwhile, keep using the old CLI.".to_string()), Self::Stake(_) => Err("`stake` command is not implemented, yet. It will be implemented in a validators extension. Meanwhile, keep using the old CLI.".to_string()), diff --git a/src/js_command_match/view_state.rs b/src/js_command_match/view_state.rs index 476ef429a..4a4540b95 100644 --- a/src/js_command_match/view_state.rs +++ b/src/js_command_match/view_state.rs @@ -1,15 +1,51 @@ #[derive(Debug, Clone, clap::Parser)] /// This is a legacy `view-state` command. Once you run it with the specified arguments, new syntax command will be suggested. pub struct ViewStateArgs { - account_id: String, - #[clap(long, default_value = "")] - prefix: String, - #[clap(long, aliases = ["block_id", "blockId"], default_value = "0")] - block_id: String, - #[clap(long, default_value = "final")] - finality: String, - #[clap(long, default_value = "false")] - utf8: String, + contract_account_id: String, + #[clap(long, default_value = None, conflicts_with = "block_id")] + finality: Option, + #[clap(long, aliases = ["block_id", "blockId"], default_value = None)] + block_id: Option, + #[clap(long, default_value = None)] + prefix: Option, + #[clap(long, default_value_t = false)] + utf8: bool, #[clap(allow_hyphen_values = true, num_args = 0..)] _unknown_args: Vec, } + +impl ViewStateArgs { + pub fn to_cli_args(&self, network_config: String) -> Vec { + if self.prefix.is_none() { + let output_format = if self.utf8 { "as-text" } else { "as-json" }; + if self.finality.is_some() { + vec![ + "contract".to_owned(), + "view-storage".to_owned(), + self.contract_account_id.to_owned(), + "all".to_owned(), + output_format.to_owned(), + "network-config".to_owned(), + network_config, + "now".to_owned(), + ] + } else { + vec![ + "contract".to_owned(), + "view-storage".to_owned(), + self.contract_account_id.to_owned(), + "all".to_owned(), + output_format.to_owned(), + "network-config".to_owned(), + network_config, + ] + } + } else { + vec![ + "contract".to_owned(), + "view-storage".to_owned(), + self.contract_account_id.to_owned(), + ] + } + } +} From da8f565bb6e964befdadd887554e5a941c6b8975 Mon Sep 17 00:00:00 2001 From: FroVolod Date: Fri, 8 Sep 2023 19:23:51 +0300 Subject: [PATCH 13/17] owo_colors instead color-print --- Cargo.lock | 38 ------------------- Cargo.toml | 1 - .../view_storage/output_format/as_text.rs | 7 ++-- 3 files changed, 3 insertions(+), 43 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b46da4da7..fbe63c42e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -686,27 +686,6 @@ dependencies = [ "tracing-error", ] -[[package]] -name = "color-print" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a858372ff14bab9b1b30ea504f2a4bc534582aee3e42ba2d41d2a7baba63d5d" -dependencies = [ - "color-print-proc-macro", -] - -[[package]] -name = "color-print-proc-macro" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57e37866456a721d0a404439a1adae37a31be4e0055590d053dfe6981e05003f" -dependencies = [ - "nom", - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "color-spantrace" version = "0.2.0" @@ -1992,12 +1971,6 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" -[[package]] -name = "minimal-lexical" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" - [[package]] name = "miniz_oxide" version = "0.6.2" @@ -2103,7 +2076,6 @@ dependencies = [ "cargo-util", "clap", "color-eyre", - "color-print", "derive_more", "dirs", "easy-ext 1.0.1", @@ -2391,16 +2363,6 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b93853da6d84c2e3c7d730d6473e8817692dd89be387eb01b94d7f108ecb5b8c" -[[package]] -name = "nom" -version = "7.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" -dependencies = [ - "memchr", - "minimal-lexical", -] - [[package]] name = "nu-ansi-term" version = "0.46.0" diff --git a/Cargo.toml b/Cargo.toml index a7706a9f5..8037acbb5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -56,7 +56,6 @@ thiserror = "1" bytesize = "1.1.0" prettytable = "0.10.0" -color-print = "0.3.5" near-ledger = { version = "0.2.0", optional = true } diff --git a/src/commands/contract/view_storage/output_format/as_text.rs b/src/commands/contract/view_storage/output_format/as_text.rs index 07773111b..8db7c7896 100644 --- a/src/commands/contract/view_storage/output_format/as_text.rs +++ b/src/commands/contract/view_storage/output_format/as_text.rs @@ -1,5 +1,4 @@ -use color_eyre::eyre::Context; -use color_print::cprintln; +use color_eyre::{eyre::Context, owo_colors::OwoColorize}; use crate::common::JsonRpcClientExt; @@ -49,7 +48,7 @@ impl AsTextContext { .collect::>(), ) .wrap_err("Wrong format. utf-8 is expected.")?; - cprintln!("key:\n{}", key); + eprintln!("key:\n{}", key.green()); let val = String::from_utf8( value.value @@ -59,7 +58,7 @@ impl AsTextContext { .collect::>(), ) .wrap_err("Wrong format. utf-8 is expected.")?; - cprintln!("value:\n{}", val); + eprintln!("value:\n{}", val.yellow()); eprintln!("--------------------------------"); } From a572e0595998fd4a4874bb19d2f465c6fb661e1f Mon Sep 17 00:00:00 2001 From: FroVolod Date: Fri, 8 Sep 2023 20:07:57 +0300 Subject: [PATCH 14/17] refactored print for key/value --- .../view_storage/output_format/as_text.rs | 32 +++++++------------ 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/src/commands/contract/view_storage/output_format/as_text.rs b/src/commands/contract/view_storage/output_format/as_text.rs index 8db7c7896..6e79b4328 100644 --- a/src/commands/contract/view_storage/output_format/as_text.rs +++ b/src/commands/contract/view_storage/output_format/as_text.rs @@ -40,26 +40,8 @@ impl AsTextContext { { eprintln!("Contract state (values):"); for value in &result.values { - let key = String::from_utf8( - value.key - .as_slice() - .iter() - .flat_map(|b| std::ascii::escape_default(*b)) - .collect::>(), - ) - .wrap_err("Wrong format. utf-8 is expected.")?; - eprintln!("key:\n{}", key.green()); - - let val = String::from_utf8( - value.value - .as_slice() - .iter() - .flat_map(|b| std::ascii::escape_default(*b)) - .collect::>(), - ) - .wrap_err("Wrong format. utf-8 is expected.")?; - eprintln!("value:\n{}", val.yellow()); - + eprintln!("key:\n{}", key_value_to_string(&value.key)?.green()); + eprintln!("value:\n{}", key_value_to_string(&value.value)?.yellow()); eprintln!("--------------------------------"); } eprintln!( @@ -86,3 +68,13 @@ impl From for crate::network_view_at_block::ArgsForViewContext { item.0 } } + +fn key_value_to_string(slice: &[u8]) -> color_eyre::eyre::Result { + String::from_utf8( + slice + .iter() + .flat_map(|b| std::ascii::escape_default(*b)) + .collect::>(), + ) + .wrap_err("Wrong format. utf-8 is expected.") +} From 21f8eeefa96f138409d5950aaa8c5a0ba3cfb703 Mon Sep 17 00:00:00 2001 From: FroVolod Date: Sat, 9 Sep 2023 12:21:27 +0300 Subject: [PATCH 15/17] refactored KeysStartWithBytesAsBase64 --- .../keys_start_with_bytes_as_base64.rs | 4 +- src/types/base64_string.rs | 39 +++++++++++++++++++ src/types/mod.rs | 1 + 3 files changed, 42 insertions(+), 2 deletions(-) create mode 100644 src/types/base64_string.rs diff --git a/src/commands/contract/view_storage/keys_to_view/keys_start_with_bytes_as_base64.rs b/src/commands/contract/view_storage/keys_to_view/keys_start_with_bytes_as_base64.rs index dbd8ec434..58ab9f2b1 100644 --- a/src/commands/contract/view_storage/keys_to_view/keys_start_with_bytes_as_base64.rs +++ b/src/commands/contract/view_storage/keys_to_view/keys_start_with_bytes_as_base64.rs @@ -3,7 +3,7 @@ #[interactive_clap(output_context = KeysStartWithBytesAsBase64Context)] pub struct KeysStartWithBytesAsBase64 { /// Enter the string that the keys begin with Base64 bytes (for example, "Uw=="): - keys_begin_with: String, + keys_begin_with: crate::types::base64_string::Base64String, #[interactive_clap(subcommand)] output_format: super::super::output_format::OutputFormat, } @@ -20,7 +20,7 @@ impl KeysStartWithBytesAsBase64Context { global_context: previous_context.global_context, contract_account_id: previous_context.contract_account_id, prefix: near_primitives::types::StoreKey::from( - near_primitives::serialize::from_base64(&scope.keys_begin_with)?, + scope.keys_begin_with.inner.clone().into_bytes(), ), })) } diff --git a/src/types/base64_string.rs b/src/types/base64_string.rs new file mode 100644 index 000000000..1378a52c2 --- /dev/null +++ b/src/types/base64_string.rs @@ -0,0 +1,39 @@ +use near_primitives::borsh::BorshSerialize; + +#[derive(Debug, Clone)] +pub struct Base64String { + pub inner: String, +} + +impl interactive_clap::ToCli for Base64String { + type CliVariant = Base64String; +} + +impl std::str::FromStr for Base64String { + type Err = String; + fn from_str(s: &str) -> Result { + Ok(Self { + inner: String::from_utf8(near_primitives::serialize::from_base64(s).map_err( + |err| { + format!( + "parsing action {s} failed due to invalid base64 sequence: {}", + err + ) + }, + )?) + .map_err(|err| format!("Base64String not be deserialized from utf8: {}", err))?, + }) + } +} + +impl std::fmt::Display for Base64String { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let base64_string = near_primitives::serialize::to_base64( + &self + .inner + .try_to_vec() + .expect("Base64String is not expected to fail on serialization"), + ); + write!(f, "{}", base64_string) + } +} diff --git a/src/types/mod.rs b/src/types/mod.rs index 78706ad08..75ef68491 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -1,5 +1,6 @@ pub mod account_id; pub mod api_key; +pub mod base64_string; pub mod crypto_hash; pub mod json; pub mod path_buf; From 08297730b9a10b206589b127bdf8a2e3916ec068 Mon Sep 17 00:00:00 2001 From: FroVolod Date: Sat, 9 Sep 2023 13:08:43 +0300 Subject: [PATCH 16/17] refactored KeysStartWithBytesAsBase64 --- .../keys_start_with_bytes_as_base64.rs | 6 +-- src/types/base64_bytes.rs | 28 +++++++++++++ src/types/base64_string.rs | 39 ------------------- src/types/mod.rs | 2 +- 4 files changed, 31 insertions(+), 44 deletions(-) create mode 100644 src/types/base64_bytes.rs delete mode 100644 src/types/base64_string.rs diff --git a/src/commands/contract/view_storage/keys_to_view/keys_start_with_bytes_as_base64.rs b/src/commands/contract/view_storage/keys_to_view/keys_start_with_bytes_as_base64.rs index 58ab9f2b1..079639c81 100644 --- a/src/commands/contract/view_storage/keys_to_view/keys_start_with_bytes_as_base64.rs +++ b/src/commands/contract/view_storage/keys_to_view/keys_start_with_bytes_as_base64.rs @@ -3,7 +3,7 @@ #[interactive_clap(output_context = KeysStartWithBytesAsBase64Context)] pub struct KeysStartWithBytesAsBase64 { /// Enter the string that the keys begin with Base64 bytes (for example, "Uw=="): - keys_begin_with: crate::types::base64_string::Base64String, + keys_begin_with: crate::types::base64_bytes::Base64Bytes, #[interactive_clap(subcommand)] output_format: super::super::output_format::OutputFormat, } @@ -19,9 +19,7 @@ impl KeysStartWithBytesAsBase64Context { Ok(Self(super::KeysContext { global_context: previous_context.global_context, contract_account_id: previous_context.contract_account_id, - prefix: near_primitives::types::StoreKey::from( - scope.keys_begin_with.inner.clone().into_bytes(), - ), + prefix: near_primitives::types::StoreKey::from(scope.keys_begin_with.inner.clone()), })) } } diff --git a/src/types/base64_bytes.rs b/src/types/base64_bytes.rs new file mode 100644 index 000000000..b5e8ebb6f --- /dev/null +++ b/src/types/base64_bytes.rs @@ -0,0 +1,28 @@ +#[derive(Debug, Clone)] +pub struct Base64Bytes { + pub inner: Vec, +} + +impl interactive_clap::ToCli for Base64Bytes { + type CliVariant = Base64Bytes; +} + +impl std::str::FromStr for Base64Bytes { + type Err = String; + fn from_str(s: &str) -> Result { + Ok(Self { + inner: near_primitives::serialize::from_base64(s).map_err(|err| { + format!( + "parsing action {s} failed due to invalid base64 sequence: {}", + err + ) + })?, + }) + } +} + +impl std::fmt::Display for Base64Bytes { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", near_primitives::serialize::to_base64(&self.inner)) + } +} diff --git a/src/types/base64_string.rs b/src/types/base64_string.rs deleted file mode 100644 index 1378a52c2..000000000 --- a/src/types/base64_string.rs +++ /dev/null @@ -1,39 +0,0 @@ -use near_primitives::borsh::BorshSerialize; - -#[derive(Debug, Clone)] -pub struct Base64String { - pub inner: String, -} - -impl interactive_clap::ToCli for Base64String { - type CliVariant = Base64String; -} - -impl std::str::FromStr for Base64String { - type Err = String; - fn from_str(s: &str) -> Result { - Ok(Self { - inner: String::from_utf8(near_primitives::serialize::from_base64(s).map_err( - |err| { - format!( - "parsing action {s} failed due to invalid base64 sequence: {}", - err - ) - }, - )?) - .map_err(|err| format!("Base64String not be deserialized from utf8: {}", err))?, - }) - } -} - -impl std::fmt::Display for Base64String { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let base64_string = near_primitives::serialize::to_base64( - &self - .inner - .try_to_vec() - .expect("Base64String is not expected to fail on serialization"), - ); - write!(f, "{}", base64_string) - } -} diff --git a/src/types/mod.rs b/src/types/mod.rs index 75ef68491..a618d8632 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -1,6 +1,6 @@ pub mod account_id; pub mod api_key; -pub mod base64_string; +pub mod base64_bytes; pub mod crypto_hash; pub mod json; pub mod path_buf; From 61f3c79de487a799870f715115269849ce38165f Mon Sep 17 00:00:00 2001 From: Vlad Frolov Date: Sat, 9 Sep 2023 12:18:40 +0200 Subject: [PATCH 17/17] Update src/commands/contract/view_storage/output_format/as_json.rs --- src/commands/contract/view_storage/output_format/as_json.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/commands/contract/view_storage/output_format/as_json.rs b/src/commands/contract/view_storage/output_format/as_json.rs index 9228ff75c..b903a539a 100644 --- a/src/commands/contract/view_storage/output_format/as_json.rs +++ b/src/commands/contract/view_storage/output_format/as_json.rs @@ -38,7 +38,7 @@ impl AsJsonContext { query_view_method_response.kind { eprintln!("Contract state (values):"); - eprintln!( + println!( "{}", serde_json::to_string_pretty(&result.values)? );