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: 0xec5bee2dbb67da8757091ad3d9526ba3ed2e2137
Then 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:
User
callsScratchPad
ScratchPad
firesPad1
event- Using
delegatecall
ScratchPad
executesScratchPadActions.relay
as though therelay
code andSenderActions
event were written directly in its code. - This then invokes the
ScratchPad2.relay
function withScratchPad
as themsg.sender
ScratchPad2.relay
returns itsmsg.sender
which gets returned out ofScratchPad.relay
asbytes