ERC-721, _checkOnERC721Received 함수 이해 [TIL / 솔리디티]

알락·2022년 12월 5일
0

이더리움

목록 보기
10/16

solidity banner

ERC-721, NFT를 발행할 수 있는 스마트 컨트랙트 작성을 학습 중이다. 사실 이더리움에서 제안된 EIP-721에서 ERC-721을 개발하는데 제안된 표준은 딱 블록체인에서 NFT가 거래될 수 있는 최소한의 기능만 구현되어있다.
그렇다보니 좀 더 현실 비즈니스 실정에 맞게, ERC-721에서 제공하는 표준에 더하여 필요한 기능들을 구현하는 인터페이스들이 제공되고 있다. 대표적으로 openZeppelin이 있다.

openZeppelin 이 제공하는 ERC721은 이름과 심볼만 지정하면 바로 배포할 수 있을 정도로 구현이 거의 다 완성되어 있다. 여기에 추가적으로 구현하고 싶은 기능은 상속하여, 오버라이드를 하여 구현하면 된다.

하지만 나는 기존의 구현되어있는 코드에서도 _checkOnERC721Received의 private 함수를 이해하는데 시간이 꽤 걸렸다. 이 함수를 이해해가는 과정을 설명해보려고 한다.


_CheckOnERC721Received

[함수 코드]

function _checkOnERC721Received(
        address from,
        address to,
        uint256 tokenId,
        bytes memory data
    ) private returns (bool) {
        if (to.isContract()) {
            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 {
                    /// @solidity memory-safe-assembly
                    assembly {
                        revert(add(32, reason), mload(reason))
                    }
                }
            }
        } else {
            return true;
        }
    }

구현되는 함수의 모습은 위와 같다.

1. try / catch 구문

우선 솔리디티의 try/catch 문을 이해해야 한다. 일반적인 try/catch 문과는 다르게 솔리디티에서는 다음고 같은 상황에서 이 구문을 사용한다.

  1. 외부 스마트 컨트랙트를 호출할 때.
  2. 외부 스마트 컨트랙트를 생성할 때.
  3. 스마트 컨트랙트 내에서 함수를 호출할 때.(this 사용 호출)

이런 상황에서 try/catch 문을 이용하는 이유는 외부 스마트 컨트랙트가 현재 작성하거나 배포할 스마트 컨트랙트와는 독립되어있기 때문이다. 외부 스마트 컨트랙트의 함수를 호출하거나, 생성할 때 에러가 생긴다면 해당 문제는 외부 스마트 컨트랙트 내부에서만 전달된다. 즉, 내가 만든 스마트 컨트랙트에서는 해당 에러를 따로 전달 받을 방법이 필요하다는 것이다. 이것을 try/catch 문이 해결해준다.

[해당 명령줄]

try IERC721Receiver(to).onERC721Received(_msgSender(), from, tokenId, data) returns (bytes4 retval) {
	return retval == IERC721Receiver.onERC721Received.selector;
}

만약 이와 같이 작성되어 있다면, {} 내에는 앞서 실행한 함수의 리턴값이 retval에 저장되어 사용할 수 있다.

2. 구현 이유

결국 _CheckOnERC721Received 함수 사용 이유를 알아볼 필요가 있다. 이더리움 블록체인에서는 EOA 주소와 Contract 주소를 한 포맷을 공유하여 사용하고 있다. 이 때, NFT가 EOA로 전송요청되면 상관없이 보내도 되지만, Contract 주소에 보내면 문제가 생긴다.

Contract 계정을 EOA 계정과 달리 트랜잭션을 직접 보낼 수 있는 방법이 없다. 그렇다면 기껏 전송된 NFT를 제3자에 의해서 빼내오거나 전송될 방법이 해당 컨트랙트에서 구현되어있지 않다면, 전송된 NFT는 Contract에 영원히 귀속되어 버린다.

그래서 Contract 계정 한정, 계정에 NFT가 전송되어도 NFT를 다룰 수 있는 기능이 구현되어있다는 것을 확인하기 위해 _CheckOnERC721Received를 구현하여 사용하고 있다.


참고

profile
블록체인 개발 공부 중입니다, 프로그래밍 공부합시다!

0개의 댓글