I can understand that the from
address in the safeTransferFrom
function could be used by an approved operator. But I also wonder why not have another function for actual owners called just safeTransferTo
that doesn't need to provide the from
address?
safeTransferTo(address to, uint tokenId, bytes memory_data);
In this case, the from
is implicit as it's coming from the msg.sender
address. Isn't this much more convenient?
Is there any implications for doing it this way such as security concerns that I don't see people are doing it like this? If I would like to, should I implement such a function in my own contract?
Best Answer
Not really. When you do this through a gui that operates under this assumption, you aren't going to be filling in the
from
by hand--it'll be done on your behalf by javascript crafting the transaction in the dApp/website; you just sign off on the transaction when you are then prompted with it by metamask.On the other hand, deploying extra code to the blockchain is expensive. One function that does it all in this case means less gas costs to deploy and less code to create bugs in and to audit.
On the surface, why not? In practice, though, yes, probably many people would lose a lot of tokens... Or, after taking these recommendations into account, the function would likely never end up being used. :)
Because an ERC20 is not ETH, and sending ERC20 to a contract cannot trigger a
receive()
orfallback()
function, the way we almost always send ERC20 to a contract is byapproving()
the contract, and then letting the contract pull the ERC20 from you, not pushing the ERC20 to it. (ERC777
handles this instead by implementing hooks and operators btw, eliminating the need for theapprove()
step, and its associated cost and problems.)Trying to push ERC20 to a contract is basically the number one way it gets lost--just two months ago, a guy accidentally burned $500k USD (on etherscan) (news) by pushing ERC20 wETH to the wETH contract. Why is that a problem? Because the wETH contract is almost certainly designed, like all other ERC20 manipulating contracts, to
pull
the tokens, not receive them from a push.A quick point to understand the rest:
While you may see
safeTransferFrom()
in both ERC20 and ERC721 related code, the two functions have basically nothing to do with each other and have very different reasons for claiming to be 'safe'. It is an official part of theERC721
spec, but not a part of theERC20
spec!The
ERC721
spec checks to make sure that, if the recipient is a contract, the recipient can return a magic value in order to confirm that it was designed to receive ERC721 NFTs.The ERC20 implementation that I'm familiar with comes from Open Zeppelin's
SafeERC20.sol
, where the "safe" refers to interacting with and handling non-standards-compliant tokens that don't fail properly. Their tldr:You could handle either/both of those concerns, and yet because you pushed it instead of letting the contract pull it, it would be lost forever because, in general, the contract would have no way of knowing who sent it.
As a part of
safeTransferTo()
, you'd have to specify a newEIP165
-esque check to make sure the receiver, if it is a contract, expects and will handle tokens pushed to it directly. However, given that no project out there knows about your token and your token will be the only one doing this to start (getting momentum on new paradigms is hard, and there's a reason we don't do the more intuitiveto
instead offrom
, as elucidated at the beginning of this answer), this function likely won't end up seeing much use.ERC20 is a problematic, creaky old standard that we only keep using because of its wide interoperability, even though we all know it's error-prone. Rather than throw in some new standards-non-conforming function, probably best to keep using
SafeERC20
--or just switch toERC777
!...just make sure you have fully anticipated the re-entrancy consequences that can result from hooks. :)