Skip to content

Commit

Permalink
feat: cast abi-decode, min-int (gakonst#284)
Browse files Browse the repository at this point in the history
* chore: cherry pick commits

* feat: abi-decode

* chore: lint

* chore: move abi-decode logic to SimpleCast impl

* chore: add doc  test for abi-decode

* feat min --int, add doc output tst to abi_decode

* chore: cargo fmt, clippy, Cargo.lock

* chore: update README

* chore: simplify abi-decode impl

* feat(cast): --calldata-decode and adjust abi-decode cli cmd

* chore: typo

Co-authored-by: Georgios Konstantopoulos <[email protected]>
  • Loading branch information
odyslam and gakonst authored Dec 26, 2021
1 parent a6af7b5 commit a1dd429
Show file tree
Hide file tree
Showing 8 changed files with 125 additions and 5 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions cast/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ eyre = "0.6.5"
rustc-hex = "2.1.0"
serde_json = "1.0.67"
chrono = "0.2"
hex = "0.4.3"

[features]
default = ["ledger", "trezor"]
Expand Down
6 changes: 3 additions & 3 deletions cast/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@

## Features

- [ ] `--abi-decode`
- [ ] `--calldata-decode`
- [x] `--abi-decode`
- [x] `--calldata-decode`
- [x] `--from-ascii` (with `--from-utf8` alias)
- [ ] `--from-bin`
- [ ] `--from-fix`
- [x] `--from-wei`
- [x] `--max-int`
- [x] `--max-uint`
- [ ] `--min-int`
- [x] `--min-int`
- [x] `--to-checksum-address` (`--to-address` in dapptools)
- [x] `--to-ascii`
- [x] `--to-bytes32`
Expand Down
63 changes: 62 additions & 1 deletion cast/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
//! TODO
use chrono::NaiveDateTime;
use ethers_core::{
abi::AbiParser,
abi::{AbiParser, Token},
types::*,
utils::{self, keccak256},
};
Expand Down Expand Up @@ -446,6 +446,21 @@ impl SimpleCast {
Ok(I256::MAX)
}

/// Returns minimum I256 value
///
/// ```
/// use cast::SimpleCast as Cast;
/// use ethers_core::types::I256;
///
/// fn main() -> eyre::Result<()> {
/// assert_eq!(I256::MIN, Cast::min_int()?);
///
/// Ok(())
/// }
/// ```
pub fn min_int() -> Result<I256> {
Ok(I256::MIN)
}
/// Returns maximum U256 value
///
/// ```
Expand Down Expand Up @@ -490,6 +505,52 @@ impl SimpleCast {
Ok(value)
}
}
/// Decodes abi-encoded hex input or output
///
/// ```
/// use cast::SimpleCast as Cast;
///
/// fn main() -> eyre::Result<()> {
/// // Passing `input = false` will decode the data as the output type.
/// // The input data types and the full function sig are ignored, i.e.
/// // you could also pass `balanceOf()(uint256)` and it'd still work.
/// let data = "0x0000000000000000000000000000000000000000000000000000000000000001";
/// let sig = "balanceOf(address, uint256)(uint256)";
/// let decoded = Cast::abi_decode(sig, data, false)?[0].to_string();
/// assert_eq!(decoded, "1");
///
/// // Passing `input = true` will decode the data with the input function signature.
/// let data = "0xf242432a0000000000000000000000008dbd1b711dc621e1404633da156fcc779e1c6f3e000000000000000000000000d9f3c9cc99548bf3b44a43e0a2d07399eb918adc000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000";
/// let sig = "safeTransferFrom(address, address, uint256, uint256, bytes)";
/// let decoded = Cast::abi_decode(sig, data, false)?;
/// let decoded = decoded.iter().map(ToString::to_string).collect::<Vec<_>>();
/// assert_eq!(
/// decoded,
/// vec!["8dbd1b711dc621e1404633da156fcc779e1c6f3e", "d9f3c9cc99548bf3b44a43e0a2d07399eb918adc", "2a", "1", ""]
/// );
///
///
/// # Ok(())
/// }
/// ```
pub fn abi_decode(sig: &str, calldata: &str, input: bool) -> Result<Vec<Token>> {
let func = foundry_utils::IntoFunction::into(sig);
let calldata = calldata.strip_prefix("0x").unwrap_or(calldata);
let calldata = hex::decode(calldata)?;
let res = if input {
// need to strip the function selector
func.decode_input(&calldata[4..])?
} else {
func.decode_output(&calldata)?
};

// in case the decoding worked but nothing was decoded
if res.is_empty() {
eyre::bail!("no data was decoded")
}

Ok(res)
}

/// Converts decimal input to hex
///
Expand Down
1 change: 1 addition & 0 deletions cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ ansi_term = "0.12.1"
rpassword = "5.0.1"
tracing-subscriber = "0.2.20"
tracing = "0.1.26"
hex = "0.4.3"

## EVM Implementations
# evm = { version = "0.30.1" }
Expand Down
13 changes: 13 additions & 0 deletions cli/src/cast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ async fn main() -> eyre::Result<()> {
Subcommands::MaxInt => {
println!("{}", SimpleCast::max_int()?);
}
Subcommands::MinInt => {
println!("{}", SimpleCast::min_int()?);
}
Subcommands::MaxUint => {
println!("{}", SimpleCast::max_uint()?);
}
Expand Down Expand Up @@ -162,6 +165,16 @@ async fn main() -> eyre::Result<()> {
cast_send(provider, from, to, sig, args, cast_async).await?;
}
}
Subcommands::CalldataDecode { sig, calldata } => {
let tokens = SimpleCast::abi_decode(&sig, &calldata, true)?;
let tokens = utils::format_tokens(&tokens);
tokens.for_each(|t| println!("{}", t));
}
Subcommands::AbiDecode { sig, calldata, input } => {
let tokens = SimpleCast::abi_decode(&sig, &calldata, input)?;
let tokens = utils::format_tokens(&tokens);
tokens.for_each(|t| println!("{}", t));
}
Subcommands::Age { block, rpc_url } => {
let provider = Provider::try_from(rpc_url)?;
println!(
Expand Down
29 changes: 29 additions & 0 deletions cli/src/opts/cast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ pub enum Subcommands {
#[structopt(name = "--max-int")]
#[structopt(about = "maximum i256 value")]
MaxInt,
#[structopt(name = "--min-int")]
#[structopt(about = "minimum i256 value")]
MinInt,
#[structopt(name = "--max-uint")]
#[structopt(about = "maximum u256 value")]
MaxUint,
Expand Down Expand Up @@ -137,6 +140,32 @@ pub enum Subcommands {
#[structopt(flatten)]
eth: EthereumOpts,
},
#[structopt(name = "--calldata-decode")]
#[structopt(
about = "Decode ABI-encoded hex input data. Use `--abi-decode` to decode output data"
)]
CalldataDecode {
#[structopt(
help = "the function signature you want to decode, in the format `<name>(<in-types>)(<out-types>)`"
)]
sig: String,
#[structopt(help = "the encoded calladata, in hex format")]
calldata: String,
},
#[structopt(name = "--abi-decode")]
#[structopt(
about = "Decode ABI-encoded hex output data. Pass --input to decode as input, or use `--calldata-decode`"
)]
AbiDecode {
#[structopt(
help = "the function signature you want to decode, in the format `<name>(<in-types>)(<out-types>)`"
)]
sig: String,
#[structopt(help = "the encoded calladata, in hex format")]
calldata: String,
#[structopt(long, short, help = "the encoded output, in hex format")]
input: bool,
},
#[structopt(name = "age")]
#[structopt(about = "Prints the timestamp of a block")]
Age {
Expand Down
15 changes: 14 additions & 1 deletion cli/src/utils.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
use ethers::solc::{artifacts::Contract, EvmVersion};
use ethers::{
abi::Token,
solc::{artifacts::Contract, EvmVersion},
};

use eyre::{ContextCompat, WrapErr};
use std::{
Expand Down Expand Up @@ -122,6 +125,16 @@ fn find_fave_or_alt_path(root: impl AsRef<Path>, fave: &str, alt: &str) -> PathB
p
}

// need some special handling to print the types nicely
#[allow(dead_code)]
pub fn format_tokens(tokens: &[Token]) -> impl Iterator<Item = String> + '_ {
tokens.iter().map(|token| match token {
Token::Address(inner) => format!("{:?}", inner),
Token::Uint(inner) => format!("{}", inner),
other => other.to_string(),
})
}

#[cfg(feature = "sputnik-evm")]
pub fn sputnik_cfg(evm: EvmVersion) -> Config {
match evm {
Expand Down

0 comments on commit a1dd429

Please sign in to comment.