[ERC-20] Question on _msgSender() in _transferFrom()

Question on _msgSender() in _transferFrom()

I am trying to understand the behaviour of _msgSender() in transferFrom(), in the ERC-20 contract. For reference:

function transferFrom(address from,address to,uint256 amount) public virtual override returns (bool) {
    address spender = _msgSender();
    _spendAllowance(from, spender, amount);
    _transfer(from, to, amount);
    return true;
}

From reading online I gathered that transferFrom() is called on by the person with the allowance to either withdraw it to himself or some other destination of choice. Meaning _msgSender() evaluates to the address of the caller of transferFrom(), so that spender = caller address.

I don't understand how this is possible.
I had expected that _msgSender() would return the smart contract address of transferFrom(), not the initial caller of transferFrom().

Let me give a concrete example to share my understanding:

  1. transferFrom() is on Vendor.sol
  2. userA calls transferFrom()
  3. consequently, transferFrom() calls on _msgSender()
  4. _msgSender() evaluates this as an external call, and therefore returns address of Vendor.sol
  5. spender = address(Vendor.sol)

As a result spender will always evaluate to the smart contract address that holds transferFrom(), and not the initial caller of transferFrom() as mentioned at the beginning.

Not sure where I've gone wrong with my understanding, clarity would be appreciated.

I tested the following code in Remix, hoping to invalidate my line of thinking. But failed.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract Storage{

    function inspectsender() public view returns(address) {
        return msg.sender;
    }
}


contract Vendor is Storage {
  
  Storage public Storagecontract;    //of type

  constructor(){
    Storagecontract = new Storage();   // of instance
  }

  function vendorSender() public view returns(address){
    return msg.sender;
  }

  function vendorStoragesender() public view returns(address){
    return Storagecontract.inspectsender();

  }
}

I call the following as (0x5B38..)
vendorSender -> returns my wallet address, (0x5B38..)
vendorStoragersender() -> returns Vendor's contract address

This appears to validates the idea that _msgSender() would return the address of the entity that called it. Given that there is evidence for both ideas I'm pretty much pulling my hair out now :melting_face:

Hi, welcome to the community! :wave:

Sorry, I am not sure what is your problem, and I think what you said is right.

Edited original post, hope its cleared now!

You mentioned you were able to resolve your issue/confusion. Feel free to put a follow-up here. It could be helpful for others facing a similar challenge.

Actually, _msgSender() means msg.sender in the OpenZeppelin contract, and this is one of the global variables, it means the actual caller.

This is just what you test at above.

1 Like