_msgSender() function from ERC2771context returning the signer address

I am having difficulty understanding what address the _msgSender() function returns from the erc2771 context which is in the last 20 bytes of the data is it the user who wants to do the gasless transaction or the sender who is paying for the gas fee.

function _msgSender() internal view returns (address payable signer) {
        signer = msg.sender;
        if (msg.data.length>=20 && isTrustedForwarder(signer)) {
            assembly {
                signer := shr(96,calldataload(sub(calldatasize(),20)))
            }
        }    
    }

also is it better to create a contract for the relay part or use web3 or ethers to forward the transaction to the contract containing erc2771 context inheritence.

If the length of the transaction data is 20 bytes or more, AND the sender of the transaction is trusted, then the function returns the user who wants to do the gasless transaction.

Otherwise, the function returns the sender who is paying for the gas fee.

1 Like

100%, the trusted forwarder has super powers on the contract, it should be a constrained Forwarder contract, and never an EOA.

For a good example see the ERC2771Forwarder contract in the upcoming 5.0 release.

1 Like

I tried following this method of signing a transaction by the user and then sending it to the Contract containing erc2771Context

for some reason, it didn't work for me. Anyone who can help with that thanks!

You need to share more detail. What did you do exactly, what did you sign?

const fs = require("fs-extra");
const Web3 = require("web3");
require("dotenv").config();

const contractJson = fs.readFileSync("./contract_abi.abi", "utf8");
const contract_abi = JSON.parse(contractJson);

const web3 = new Web3(
 process.env.ALCHEMY_URL
);

const USER_PRIVATE_KEY = process.env.USER_PRIVATE_KEY;
const USER_ADDRESS = process.env.USER_ADDRESS;
const RELAYER_ADDRESS = process.env.RELAYER_ADDRESS;
const CONTRACT_ADDRESS = process.env.CONTRACT_ADDRESS;

const contract = new web3.eth.Contract(contract_abi, CONTRACT_ADDRESS);

async function forwardTransaction(address) {
  var txData = contract.methods.mint(address).encodeABI();
  // var message = web3.utils.utf8ToHex(txData);
  var message = web3.utils.soliditySha3(txData);

  var signature = await web3.eth.personal.sign(
    message,
    USER_ADDRESS,
    USER_PRIVATE_KEY
  );

  var req = {
    Txdata: txData,
    From: USER_ADDRESS,
    To: CONTRACT_ADDRESS,
    Msg: message,
    Signature: signature,
  };

    web3.eth
      .sendTransaction({
        from: RELAYER_ADDRESS,
        to: req.To,
        data: req.Txdata,
      })
      .then((receipt) => {
        console.log(receipt);
      });
  
}

The function i am encoding here is getting signed by the user private key and then the other person is sending the transaction instead of user.

So... what exactly didn't work for you in the code above?

BTW, you may notice that you're not doing anything with any of the fields in req, other than the to field and the Txdata field... and that includes the fields which hold the message and the signature :

Placing these two pieces of information as part of the req object doesn't mean that they are "getting relayed anywhere"... so by the time the contract function is executed, it "has no knowledge" of them.

yeah i just noticed that part thank you for highlighting it!
the error i am getting right now is this

Unsupported method: personal_sign. See available methods at https://docs.alchemy.com/alchemy/documentation/apis
    at Object.ErrorResponse

i tried to use other sign methods but that also didn't worked.

also tried this link but this hex thing before the data part didn't make sense to me if we can just encode the whole method with parameters??

https://medium.com/coinmonks/complete-guide-to-meta-transactions-c46ca51dbd21


this part ^

That's probably the function selector of the contract function which you are trying to execute.

solved the issue first i'm signing the contract method with parameters with the user and then signing the transaction with the relayer or forwarder and sending the transaction and it executes the mint function which includes the ERC2771 context inheritance.

const fs = require("fs-extra");
const Web3 = require("web3");
require("dotenv").config();

const contractJson = fs.readFileSync("./contract_abi.abi", "utf8");
const contract_abi = JSON.parse(contractJson);

const web3 = new Web3(
process.env.MUMBAI_URL
);

const USER_PRIVATE_KEY = process.env.USER_PRIVATE_KEY;
const USER_ADDRESS = process.env.USER_ADDRESS;
const RELAYER_ADDRESS = process.env.RELAYER_ADDRESS;
const RELAYER_PRIVATE_KEY = process.env.RELAYER_PRIVATE_KEY;
const CONTRACT_ADDRESS = process.env.CONTRACT_ADDRESS;

const contract = new web3.eth.Contract(contract_abi, CONTRACT_ADDRESS);

async function forwardTransaction(address) {
  var txData = contract.methods.mint(address).encodeABI();
  // console.log(txData);

  var signatures = await web3.eth.accounts.sign(txData, USER_PRIVATE_KEY);
  console.log(signatures);
  var req = {
    Txdata: txData,
    From: USER_ADDRESS,
    To: CONTRACT_ADDRESS,
    // Msg: message,
    Signature: signatures.signature,
  };

  var transaction = await web3.eth.accounts.signTransaction(
    {
      from: RELAYER_ADDRESS,
      to: req.To,
      data: req.Txdata,
      gas: 300000,
    },
    RELAYER_PRIVATE_KEY
  );

  await web3.eth
    .sendSignedTransaction(transaction.rawTransaction)
    .on("receipt", (receipt) => {
      console.log(receipt);
    });
}


    function mint(address _address) public {
        require(_address == _msgSender(), "wrong address");

        _mint(_address, tokenIdCounter.current());

        tokenIdCounter.increment();
    }

  function _msgSender()
        internal
        view
        virtual
        override(ERC2771Context, Context)
        returns (address sender)
    {
        return ERC2771Context._msgSender();
    }