diff --git a/crates/cast/bin/args.rs b/crates/cast/bin/args.rs index 47bf9a8849f60..2002bc0324f8d 100644 --- a/crates/cast/bin/args.rs +++ b/crates/cast/bin/args.rs @@ -606,7 +606,7 @@ pub enum CastSubcommand { formula_id: String, }, - /// Fetch the EIP-1967 implementation account + /// Fetch the EIP-1967 implementation for a contract, in the implementation slot or the beacon slot. #[command(visible_alias = "impl")] Implementation { /// The block height to query at. @@ -615,7 +615,13 @@ pub enum CastSubcommand { #[arg(long, short = 'B')] block: Option, - /// The address to get the nonce for. + /// Fetch the implementation from the beacon slot. + /// + /// If not specified, the implementation slot is used. + #[arg(long)] + beacon: bool, + + /// The address for which the implementation will be fetched. #[arg(value_parser = NameOrAddress::from_str)] who: NameOrAddress, @@ -632,7 +638,7 @@ pub enum CastSubcommand { #[arg(long, short = 'B')] block: Option, - /// The address to get the nonce for. + /// The address from which the admin account will be fetched. #[arg(value_parser = NameOrAddress::from_str)] who: NameOrAddress, diff --git a/crates/cast/bin/main.rs b/crates/cast/bin/main.rs index 01bab16ac16ce..0d2de240024c5 100644 --- a/crates/cast/bin/main.rs +++ b/crates/cast/bin/main.rs @@ -425,11 +425,11 @@ async fn main_args(args: CastArgs) -> Result<()> { let id = stdin::unwrap_line(id)?; sh_println!("{}", foundry_common::erc7201(&id))?; } - CastSubcommand::Implementation { block, who, rpc } => { + CastSubcommand::Implementation { block, beacon, who, rpc } => { let config = Config::from(&rpc); let provider = utils::get_provider(&config)?; let who = who.resolve(&provider).await?; - sh_println!("{}", Cast::new(provider).implementation(who, block).await?)?; + sh_println!("{}", Cast::new(provider).implementation(who, beacon, block).await?)?; } CastSubcommand::Admin { block, who, rpc } => { let config = Config::from(&rpc); diff --git a/crates/cast/src/lib.rs b/crates/cast/src/lib.rs index 01d7da6efc80f..cb7f48731f613 100644 --- a/crates/cast/src/lib.rs +++ b/crates/cast/src/lib.rs @@ -571,14 +571,33 @@ where /// ProviderBuilder::<_, _, AnyNetwork>::default().on_builtin("http://localhost:8545").await?; /// let cast = Cast::new(provider); /// let addr = Address::from_str("0x7eD52863829AB99354F3a0503A622e82AcD5F7d3")?; - /// let implementation = cast.implementation(addr, None).await?; + /// let implementation = cast.implementation(addr, false, None).await?; /// println!("{}", implementation); /// # Ok(()) /// # } /// ``` - pub async fn implementation(&self, who: Address, block: Option) -> Result { - let slot = - B256::from_str("0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc")?; + pub async fn implementation( + &self, + who: Address, + is_beacon: bool, + block: Option, + ) -> Result { + let slot; + match is_beacon { + true => { + // Use the beacon slot : bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1) + slot = B256::from_str( + "0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50", + )?; + } + false => { + // Use the implementation slot : bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1) + slot = B256::from_str( + "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc", + )?; + } + } + let value = self .provider .get_storage_at(who, slot.into()) diff --git a/crates/cast/tests/cli/main.rs b/crates/cast/tests/cli/main.rs index df98427224749..4b916cbff7c52 100644 --- a/crates/cast/tests/cli/main.rs +++ b/crates/cast/tests/cli/main.rs @@ -627,6 +627,42 @@ casttest!(rlp, |_prj, cmd| { "#]]); }); +// test that `cast impl` works correctly for both the implementation slot and the beacon slot +casttest!(impl_slot, |_prj, cmd| { + let eth_rpc_url = next_http_rpc_endpoint(); + + // Call `cast impl` for the implementation slot (AAVE Proxy) + cmd.args([ + "impl", + "0x4965f6FA20fE9728deCf5165016fc338a5a85aBF", + "--rpc-url", + eth_rpc_url.as_str(), + ]) + .assert_success() + .stdout_eq(str![[r#" +0xb61306c8eb34a2104d9eb8d84f1bb1001067fa4b + +"#]]); +}); + +casttest!(impl_slot_beacon, |_prj, cmd| { + let eth_rpc_url = next_http_rpc_endpoint(); + + // Call `cast impl` for the beacon slot + cmd.args([ + "impl", + "0xc63d9f0040d35f328274312fc8771a986fc4ba86", + "--beacon", + "--rpc-url", + eth_rpc_url.as_str(), + ]) + .assert_success() + .stdout_eq(str![[r#" +0xa748ae65ba11606492a9c57effa0d4b7be551ec2 + +"#]]); +}); + // test for cast_rpc without arguments casttest!(rpc_no_args, |_prj, cmd| { let eth_rpc_url = next_http_rpc_endpoint();