1

When a contract receives an NFT by calling safeTransferFrom, onERC721Received is automatically called on the receiving contract. I want to be able to update a Hashmap to record the address of the sender and the NFT token ID when a contract receives an NFT by modifying the onERC721Received function.

Here, calling setOwner works as expected:

ownerMapping: HashMap[address, DynArray[uint256, 3]]

@external
def setOwner(newAddress:address, nft:uint256):
    self._setOwner(newAddress, nft)
    
@internal
def _setOwner(newAddress:address, nft:uint256):
    #set a new owner
    self.ownerMapping[newAddress].append(nft)

but when I try to call _setOwner in the onERC721Received function by sending an NFT to the contract using safeTransferFrom the transaction reverts:

ownerMapping: HashMap[address, DynArray[uint256, 3]]

@external
def onERC721Received(_operator: address,_from: address,_tokenId: uint256,_data: Bytes[1024]) -> bytes4:
    self._setOwner(_from, _tokenId)
    return method_id("onERC721Received(address,address,uint256,bytes)", output_type=bytes4)
    
@internal
def _setOwner(newAddress:address, nft:uint256):
    #set a new owner
    self.ownerMapping[newAddress].append(nft)

if I directly call onERC721Received, then _setOwner is called successfully. It is only when onERC721Received is called by sending an NFT to the contract via safeTransferFrom that the transaction reverts.

Here is the safeTransferFrom function along with the interface from the ERC721 token standard I am using (from here). You can see that onERC721Received is called :

interface ERC721Receiver:
    def onERC721Received(
            _operator: address,
            _from: address,
            _tokenId: uint256,
            _data: Bytes[1024]
        ) -> bytes4: view
        
def safeTransferFrom(
        _from: address,
        _to: address,
        _tokenId: uint256,
        _data: Bytes[1024]=b""
    ):
    self._transferFrom(_from, _to, _tokenId, msg.sender)
    if _to.is_contract: # check if `_to` is a contract address
        returnValue: bytes4 = ERC721Receiver(_to).onERC721Received(msg.sender, _from, _tokenId, _data)
        # Throws if transfer destination is a contract which does not implement 'onERC721Received'
        assert returnValue == method_id("onERC721Received(address,address,uint256,bytes)", output_type=bytes4)
9
  • Is the safeTransferFrom function calling onERC721Received? Aug 13, 2022 at 1:20
  • 1
    @PatrickCollins question updated for clairty. Btw your smart contract video course is great! thanks
    – Ryan
    Aug 13, 2022 at 9:44
  • 1
    Yep, I think you're right. Interesting, the actual EIP doesn't have the view keyword: eips.ethereum.org/EIPS/eip-721 Aug 13, 2022 at 15:48
  • 1
    Perhaps we should make an issue on the vyper repo to update this Aug 13, 2022 at 15:48
  • 1
    @PatrickCollins this was fixed: github.com/vyperlang/vyper/pull/3076
    – Ryan
    Nov 4, 2022 at 10:39

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.