Openzeppelin-contracts: Support for native-meta transaction in contract3.X standard

Created on 22 May 2020  路  4Comments  路  Source: OpenZeppelin/openzeppelin-contracts

馃 Motivation
Contracts3.X doesn't support Native Meta-Transaction support. It's not compatible with any of the meta-transaction out there in the market.

馃摑 Details
As contract 3.X uses v0.6.6 standard of solidity, And function _msgSender in Context contract is virtual so it can only be overridden in parent contract which inherits the ERC20 standard. That means, the existing solutions for meta transaction are not compatible anymore. To give some context, Meta-Transaction relayers uses _msgSender, For instance Gas station network uses this implementation.

    function _msgSender() internal view returns (address payable) {
        if (msg.sender == address(this)) {
            bytes20 userAddress;
            uint256 dataLength = msg.data.length;
            assembly {
                calldatacopy(0x0, sub(dataLength, 40), sub(dataLength, 20))
                userAddress := mload(0x0)
            }
            return address(uint160(userAddress));
        } else {
            return msg.sender;
        }
    }

I propose that we should include this change in Context contract itself or make changes so _msgSender() can be overridden by the other contracts too.


Most helpful comment

Thank you for reaching out @sanchaymittal.

I wasn't able to understand the problem that you point out.

Context._msgSender was specifically built for the GSN. The inheritance changes in Solidity 0.6 made it a bit more cumbersome to use, but everything should work. GSNRecipient defines the override for _msgSender.

Unfortunately in Solidity 0.6 inheriting from GSNRecipient will likely result in a contract that doesn't compile because the compiler doesn't know if it should use Context._msgSender or GSNRecipient._msgSender, but this is fixed by adding the following lines to your contract:

function _msgSender() internal view override(Context, GSNRecipient) returns (address payable) {
    return super._msgSender();
}

function _msgData() internal view override(Context, GSNRecipient) returns (bytes memory) {
    return super._msgData();
}

By using super we specify that we want to use the "most specific" implementation of _msgData according to the Solidity linearization, and that will be GSNRecipient's.

We definitely should have specifically this bit of code in the docs, but I couldn't find it. cc @nventuro

All 4 comments

Thank you for reaching out @sanchaymittal.

I wasn't able to understand the problem that you point out.

Context._msgSender was specifically built for the GSN. The inheritance changes in Solidity 0.6 made it a bit more cumbersome to use, but everything should work. GSNRecipient defines the override for _msgSender.

Unfortunately in Solidity 0.6 inheriting from GSNRecipient will likely result in a contract that doesn't compile because the compiler doesn't know if it should use Context._msgSender or GSNRecipient._msgSender, but this is fixed by adding the following lines to your contract:

function _msgSender() internal view override(Context, GSNRecipient) returns (address payable) {
    return super._msgSender();
}

function _msgData() internal view override(Context, GSNRecipient) returns (bytes memory) {
    return super._msgData();
}

By using super we specify that we want to use the "most specific" implementation of _msgData according to the Solidity linearization, and that will be GSNRecipient's.

We definitely should have specifically this bit of code in the docs, but I couldn't find it. cc @nventuro

We have notes on the required overrides for GSN in the release notes for v3: https://github.com/OpenZeppelin/openzeppelin-contracts/releases/tag/v3.0.0

Thanks a lot guys!!! @frangio @abcoathup.
Got it resolved...

pragma solidity "0.6.6";

import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@opengsn/gsn/contracts/BaseRelayRecipient.sol";

contract TestToken is ERC20, BaseRelayRecipient {
  constructor() 
    ERC20("Test Token", "TST")
    public 
  {
    uint256 value = 10**10 * (10**18);
    _mint(_msgSender(), value);
  }

function _msgSender() internal view override(Context, BaseRelayRecipient) returns (address payable) {
    return BaseRelayRecipient._msgSender();
}
}

Awesome!

Was this page helpful?
0 / 5 - 0 ratings