Skip to content

fix(cast): Always use from field of getTransaction rpc response in cast run #10795

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged

Conversation

ckoopmann
Copy link
Contributor

@ckoopmann ckoopmann commented Jun 18, 2025

Motivation

Currently the code assumes that any impersonated transaction has been signed with a given constant key. (r=1, s=1). If this check is not passed then the from address will be recovered from the tx signature.

This makes cast run incompatible with impersonated transactions on rpc implementations where that assumption does not hold (i.e. when debugging tests in legacy hardhat projects).

Solution

Add a bypass-sender-recovery flag to cast run that makes it such that the from address is always set to the corresponding field on the getTransaction response from the rpc. (and not recovered from the tx signature)

Always use the from address as returned by the rpc in the getTransaction response.

PR Checklist

  • Added Tests
  • Added Documentation
  • Breaking changes

@ckoopmann
Copy link
Contributor Author

ckoopmann commented Jun 18, 2025

Note: To fully e2e test this feature you'd have to:

  • Start a hardhat rpc node (or other framework that doesn't adhere to the standard impersonation signature)
  • Submit a transaction under an impersonated signer
  • Run cast run against that tx

I did this locally but wasn't sure if / how to add this to the CI. Lmk if this is critical (or you have a simpler idea of how to test this) and I will look into it further.

@ckoopmann
Copy link
Contributor Author

Furthermore we might want to add a warning when this flag is not set and the recovered from address differs from the one returned by the getTransaction rpc call as this indicates that something is going wrong. (Originally I had a bunch of "balance too low" or "nonce too high" errors that were quite hard to trace to this root cause).

@@ -261,7 +270,11 @@ impl RunArgs {
let result = {
executor.set_trace_printer(self.trace_printer);

configure_tx_env(&mut env.as_env_mut(), &tx.inner);
configure_tx_env_assume_impersonation(
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that naming is not entirely consistent here. Elsewhere i call it "bypass sender recovery" whereas here i call it "assume impersonation", happy to change it to either one, or any other naming that improves readability / clairity.

Copy link
Member

@mattsse mattsse left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this comes up from time to time.

I think instead of doing this we should just always use the from field received in the tx rpc response?

Comment on lines 96 to 90
pub fn configure_tx_env(env: &mut EnvMut<'_>, tx: &Transaction<AnyTxEnvelope>) {
let impersonated_from = is_impersonated_tx(&tx.inner).then_some(tx.from());
configure_tx_env_inner(env, &tx.clone(), impersonated_from);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

after reviewing this again, I tink we should actually just always use the rpc response from value here?

Copy link
Contributor Author

@ckoopmann ckoopmann Jun 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree.

I think when running cast run at least it is fine to trust the "getTransaction" response coming back from the RPC.
I only added the additional flag to keep the default behaviour backwards compatible assuming that there are some users / use cases that depend on the transaction signature being "verified" by recovering the sender from it. Otherwise why was it done this way in the first place ?

Anyway, I will adjust this PR to remove the additional flag and just always use the rpc response as suggested.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adjusted to always use the rpc "from" field in cast run: 5d0c3e6

…bility with hardhat impersonated transactions
@ckoopmann ckoopmann force-pushed the cast-run-optionaly-bypass-sender-recovery branch from b72ba35 to eba043d Compare June 28, 2025 02:34
if let AnyTxEnvelope::Ethereum(tx) = &tx.inner.inner() {
configure_tx_req_env(env, &tx.clone().into(), impersonated_from).expect("cannot fail");
configure_tx_req_env(env, &tx.clone().into(), Some(from)).expect("cannot fail");
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: I didn't change the configure_tx_req_env function signature or implementation to avoid having to change logic in other places where it is used such as:

I could easily change the argument name from impersonated_from to something like overwrite_from though to indicate that this value is not only used for impersonated tx's anymore.

@ckoopmann
Copy link
Contributor Author

Clippy failures seem unrelated to my changes so will just ignore them for now.

@ckoopmann ckoopmann changed the title feat(cast): Add --bypass-sender-recovery flag to cast run for compatibility with hardhat impersonated transactions fix(cast): Always use from field of getTransaction rpc response in cast run Jun 28, 2025
@ckoopmann ckoopmann requested a review from mattsse June 28, 2025 02:59
mattsse
mattsse previously approved these changes Jun 28, 2025
Copy link
Member

@mattsse mattsse left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm!

@mattsse mattsse enabled auto-merge (squash) June 28, 2025 09:55
@mattsse mattsse merged commit ae7205c into foundry-rs:master Jun 28, 2025
22 checks passed
@github-project-automation github-project-automation bot moved this to Done in Foundry Jun 28, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

2 participants