체인의정석

ERC721,1155 receiver 본문

블록체인/Solidity

ERC721,1155 receiver

체인의정석 2024. 4. 5. 12:21
728x90
반응형

https://github.com/OpenZeppelin/openzeppelin-contracts/blob/8a7a9c585706503f5250f09b033c94cf7ee37db2/contracts/token/ERC721/utils/ERC721Holder.sol#L17

 

openzeppelin-contracts/contracts/token/ERC721/utils/ERC721Holder.sol at 8a7a9c585706503f5250f09b033c94cf7ee37db2 · OpenZeppelin

OpenZeppelin Contracts is a library for secure smart contract development. - OpenZeppelin/openzeppelin-contracts

github.com

NFT를 컨트렉트에 보관할때는 reciever를 달아주어야 한다. 안전상의 이유인데 safeTransferFrom과 같이 safe가 붙으면 reciver가달렸는지 체크하고 있다.

따라서 AA 지갑, 브릿지 또는 valut를 만들때 reciever가 필요한데 오픈제플린의 ERC721Holder.sol 또는 1155Holder를 쓰면 인터페이스와 리시브 함수를 한번에 불러와 쓸 수 있어서 편하다. 

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/utils/ERC721Holder.sol)

pragma solidity ^0.8.20;

import {IERC721Receiver} from "../IERC721Receiver.sol";

/**
 * @dev Implementation of the {IERC721Receiver} interface.
 *
 * Accepts all token transfers.
 * Make sure the contract is able to use its token with {IERC721-safeTransferFrom}, {IERC721-approve} or
 * {IERC721-setApprovalForAll}.
 */
abstract contract ERC721Holder is IERC721Receiver {
    /**
     * @dev See {IERC721Receiver-onERC721Received}.
     *
     * Always returns `IERC721Receiver.onERC721Received.selector`.
     */
    function onERC721Received(address, address, uint256, bytes memory) public virtual returns (bytes4) {
        return this.onERC721Received.selector;
    }
}

그 외에 ERC721 Utils와 ERC1155 Utils가 존재하는데

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.20;

import {IERC721Receiver} from "../IERC721Receiver.sol";
import {IERC721Errors} from "../../../interfaces/draft-IERC6093.sol";

/**
 * @dev Library that provide common ERC-721 utility functions.
 *
 * See https://eips.ethereum.org/EIPS/eip-721[ERC-721].
 */
library ERC721Utils {
    /**
     * @dev Performs an acceptance check for the provided `operator` by calling {IERC721-onERC721Received}
     * on the `to` address. The `operator` is generally the address that initiated the token transfer (i.e. `msg.sender`).
     *
     * The acceptance call is not executed and treated as a no-op if the target address doesn't contain code (i.e. an EOA).
     * Otherwise, the recipient must implement {IERC721Receiver-onERC721Received} and return the acceptance magic value to accept
     * the transfer.
     */
    function checkOnERC721Received(
        address operator,
        address from,
        address to,
        uint256 tokenId,
        bytes memory data
    ) internal {
        if (to.code.length > 0) {
            try IERC721Receiver(to).onERC721Received(operator, from, tokenId, data) returns (bytes4 retval) {
                if (retval != IERC721Receiver.onERC721Received.selector) {
                    // Token rejected
                    revert IERC721Errors.ERC721InvalidReceiver(to);
                }
            } catch (bytes memory reason) {
                if (reason.length == 0) {
                    // non-IERC721Receiver implementer
                    revert IERC721Errors.ERC721InvalidReceiver(to);
                } else {
                    /// @solidity memory-safe-assembly
                    assembly {
                        revert(add(32, reason), mload(reason))
                    }
                }
            }
        }
    }
}

코드를 살펴보면 EOA인 경우 검사를 패스하는 로직이 들어 있어 가스비 입장에서 더 효율적이라는 것을 알 수 있다.

    /**
     * @dev Performs an acceptance check for the provided `operator` by calling {IERC721-onERC721Received}
     * on the `to` address. The `operator` is generally the address that initiated the token transfer (i.e. `msg.sender`).
     *
     * The acceptance call is not executed and treated as a no-op if the target address doesn't contain code (i.e. an EOA).
     * Otherwise, the recipient must implement {IERC721Receiver-onERC721Received} and return the acceptance magic value to accept
     * the transfer.
     */

설명도 이와 일치한다.

728x90
반응형
Comments