To evaluate the value of msg.sender when using solidity/assembly's delegatecall:
We start out with 3 contracts:
pragma solidity ^0.5.7;
contract ScratchPadActions {
event SenderActions(address contractAddress, address sender, address second);
function relay(ScratchPad2 second) public returns (address) {
emit SenderActions(address(this), msg.sender, address(second));
return second.relay();
}
}
contract ScratchPad {
event Pad1(address contractAddress, address sender, address actions, address target);
function execute(address actions, address _target) public returns (bytes memory response) {
emit Pad1(address(this), msg.sender, actions, _target);
// call contract in current context
bytes memory _data = abi.encodeWithSignature("relay(address)", _target);
assembly {
let succeeded := delegatecall(sub(gas, 5000), actions, add(_data, 0x20), mload(_data), 0, 0)
let size := returndatasize
response := mload(0x40)
mstore(0x40, add(response, and(add(add(size, 0x20), 0x1f), not(0x1f))))
mstore(response, size)
returndatacopy(add(response, 0x20), 0, size)
switch iszero(succeeded)
case 1 {
// throw if delegatecall failed
revert(add(response, 0x20), size)
}
}
}
}
contract ScratchPad2 {
event Sender(address contractAddress, address sender);
function relay() public returns (address) {
emit Sender(address(this), msg.sender);
return msg.sender;
}
}When we deploy those and call execute with the address of actions as the first parameter and the address of ScratchPad2 as the second parameter. For this example assume the following addresses:
User: 0xca35b7d915458ef540ade6068dfe2f44e8fa733c
ScratchPad: 0xbde95422681e4c3984635af2f2f35f8c44a4ddc9
ScratchPad2: 0x35ef07393b57464e93deb59175ff72e6499450cf
ScratchPadActions: 0xec5bee2dbb67da8757091ad3d9526ba3ed2e2137Then if we call ScratchPad(0xbde95422681e4c3984635af2f2f35f8c44a4ddc9).execute(0xec5bee2dbb67da8757091ad3d9526ba3ed2e2137, 0x35ef07393b57464e93deb59175ff72e6499450cf), we see 3 events:
[
{
"from": "0xbde95422681e4c3984635af2f2f35f8c44a4ddc9",
"topic": "0x6f50d9e36e20831209b5ad6950d7f26e557bcdef6b0174425405f374f3732863",
"event": "Pad1",
"args": {
"0": "0xBde95422681e4C3984635Af2f2F35f8c44A4ddc9",
"1": "0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c",
"2": "0xEc5bEe2dbB67dA8757091Ad3D9526Ba3ed2E2137",
"3": "0x35eF07393b57464e93dEB59175fF72E6499450cF",
"contractAddress": "0xBde95422681e4C3984635Af2f2F35f8c44A4ddc9",
"sender": "0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c",
"actions": "0xEc5bEe2dbB67dA8757091Ad3D9526Ba3ed2E2137",
"target": "0x35eF07393b57464e93dEB59175fF72E6499450cF",
"length": 4
}
},
{
"from": "0xbde95422681e4c3984635af2f2f35f8c44a4ddc9",
"topic": "0x57015201bfe939f17dde60b50c1d0e500de2ece9f2e1ede8b064632ea6936724",
"event": "SenderActions",
"args": {
"0": "0xBde95422681e4C3984635Af2f2F35f8c44A4ddc9",
"1": "0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c",
"2": "0x35eF07393b57464e93dEB59175fF72E6499450cF",
"contractAddress": "0xBde95422681e4C3984635Af2f2F35f8c44A4ddc9",
"sender": "0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c",
"second": "0x35eF07393b57464e93dEB59175fF72E6499450cF",
"length": 3
}
},
{
"from": "0x35ef07393b57464e93deb59175ff72e6499450cf",
"topic": "0x2960695afa2609b7591ca0b79d5586f853e610ce0515852f7a5ced78fd32ade9",
"event": "Sender",
"args": {
"0": "0x35eF07393b57464e93dEB59175fF72E6499450cF",
"1": "0xBde95422681e4C3984635Af2f2F35f8c44A4ddc9",
"contractAddress": "0x35eF07393b57464e93dEB59175fF72E6499450cF",
"sender": "0xBde95422681e4C3984635Af2f2F35f8c44A4ddc9",
"length": 2
}
}
]Our return value is:
0x000000000000000000000000bde95422681e4c3984635af2f2f35f8c44a4ddc9
So what is happening here:
UsercallsScratchPadScratchPadfiresPad1event- Using
delegatecallScratchPadexecutesScratchPadActions.relayas though therelaycode andSenderActionsevent were written directly in its code. - This then invokes the
ScratchPad2.relayfunction withScratchPadas themsg.sender ScratchPad2.relayreturns itsmsg.senderwhich gets returned out ofScratchPad.relayasbytes