Skip to content

Commit

Permalink
cast: eip1967 commands (#4258)
Browse files Browse the repository at this point in the history
* cast: eip1967 commands

Implements two new commands for `cast` that fetch common
EIP-1967 storage slots and print the values as address
strings.

```
$ cast admin
$ cast implementation
```

This makes it very easy to determine if a contract is proxied.

* lint: fix

* clippy: make happy
  • Loading branch information
tynes authored Feb 3, 2023
1 parent 6157d4a commit ecb6bf2
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 0 deletions.
58 changes: 58 additions & 0 deletions cast/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,64 @@ where
Ok(self.provider.get_transaction_count(who, block).await?)
}

/// # Example
///
/// ```no_run
/// use cast::Cast;
/// use ethers_providers::{Provider, Http};
/// use ethers_core::types::Address;
/// use std::{str::FromStr, convert::TryFrom};
///
/// # async fn foo() -> eyre::Result<()> {
/// let provider = Provider::<Http>::try_from("http://localhost:8545")?;
/// let cast = Cast::new(provider);
/// let addr = Address::from_str("0x7eD52863829AB99354F3a0503A622e82AcD5F7d3")?;
/// let implementation = cast.implementation(addr, None).await?;
/// println!("{}", implementation);
/// # Ok(())
/// # }
/// ```
pub async fn implementation<T: Into<NameOrAddress> + Send + Sync>(
&self,
who: T,
block: Option<BlockId>,
) -> Result<String> {
let slot =
H256::from_str("0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc")?;
let value = self.provider.get_storage_at(who, slot, block).await?;
let addr: H160 = value.into();
Ok(format!("{addr:?}"))
}

/// # Example
///
/// ```no_run
/// use cast::Cast;
/// use ethers_providers::{Provider, Http};
/// use ethers_core::types::Address;
/// use std::{str::FromStr, convert::TryFrom};
///
/// # async fn foo() -> eyre::Result<()> {
/// let provider = Provider::<Http>::try_from("http://localhost:8545")?;
/// let cast = Cast::new(provider);
/// let addr = Address::from_str("0x7eD52863829AB99354F3a0503A622e82AcD5F7d3")?;
/// let admin = cast.admin(addr, None).await?;
/// println!("{}", admin);
/// # Ok(())
/// # }
/// ```
pub async fn admin<T: Into<NameOrAddress> + Send + Sync>(
&self,
who: T,
block: Option<BlockId>,
) -> Result<String> {
let slot =
H256::from_str("0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103")?;
let value = self.provider.get_storage_at(who, slot, block).await?;
let addr: H160 = value.into();
Ok(format!("{addr:?}"))
}

/// # Example
///
/// ```no_run
Expand Down
10 changes: 10 additions & 0 deletions cli/src/cast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,16 @@ async fn main() -> eyre::Result<()> {
let encoded = SimpleCast::index(&key_type, &key, &slot_number)?;
println!("{encoded}");
}
Subcommands::Implementation { block, who, rpc_url } => {
let rpc_url = try_consume_config_rpc_url(rpc_url)?;
let provider = try_get_http_provider(rpc_url)?;
println!("{}", Cast::new(provider).implementation(who, block).await?);
}
Subcommands::Admin { block, who, rpc_url } => {
let rpc_url = try_consume_config_rpc_url(rpc_url)?;
let provider = try_get_http_provider(rpc_url)?;
println!("{}", Cast::new(provider).admin(who, block).await?);
}
Subcommands::Nonce { block, who, rpc_url } => {
let rpc_url = try_consume_config_rpc_url(rpc_url)?;
let provider = try_get_http_provider(rpc_url)?;
Expand Down
36 changes: 36 additions & 0 deletions cli/src/opts/cast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,42 @@ Defaults to decoding output data. To decode input data pass --input or use cast
#[clap(help = "The storage slot of the mapping.", value_name = "SLOT_NUMBER")]
slot_number: String,
},
#[clap(name = "implementation")]
#[clap(visible_alias = "impl")]
#[clap(about = "Fetch the EIP-1967 implementation account")]
Implementation {
#[clap(
long,
short = 'B',
help = "The block height you want to query at.",
long_help = "The block height you want to query at. Can also be the tags earliest, latest, or pending.",
value_parser = parse_block_id,
value_name = "BLOCK"
)]
block: Option<BlockId>,
#[clap(help = "The address you want to get the nonce for.", value_parser = parse_name_or_address, value_name = "WHO")]
who: NameOrAddress,
#[clap(short, long, env = "ETH_RPC_URL", value_name = "URL")]
rpc_url: Option<String>,
},
#[clap(name = "admin")]
#[clap(visible_alias = "adm")]
#[clap(about = "Fetch the EIP-1967 admin account")]
Admin {
#[clap(
long,
short = 'B',
help = "The block height you want to query at.",
long_help = "The block height you want to query at. Can also be the tags earliest, latest, or pending.",
value_parser = parse_block_id,
value_name = "BLOCK"
)]
block: Option<BlockId>,
#[clap(help = "The address you want to get the nonce for.", value_parser = parse_name_or_address, value_name = "WHO")]
who: NameOrAddress,
#[clap(short, long, env = "ETH_RPC_URL", value_name = "URL")]
rpc_url: Option<String>,
},
#[clap(name = "4byte")]
#[clap(visible_aliases = &["4", "4b"])]
#[clap(
Expand Down

0 comments on commit ecb6bf2

Please sign in to comment.