Skip to content
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

bug(cheatcodes): Facing issues with startPrank(address msgSender, bool delegateCall) setting msg.sender for ALL subsequent delegatecalls at ANY depth #9990

Open
2 tasks done
blmalone opened this issue Mar 1, 2025 · 2 comments
Labels
A-cheatcodes Area: cheatcodes T-bug Type: bug T-to-investigate Type: to investigate

Comments

@blmalone
Copy link

blmalone commented Mar 1, 2025

Component

Forge

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

  • Foundry
  • Foundryup

What version of Foundry are you on?

forge Version: 1.0.0-nightly

What version of Foundryup are you on?

No response

What command(s) is the bug in?

The startPrank cheatcode vm.startPrank(address msgSender, bool delegateCall)

Operating System

macOS (Apple Silicon)

Describe the bug

Description:

Hi, I recently encountered some unexpected behavior with startPrank(address msgSender, bool delegateCall) when delegateCall is set to true.

The documentation states:

startPrank(address msgSender, bool delegateCall): if delegateCall is true, it sets msg.sender for all subsequent delegate calls until stopPrank is called.

While this behavior matches my observations, I believe it shouldn't apply to all delegate calls at any depth. Shouldn't it instead apply only to top-level delegate calls? AFAICT, this is how prank currently works for regular calls.

I searched for existing issues but didn’t find one that directly addresses this. The closest I found is: #5521.

I'm keen to hear thoughts on whether this is intentional or should be adjusted. Thanks!

Steps To Reproduce:
Below is the code showing the delegateCall bug and then another test case proving prank only changes msg.sender for top level calls.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {console2} from "forge-std/console2.sol";
import {Test} from "forge-std/Test.sol";

contract MyTest is Test {
    // Shows startPrank for delegatecall sets the msg.sender call ALL delegatecalls at ANY depth.
    function testDelegatePrank() external {
        A a = new A();
        vm.etch(address(0x11111), hex"11");
        vm.startPrank(address(0x11111), true);
        (bool success,) = address(a).delegatecall(abi.encodeWithSelector(A.foo.selector));
        require(success, "MyTest: error calling foo on A");
        vm.stopPrank();
    }

    // Shows startPrank for calls only works for top level calls.
    function testCallPrank() external {
        D d = new D();
        vm.etch(address(0x11111), hex"11");
        vm.startPrank(address(0x11111));
        (bool success,) = address(d).call(abi.encodeWithSelector(D.bar.selector));
        require(success, "MyTest: error calling bar on D");
        (bool success2,) = address(d).call(abi.encodeWithSelector(D.bar.selector));
        require(success2, "MyTest: error calling bar on D (2)");
        vm.stopPrank();
    }
}

// Contracts for DELEGATECALL test case: testDelegatePrank
contract A {
    function foo() external {
        console2.log("A");
        console2.log("address(this)", address(this));
        console2.log("msg.sender", msg.sender);
        B b = new B();
        (bool success,) = address(b).call(abi.encodeWithSelector(B.bar.selector));
        require(success, "A: error calling B.bar");
    }
}

contract B {
    function bar() external {
        C c = new C();
        console2.log("B");
        console2.log("address(this)", address(this));
        console2.log("msg.sender", msg.sender);
        (bool success,) = address(c).delegatecall(abi.encodeWithSelector(C.bar.selector));
        require(success, "B: error calling C.bar");
    }
}

contract C {
    function bar() external view {
        console2.log("C");
        console2.log("address(this)", address(this));
        console2.log("msg.sender", msg.sender);
    }
}

// Contracts for CALL test case: testCallPrank
contract D {
    E e = new E();

    function bar() external {
        console2.log("D");
        console2.log("address(this)", address(this));
        console2.log("msg.sender", msg.sender);
        (bool success,) = address(e).call(abi.encodeWithSelector(E.bar.selector));
        require(success, "D: error calling E.bar");
    }
}

contract E {
    F f = new F();

    function bar() external {
        console2.log("E");
        console2.log("address(this)", address(this));
        console2.log("msg.sender", msg.sender);
        (bool success,) = address(f).call(abi.encodeWithSelector(F.bar.selector));
        require(success, "E: error calling F.bar");
    }
}

contract F {
    function bar() external view {
        console2.log("F");
        console2.log("address(this)", address(this));
        console2.log("msg.sender", msg.sender);
    }
}

Logs testDelegatePrank:
Run the command: forge test --match-test "testDelegatePrank" -vvv

  A
  address(this) 0x0000000000000000000000000000000000011111
  msg.sender 0x0000000000000000000000000000000000011111
  B
  address(this) 0x769A6A5f81bD725e4302751162A7cb30482A222d
  msg.sender 0x0000000000000000000000000000000000011111
  C
  address(this) 0x0000000000000000000000000000000000011111
  msg.sender 0x0000000000000000000000000000000000011111

Logs testCallPrank:
Run the command: forge test --match-test "testCallPrank" -vvv

  D
  address(this) 0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f
  msg.sender 0x0000000000000000000000000000000000011111
  E
  address(this) 0x104fBc016F4bb334D775a19E8A6510109AC63E00
  msg.sender 0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f
  F
  address(this) 0x41C3c259514f88211c4CA2fd805A93F8F9A57504
  msg.sender 0x104fBc016F4bb334D775a19E8A6510109AC63E00
  D
  address(this) 0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f
  msg.sender 0x0000000000000000000000000000000000011111
  E
  address(this) 0x104fBc016F4bb334D775a19E8A6510109AC63E00
  msg.sender 0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f
  F
  address(this) 0x41C3c259514f88211c4CA2fd805A93F8F9A57504
  msg.sender 0x104fBc016F4bb334D775a19E8A6510109AC63E00
@blmalone blmalone added T-bug Type: bug T-needs-triage Type: this issue needs to be labelled labels Mar 1, 2025
@github-project-automation github-project-automation bot moved this to Todo in Foundry Mar 1, 2025
@blmalone blmalone changed the title bug(cheatcodes): Facing issues with startPrank(address msgSender, bool delegateCall) setting msg.sender for ALL subsequent delegatecalls at any depth bug(cheatcodes): Facing issues with startPrank(address msgSender, bool delegateCall) setting msg.sender for ALL subsequent delegatecalls at ANY depth Mar 1, 2025
@zerosnacks zerosnacks added T-to-investigate Type: to investigate A-cheatcodes Area: cheatcodes and removed T-needs-triage Type: this issue needs to be labelled labels Mar 3, 2025
@zerosnacks
Copy link
Member

cc @grandizzy I think this may relate / be resolved by the stack system we have planned for the 1.1 milestone?

#5521 (comment)

@grandizzy
Copy link
Collaborator

cc @grandizzy I think this may relate / be resolved by the stack system we have planned for the 1.1 milestone?

Yep, indeed, is the same

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-cheatcodes Area: cheatcodes T-bug Type: bug T-to-investigate Type: to investigate
Projects
Status: Todo
Development

No branches or pull requests

3 participants