From 61650d994235fce8eacedad9259854717fd31d2e Mon Sep 17 00:00:00 2001 From: Jonathan LEI Date: Mon, 8 Jul 2024 04:59:33 +0800 Subject: [PATCH] feat: `--block` option for `call` command --- src/block_id.rs | 50 +++++++++++++++++++++++++++++++++ src/main.rs | 1 + src/subcommands/block.rs | 15 +++++----- src/subcommands/block_time.rs | 14 +++++---- src/subcommands/block_traces.rs | 12 ++++---- src/subcommands/call.rs | 15 +++++++--- src/subcommands/state_update.rs | 11 ++++---- src/utils.rs | 18 ++---------- 8 files changed, 90 insertions(+), 46 deletions(-) create mode 100644 src/block_id.rs diff --git a/src/block_id.rs b/src/block_id.rs new file mode 100644 index 0000000..b113f64 --- /dev/null +++ b/src/block_id.rs @@ -0,0 +1,50 @@ +use clap::{builder::TypedValueParser, error::ErrorKind, Arg, Command, Error}; +use regex::Regex; +use starknet::core::types::{BlockId, BlockTag, Felt}; + +#[derive(Clone)] +pub struct BlockIdParser; + +impl TypedValueParser for BlockIdParser { + type Value = BlockId; + + fn parse_ref( + &self, + cmd: &Command, + _arg: Option<&Arg>, + value: &std::ffi::OsStr, + ) -> Result { + if value.is_empty() { + Err(cmd.clone().error(ErrorKind::InvalidValue, "empty block ID")) + } else { + match value.to_str() { + Some(value) => { + let regex_block_number = Regex::new("^[0-9]{1,}$").unwrap(); + + if value == "latest" { + Ok(BlockId::Tag(BlockTag::Latest)) + } else if value == "pending" { + Ok(BlockId::Tag(BlockTag::Pending)) + } else if regex_block_number.is_match(value) { + Ok(BlockId::Number(value.parse::().map_err(|err| { + cmd.clone().error( + ErrorKind::InvalidValue, + &format!("invalid block number: {}", err), + ) + })?)) + } else { + Ok(BlockId::Hash(Felt::from_hex(value).map_err(|err| { + cmd.clone().error( + ErrorKind::InvalidValue, + &format!("invalid block hash: {}", err), + ) + })?)) + } + } + None => Err(cmd + .clone() + .error(ErrorKind::InvalidValue, "invalid block ID")), + } + } + } +} diff --git a/src/main.rs b/src/main.rs index 374107a..f53025c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,6 +7,7 @@ use crate::{provider::ProviderArgs, subcommands::*}; mod account; mod account_factory; mod address_book; +mod block_id; mod casm; mod chain_id; mod compiler; diff --git a/src/subcommands/block.rs b/src/subcommands/block.rs index 99663d2..31bc2ed 100644 --- a/src/subcommands/block.rs +++ b/src/subcommands/block.rs @@ -1,9 +1,9 @@ use anyhow::Result; use clap::Parser; use colored_json::{ColorMode, Output}; -use starknet::providers::Provider; +use starknet::{core::types::BlockId, providers::Provider}; -use crate::{utils::parse_block_id, verbosity::VerbosityArgs, ProviderArgs}; +use crate::{block_id::BlockIdParser, verbosity::VerbosityArgs, ProviderArgs}; #[derive(Debug, Parser)] pub struct Block { @@ -14,10 +14,11 @@ pub struct Block { #[clap(long, help = "Fetch receipts alongside transactions")] receipts: bool, #[clap( + value_parser = BlockIdParser, default_value = "latest", help = "Block number, hash, or tag (latest/pending)" )] - block_id: String, + block_id: BlockId, #[clap(flatten)] verbosity: VerbosityArgs, } @@ -28,14 +29,12 @@ impl Block { let provider = self.provider.into_provider()?; - let block_id = parse_block_id(&self.block_id)?; - let block_json = if self.receipts { - serde_json::to_value(provider.get_block_with_receipts(block_id).await?)? + serde_json::to_value(provider.get_block_with_receipts(self.block_id).await?)? } else if self.full { - serde_json::to_value(provider.get_block_with_txs(block_id).await?)? + serde_json::to_value(provider.get_block_with_txs(self.block_id).await?)? } else { - serde_json::to_value(provider.get_block_with_tx_hashes(block_id).await?)? + serde_json::to_value(provider.get_block_with_tx_hashes(self.block_id).await?)? }; let block_json = diff --git a/src/subcommands/block_time.rs b/src/subcommands/block_time.rs index c613040..a159f1d 100644 --- a/src/subcommands/block_time.rs +++ b/src/subcommands/block_time.rs @@ -1,9 +1,12 @@ use anyhow::Result; use chrono::{TimeZone, Utc}; use clap::Parser; -use starknet::{core::types::MaybePendingBlockWithTxHashes, providers::Provider}; +use starknet::{ + core::types::{BlockId, MaybePendingBlockWithTxHashes}, + providers::Provider, +}; -use crate::{utils::parse_block_id, verbosity::VerbosityArgs, ProviderArgs}; +use crate::{block_id::BlockIdParser, verbosity::VerbosityArgs, ProviderArgs}; #[derive(Debug, Parser)] pub struct BlockTime { @@ -22,10 +25,11 @@ pub struct BlockTime { )] rfc2822: bool, #[clap( + value_parser = BlockIdParser, default_value = "latest", help = "Block number, hash, or tag (latest/pending)" )] - block_id: String, + block_id: BlockId, #[clap(flatten)] verbosity: VerbosityArgs, } @@ -36,9 +40,7 @@ impl BlockTime { let provider = self.provider.into_provider()?; - let block_id = parse_block_id(&self.block_id)?; - - let block = provider.get_block_with_tx_hashes(block_id).await?; + let block = provider.get_block_with_tx_hashes(self.block_id).await?; let timestamp = match block { MaybePendingBlockWithTxHashes::Block(block) => block.timestamp, MaybePendingBlockWithTxHashes::PendingBlock(block) => block.timestamp, diff --git a/src/subcommands/block_traces.rs b/src/subcommands/block_traces.rs index 7a33c7b..139b20b 100644 --- a/src/subcommands/block_traces.rs +++ b/src/subcommands/block_traces.rs @@ -1,19 +1,20 @@ use anyhow::Result; use clap::Parser; use colored_json::{ColorMode, Output}; -use starknet::providers::Provider; +use starknet::{core::types::BlockId, providers::Provider}; -use crate::{utils::parse_block_id, verbosity::VerbosityArgs, ProviderArgs}; +use crate::{block_id::BlockIdParser, verbosity::VerbosityArgs, ProviderArgs}; #[derive(Debug, Parser)] pub struct BlockTraces { #[clap(flatten)] provider: ProviderArgs, #[clap( + value_parser = BlockIdParser, default_value = "latest", help = "Block number, hash, or tag (latest/pending)" )] - block_id: String, + block_id: BlockId, #[clap(flatten)] verbosity: VerbosityArgs, } @@ -24,9 +25,8 @@ impl BlockTraces { let provider = self.provider.into_provider()?; - let block_id = parse_block_id(&self.block_id)?; - - let traces_json = serde_json::to_value(provider.trace_block_transactions(block_id).await?)?; + let traces_json = + serde_json::to_value(provider.trace_block_transactions(self.block_id).await?)?; let traces_json = colored_json::to_colored_json(&traces_json, ColorMode::Auto(Output::StdOut))?; diff --git a/src/subcommands/call.rs b/src/subcommands/call.rs index 6894d08..14e83c4 100644 --- a/src/subcommands/call.rs +++ b/src/subcommands/call.rs @@ -3,19 +3,26 @@ use std::sync::Arc; use anyhow::Result; use clap::Parser; use starknet::{ - core::types::{BlockId, BlockTag, FunctionCall}, + core::types::{BlockId, FunctionCall}, providers::Provider, }; use crate::{ - address_book::AddressBookResolver, decode::FeltDecoder, error::provider_error_mapper, - verbosity::VerbosityArgs, ProviderArgs, + address_book::AddressBookResolver, block_id::BlockIdParser, decode::FeltDecoder, + error::provider_error_mapper, verbosity::VerbosityArgs, ProviderArgs, }; #[derive(Debug, Parser)] pub struct Call { #[clap(flatten)] provider: ProviderArgs, + #[clap( + long, + value_parser = BlockIdParser, + default_value = "pending", + help = "Block number, hash, or tag (latest/pending)" + )] + block: BlockId, #[clap(help = "Contract address")] contract_address: String, #[clap(help = "Name of the function being called")] @@ -52,7 +59,7 @@ impl Call { entry_point_selector: selector, calldata, }, - BlockId::Tag(BlockTag::Pending), + self.block, ) .await .map_err(provider_error_mapper)?; diff --git a/src/subcommands/state_update.rs b/src/subcommands/state_update.rs index 1ec4b4a..31c209f 100644 --- a/src/subcommands/state_update.rs +++ b/src/subcommands/state_update.rs @@ -1,19 +1,20 @@ use anyhow::Result; use clap::Parser; use colored_json::{ColorMode, Output}; -use starknet::providers::Provider; +use starknet::{core::types::BlockId, providers::Provider}; -use crate::{utils::parse_block_id, verbosity::VerbosityArgs, ProviderArgs}; +use crate::{block_id::BlockIdParser, verbosity::VerbosityArgs, ProviderArgs}; #[derive(Debug, Parser)] pub struct StateUpdate { #[clap(flatten)] provider: ProviderArgs, #[clap( + value_parser = BlockIdParser, default_value = "latest", help = "Block number, hash, or tag (latest/pending)" )] - block_id: String, + block_id: BlockId, #[clap(flatten)] verbosity: VerbosityArgs, } @@ -24,9 +25,7 @@ impl StateUpdate { let provider = self.provider.into_provider()?; - let block_id = parse_block_id(&self.block_id)?; - - let update_json = serde_json::to_value(provider.get_state_update(block_id).await?)?; + let update_json = serde_json::to_value(provider.get_state_update(self.block_id).await?)?; let update_json = colored_json::to_colored_json(&update_json, ColorMode::Auto(Output::StdOut))?; diff --git a/src/utils.rs b/src/utils.rs index 5e60468..71db3b2 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -19,8 +19,8 @@ use starknet::{ }, AbiEntry, SierraClass, SierraClassDebugInfo, }, - BlockId, BlockTag, CompressedLegacyContractClass, ExecutionResult, Felt, - FlattenedSierraClass, LegacyContractEntryPoint, StarknetError, + CompressedLegacyContractClass, ExecutionResult, Felt, FlattenedSierraClass, + LegacyContractEntryPoint, StarknetError, }, providers::{Provider, ProviderError}, }; @@ -54,20 +54,6 @@ where } } -pub fn parse_block_id(id: &str) -> Result { - let regex_block_number = Regex::new("^[0-9]{1,}$").unwrap(); - - if id == "latest" { - Ok(BlockId::Tag(BlockTag::Latest)) - } else if id == "pending" { - Ok(BlockId::Tag(BlockTag::Pending)) - } else if regex_block_number.is_match(id) { - Ok(BlockId::Number(id.parse::()?)) - } else { - Ok(BlockId::Hash(Felt::from_hex(id)?)) - } -} - pub fn parse_felt_value(felt: &str) -> Result { let regex_dec_number = Regex::new("^[0-9]{1,}$").unwrap();