ERC-721 Standard – Explaining the Redundant `_from` Parameter in `transferFrom` Function

erc-721function

The Non-Fungible Token Standard (described in EIP-721) specifies, among other things, the signature of the transferFrom() function: function transferFrom(address _from, address _to, uint256 _tokenId) external payable;

Besides, it says that the function "throws if _from is not the current owner" of _tokenId.

Now, why is the _from argument required, at all, if it is easily deducible from _tokenId (by passing it to the ownerOf() function)?
Passing _from seems redundant, and the the internal comparison of the argument to the "actual" owner of _tokenId – wasteful.

Why not just check whether the transferFrom() caller has been approved by the ownerOf(_tokenId) to transfer either the _tokenId itself (via approve()) – or all of the owner's NFTs from the collection (via setApprovalForAll())?

If the caller isn't approved to transfer the NFT, it doesn't matter whether msg.sender == _ownerOf(_tokenId). And if they are approved, then, what is the added value of forcing the caller to also pass the _from argument (which can be obtained by simply calling ownerOf(_tokenId))?

Best Answer

Great observation on the _from parameter! I never noticed this before.

Here are the two key reasons for its inclusion in the transferFrom() function in ERC-721:

As you know, ERC-721 is strongly inspired by ERC-20, _from parameter is included in both standards for the following reasons:

  • Simplifying things for developers by sharing a similar design across both standards, making them easier to use and understand.
  • Ensuring consistency across token standards: With the _from parameter in ERC-721, developers can more easily work with various token types, as they share the same signature (0x23b872dd): bytes4(keccak256('transferFrom(address,address,uint256)')) == 0x23b872dd

In summary, the _from parameter in ERC-721 maintains consistency with other token standards like ERC-20 and simplifies working with diverse tokens for developers.

It was mentioned previously that including the _from parameter could save gas by avoiding using ownerOf(uint256 tokenId). However, this function is used anyway on transferFrom in OpenZeppelin implementation:

function _isApprovedOrOwner(address spender, uint256 tokenId) internal view virtual returns (bool) {
    address owner = ERC721.ownerOf(tokenId);
    return (spender == owner || isApprovedForAll(owner, spender) || getApproved(tokenId) == spender);
}

Considering this, I don't see any benefits in terms of gas savings by including _from parameter in transferFrom().

EDIT:

Here is an Answer from the lead author of ERC-721, which provides additional insight into the reasoning behind this design choice:

"We did not include a transfer function because it may be ambiguous whether you mean to transfer something that you are the owner of versus something you are authorized to get.

The current owner must always be specified, because of security reasons (specifically, front-running). This is why transferFrom is the closest to the wire you can get."

Here's a simplified summary of the additional information provided:

  • The transfer function was not included to avoid ambiguity between transferring owned tokens and transferring authorized tokens.
  • Specifying the current owner (_from parameter) is necessary for security reasons, particularly to prevent front-running attacks.

By requiring the _from parameter, the function ensures no ambiguity between transferring an NFT you own and one you are authorized to transfer, making the process more secure.