0

When you use OpenZeppelin's ERC721 implementation, in most cases you call _safeMint function. _safeMint does some obvious basic operations except this one:

require(
    _checkOnERC721Received(address(0), to, tokenId, _data),
    "ERC721: transfer to non ERC721Receiver implementer"
);

Makes sense it tries to prevent wrong implementation of ERC721Receiver implementation. But it isn't clear when I try to dig deeper:

try IERC721Receiver(to).onERC721Received(_msgSender(), from, tokenId, _data) returns (bytes4 retval) {
                return retval == IERC721Receiver.onERC721Received.selector;
            } catch (bytes memory reason) {
                if (reason.length == 0) {
                    revert("ERC721: transfer to non ERC721Receiver implementer");
                } else {
                    assembly {
                        revert(add(32, reason), mload(reason))
                    }
                }
            }

It's comparing retval to the function selector to confirm the token transfer. Cool.

But what is assemby trying to do?

1 Answer 1

0

if onERC721Received fails it is checked if a customError is returned by the contract call or if it just does not implement the IERC721Receiver

if (reason.length == 0) {

means theres no custom error -> IERC721Receiver not implemented or empty revert by the contract

} else {
    assembly {
    revert(add(32, reason), mload(reason))
}

means there is a custom error (reason.length > 0) In this case we dont know which size the error string has (in bytes) We dont know if it is 32, 64 or even more bytes. Thats why we need to pass two arguments to the revert function, lets call them p and s -> revert(p, s)

Both (p and s) represent positions in memory. The first position is:

add(32, reason)// skipping the first 32 bytes containing the length

the first 32 bytes (containing the length) which we checked before also .thats why your "data starting point" p is: 32 + reason (starting position in memory) To determine our end position s we just load the first 32 bytes of reason containing the length. Means we select all from the position where our actual data starts (p) to the position where our data ends (s ~ p+length). we could also write it down like that: mem[p…(p+s))

2

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.