use super::DefaultConfig;
use anyhow::Result;
use colored::Colorize;
use comfy_table::{
ContentArrangement,
Table,
};
use contract_extrinsics::{
ContractArtifacts,
ContractStorage,
ContractStorageLayout,
ContractStorageRpc,
ErrorVariant,
};
use ink_env::DefaultEnvironment;
use std::path::PathBuf;
use subxt::Config;
#[derive(Debug, clap::Args)]
#[clap(name = "storage", about = "Inspect contract storage")]
pub struct StorageCommand {
#[clap(name = "contract", long, env = "CONTRACT")]
contract: <DefaultConfig as Config>::AccountId,
#[clap(long)]
raw: bool,
#[clap(name = "output-json", long, conflicts_with = "raw")]
output_json: bool,
#[clap(value_parser, conflicts_with = "manifest_path")]
file: Option<PathBuf>,
#[clap(long, value_parser)]
manifest_path: Option<PathBuf>,
#[clap(
name = "url",
long,
value_parser,
default_value = "ws://localhost:9944"
)]
url: url::Url,
}
impl StorageCommand {
pub async fn run(&self) -> Result<(), ErrorVariant> {
let rpc = ContractStorageRpc::<DefaultConfig>::new(&self.url).await?;
let storage_layout =
ContractStorage::<DefaultConfig, DefaultEnvironment>::new(rpc);
if self.raw {
let storage_data = storage_layout
.load_contract_storage_data(&self.contract)
.await?;
println!(
"{json}",
json = serde_json::to_string_pretty(&storage_data)?
);
return Ok(())
}
let contract_artifacts = ContractArtifacts::from_manifest_or_file(
self.manifest_path.as_ref(),
self.file.as_ref(),
);
match contract_artifacts {
Ok(contract_artifacts) => {
let transcoder = contract_artifacts.contract_transcoder()?;
let contract_storage = storage_layout
.load_contract_storage_with_layout(&self.contract, &transcoder)
.await?;
if self.output_json {
println!(
"{json}",
json = serde_json::to_string_pretty(&contract_storage)?
);
} else {
let table = StorageDisplayTable::new(&contract_storage);
table.display();
}
}
Err(_) => {
eprintln!(
"{} Displaying raw storage: no valid contract metadata artifacts found",
"Info:".cyan().bold(),
);
let storage_data = storage_layout
.load_contract_storage_data(&self.contract)
.await?;
println!(
"{json}",
json = serde_json::to_string_pretty(&storage_data)?
);
return Ok(())
}
}
Ok(())
}
}
struct StorageDisplayTable(Table);
impl StorageDisplayTable {
const INDEX_LABEL: &'static str = "Index";
const KEY_LABEL: &'static str = "Root Key";
const PARENT_LABEL: &'static str = "Parent";
const VALUE_LABEL: &'static str = "Value";
fn new(storage_layout: &ContractStorageLayout) -> Self {
let mut table = Table::new();
Self::table_add_header(&mut table);
Self::table_add_rows(&mut table, storage_layout);
Self(table)
}
fn table_add_header(table: &mut Table) {
table.set_content_arrangement(ContentArrangement::Dynamic);
let header = vec![
Self::INDEX_LABEL,
Self::KEY_LABEL,
Self::PARENT_LABEL,
Self::VALUE_LABEL,
];
table.set_header(header);
}
fn table_add_rows(table: &mut Table, storage_layout: &ContractStorageLayout) {
for (index, cell) in storage_layout.iter().enumerate() {
let formatted_cell = format!("{cell}");
let values = formatted_cell.split('\n');
for (i, v) in values.enumerate() {
table.add_row(vec![
(index + i).to_string().as_str(),
cell.root_key().as_str(),
cell.parent().as_str(),
v,
]);
}
}
}
fn display(&self) {
println!("{}", self.0);
}
}