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

Add _msgSender() trick #133

Open
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

danfinlay
Copy link

Fixes #132 (If you'd want it!)

A more full description of the motivation is on that issue. Here is a quick pass achieving that proposal.

I did not change msg.sender instances in constructors, since the benefit would never apply in those scenarios.

Enables MetaTransaction facets and Delegatable.

Copy link
Member

@ItsNickBarry ItsNickBarry left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems useful. Same concept as GSN in the OZ contracts, correct? I didn't re-implement that feature because I've never used it myself.

Before merge, needs to be applied to a few more files: SafeOwnableInternal, ERC20ExtendedInternal, ERC20ImplicitApprovalInternal, ERC4626BaseInternal

Comment on lines 58 to 63
assembly {
sender := and(
mload(add(array, index)),
0xffffffffffffffffffffffffffffffffffffffff
)
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you describe these steps in more detail?

* @notice Overrides the msgSender to enable delegation message signing.
* @returns address - The account whose authority is being acted on.
*/
function _msgSender() internal view virtual returns (address sender) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be better to define this in a library in utils/, to improve re-usability and avoid code duplication.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes sense, I was thinking similarly.

@danfinlay
Copy link
Author

Hi, thanks for the review!

Same concept as GSN in the OZ contracts, correct?

That's right. They should be completely compatible. I'll verify as much and get back here. And there are a few implementations going around that should all be compatible, so we can probably choose the most readable one. I'll gather the options.

Can you describe these steps in more detail?

I stole this code from someone who understood it better, but it is almost identical to GSN's version (formatted differently). Their comments are this:

    function _getRelayedCallSender() private pure returns (address payable result) {
        // We need to read 20 bytes (an address) located at array index msg.data.length - 20. In memory, the array
        // is prefixed with a 32-byte length value, so we first add 32 to get the memory read index. However, doing
        // so would leave the address in the upper 20 bytes of the 32-byte word, which is inconvenient and would
        // require bit shifting. We therefore subtract 12 from the read index so the address lands on the lower 20
        // bytes. This can always be done due to the 32-byte prefix.

        // The final memory read index is msg.data.length - 20 + 32 - 12 = msg.data.length. Using inline assembly is the
        // easiest/most-efficient way to perform this operation.

        // These fields are not accessible from assembly
        bytes memory array = msg.data;
        uint256 index = msg.data.length;

        // solhint-disable-next-line no-inline-assembly
        assembly {
            // Load the 32 bytes word from memory with the address on the lower 20 bytes, and mask those.
            result := and(mload(add(array, index)), 0xffffffffffffffffffffffffffffffffffffffff)
        }
        return result;
    }

I'll add to the remaining files, while moving the implementation to a util soon, thank you! Feel free to close until I make those changes if you'd like to keep the PR queue neat.

@danfinlay
Copy link
Author

Ok, I've made a few changes:

  • Moved the _msgSender() implementations into a single util file.
  • Copied the better-documented version of the _msgSender() that I linked to above.

Before merge, needs to be applied to a few more files: SafeOwnableInternal, ERC20ExtendedInternal, ERC20ImplicitApprovalInternal, ERC4626BaseInternal

  • SafeOwnableInternal imports OwnableInternal, which now includes the change, so it is inherited.
  • ERC20ExtendedInternal imports ERC20BaseInternal, which now includes the change, so it is inherited.
  • ERC20ImplicitApprovalInternal imports ERC20BaseInternal, which now includes the change, so it is inherited.
  • ERC4626BaseInternal imports ERC20BaseInternal, which now includes the change, so it is inherited.

If I were to block merge myself, I might suggest I add at least one test first to demonstrate reasonable usage of this method.

@zlace0x
Copy link

zlace0x commented Aug 27, 2022

This is similar to OZ's Context.sol, but they don't implement _getRelayedCallSender by default since not all contracts would require meta tx. One can override the default with their desired metatx standard (e.g. ERC2771Context or ContextMixin)

I have not tested it, but this could affect existing contracts using multicall function since it would change the sender.

@danfinlay
Copy link
Author

Oh that's interesting, does the multicall functinality rely on similarly appending to tx.data?

@zlace0x
Copy link

zlace0x commented Aug 29, 2022

I'm looking for cases where msg.sender == address(this) might return true and return an unexpected sender.
If a contract calls itself with address(this).call without appending sender, _msgSender() will return an invalid sender address.

In the case of SS's Multicall, there won't be an issue since it uses delegatecall (msg.sender doesn't change to the contract address).

But other versions of Multicall use call for read-only queries; this can cause _msgSender() to report an unexpected value.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Apply the msgSender trick
3 participants