Repository: foundry-rs/foundry (language: rust)

Issue:
bug(`cast`): 4byte-calldata should correctly handle just function selector without additional calldata
### Component

Cast

### Have you ensured that all of these are up to date?

- [x] Foundry
- [x] Foundryup

### What version of Foundry are you on?

forge Version: 1.0.0-stable

### What version of Foundryup are you on?

N/A

### What command(s) is the bug in?

cast 4byte-decode

### Operating System

macOS (Apple Silicon)

### Describe the bug

```
cast 4byte-decode 0x79ba5097
```

This should return [acceptOwnership](https://openchain.xyz/signatures?query=0x79ba5097) but instead errors.


Discussion / hints:
Thanks for flagging! This seems to be related to incorrect handling of selectors without arguments

https://github.com/foundry-rs/foundry/blob/f3130a52937039a7e82b4aef07143139c322d09f/crates/common/src/selectors.rs#L246
Actually should use 4byte, 4byte-decode is for decoding calldata https://book.getfoundry.sh/reference/cast/cast-4byte
https://book.getfoundry.sh/reference/cast/cast-4byte-decode
Ah, good catch - yes

```
cast 4byte 0x79ba5097
```

correctly yields

```
acceptOwnership()
```

imo should rename `4byte-code` to `4byte-calldata`, in line with `4byte-event`, keeping the `4byte-decode` as alias
yeah, maybe an alias to not be a breaking?
Technically, "just" a function selector _is_ calldata. So I would expect it should still work?

Otherwise, if I have a script to decode calldata, I need to check the length of the calldata and use `4byte` or `4byte-decode`. 

This could be a feature though! Up to whatever you all think. I didn't know that this result was expected.
Agreed, marked it as no longer closing this ticket, it is reasonable to expect that a function selector without additional calldata attached would still be handled correctly by `4byte-decode` even if it is not its purpose
@grandizzy can i take this one ?
> [@grandizzy](https://github.com/grandizzy) can i take this one ?

sure, assigned to you. thank you!

Fix patch:
diff --git a/crates/cast/bin/main.rs b/crates/cast/bin/main.rs
index c5be05df987b..8342f7b2d6bd 100644
--- a/crates/cast/bin/main.rs
+++ b/crates/cast/bin/main.rs
@@ -518,8 +518,21 @@ async fn main_args(args: CastArgs) -> Result<()> {
                 sh_println!("{sig}")?
             }
         }
+
         CastSubcommand::FourByteCalldata { calldata } => {
             let calldata = stdin::unwrap_line(calldata)?;
+
+            if calldata.len() == 10 {
+                let sigs = decode_function_selector(&calldata).await?;
+                if sigs.is_empty() {
+                    eyre::bail!("No matching function signatures found for calldata `{calldata}`");
+                }
+                for sig in sigs {
+                    sh_println!("{sig}")?
+                }
+                return Ok(());
+            }
+
             let sigs = decode_calldata(&calldata).await?;
             sigs.iter().enumerate().for_each(|(i, sig)| {
                 let _ = sh_println!("{}) \"{sig}\"", i + 1);
@@ -537,6 +550,7 @@ async fn main_args(args: CastArgs) -> Result<()> {
             let tokens = SimpleCast::calldata_decode(sig, &calldata, true)?;
             print_tokens(&tokens);
         }
+
         CastSubcommand::FourByteEvent { topic } => {
             let topic = stdin::unwrap_line(topic)?;
             let sigs = decode_event_topic(&topic).await?;


Test patch:
diff --git a/crates/cast/tests/cli/selectors.rs b/crates/cast/tests/cli/selectors.rs
index 32a0b7a2748c..d72f0e85f3a1 100644
--- a/crates/cast/tests/cli/selectors.rs
+++ b/crates/cast/tests/cli/selectors.rs
@@ -34,6 +34,13 @@ casttest!(fourbyte_calldata, |_prj, cmd| {
 "#]]);
 });
 
+casttest!(fourbyte_calldata_only_selector, |_prj, cmd| {
+    cmd.args(["4byte-calldata", "0xa9059cbb"]).assert_success().stdout_eq(str![[r#"
+transfer(address,uint256)
+
+"#]]);
+});
+
 casttest!(fourbyte_calldata_alias, |_prj, cmd| {
     cmd.args(["4byte-decode", "0xa9059cbb0000000000000000000000000a2ac0c368dc8ec680a0c98c907656bd970675950000000000000000000000000000000000000000000000000000000767954a79"]).assert_success().stdout_eq(str![[r#"
 1) "transfer(address,uint256)"