Skip to content

Instantly share code, notes, and snippets.

@iamchrissmith
Created April 1, 2019 20:08
Show Gist options
  • Save iamchrissmith/cad2a385952ccb1d25c215766e520dee to your computer and use it in GitHub Desktop.
Save iamchrissmith/cad2a385952ccb1d25c215766e520dee to your computer and use it in GitHub Desktop.
Understanding msg.sender with delegatecall

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:

  1. User calls ScratchPad
  2. ScratchPad fires Pad1 event
  3. Using delegatecall ScratchPad executes ScratchPadActions.relay as though the relay code and SenderActions event were written directly in its code.
  4. This then invokes the ScratchPad2.relay function with ScratchPad as the msg.sender
  5. ScratchPad2.relay returns its msg.sender which gets returned out of ScratchPad.relay as bytes
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment