1

I found this in the official solidity docs.

The values of all members of msg, including msg.sender and msg.value can change for every external function call. This includes calls to library functions.

The "can change for every external function call" is too vague for me.

Does anyone know when it does change for an external function call?

My concrete example:

pragma solidity ^0.5.10;

contract A {
    function foo() external view returns(address payable) {return msg.sender;}
}

contract B is A {
    address payable public msgSender;

    function bar() external {
        // 3 ways of doing the same thing
        msgSender = A.foo();  // does this work and who will be msgSender?
        msgSender = this.foo();  // does this work and who will be msgSender? 
        msgSender = super.foo();  // does this work and who will be msgSender? 
    }
}

Please take all the code as is => in the 3 notations A.foo() this.foo() super.foo() I am NOT talking to another deployed contract instance. With all 3 ways I want to showcase 3 (different but same) ways of calling external functions (inherited ones in this case) on a contract from inside (internally) that same contract. The 3 ways basically just vary in their scope or explicitness I believe.

Back to my main question: Who is the msg.sender emitted in the EmitMsgSender event emitted by the call to A/this/super.foo() in my example? Is it contract B's address payable or is it the address that sent the tx to B.bar()?

By the way, it would make my life easier, if it were the original caller of the B.bar() function, and not B's address.

Thanks for your help!

3
  • All of your 3 questions in the code - you can simply try and find out. I don't think that either one of them is even going to compile since function foo is external, and you cannot call external functions from the same contract (and since B is A, we're talking about the same contract here). So you probably wanna change it to public or internal and then simply give it a try and see what you get in the emitted event. BTW, you might find it easier to test the whole thing it you declare the function as view returns (address) and simply return msg.sender. Commented Oct 9, 2019 at 17:22
  • Thanks for your input. Your right, I'll just test it. But gonna leave the question here and supply my test results as an aswer. btw you can call external functions inside the same contract : " An external function f cannot be called internally (i.e. f() does not work, but this.f() works)." (see the docs: solidity.readthedocs.io/en/latest/…) Commented Oct 9, 2019 at 18:36
  • OK, well done for testing it and providing your own observation / analysis. Voted it upwards. Commented Oct 9, 2019 at 19:02

1 Answer 1

2

Here is my answer, to my own questions after testing in Remix:

  1. A.foo() does not work: TypeError Member "foo" not found or not visible after argument-dependent lookup in type(contract A)
  2. same for super.foo() except it says ... lookup in contract super B Both 1) and 2) could be expected from the docs that only list this.f() as a way to call external functions from inside a contract.

And now to the interesting part: Edits thanks to @smarx this.foo()works and it returns the address of contract B as msg.sender.

=> When I call an inherited external function internally (from inside the same contract), the context DID change the msg.sender to the derived contract's address, who forwarded the message to its base contract's external function via the special this.function() syntax.

The msg.sender is NOT preserved (as could be expected from the solidity docs).

However, the docs are still ambiguous:

msg.value can change for every external function call.

This seems to imply that sometimes it does not change. So: When does it change (in my example it did) and most of all when DOES IT NOT?

EDIT: Answer from @smarx (paraphrased)

the context of msg.sender DOES NOT change in calls to an external contract function when you use delegatecall

4
  • 1
    This doesn't match my testing. When I call this.foo(), foo() sees the contract's address as msg.sender.
    – user19510
    Commented Oct 9, 2019 at 23:56
  • (BTW, this has nothing to do with inheritance. If you just declare foo() in the same contract and use this.foo() to invoke it, msg.sender will still be the contract's address.)
    – user19510
    Commented Oct 9, 2019 at 23:58
  • Hi @smarx - I edited the code in my initial question slightly, to match exactly what I tested it in Remix IDE using the JavaScript VM. I realized I made a mistake in the Remix IDE initially. I accidentally only compiled and deployed ContractA which obviously returned my account from foo() . thanks for pointing this out, I will edit my answer. One question remains: the solidity docs are quite ambiguous ("can change for every external function call") - do you know the examples where it does NOT change? thanks! Commented Oct 10, 2019 at 7:42
  • 1
    It doesn't change when you use a delegatecall.
    – user19510
    Commented Oct 10, 2019 at 8:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.