[ERC 시리즈] ERC-165

molly·2023년 3월 8일
1

ERC/EIP

목록 보기
2/2
post-thumbnail

리서치 이유

멀티토큰(ERC-1155)을 SBT로 만들어보고 싶었는데 관련 eip문서를 보던 중 ERC-165에 대한 언급이 나왔고 살펴보았다.

전에 ERC-20 토큰의 표준에 만족하는지에 대한 여부를 체크할 수 있는 프로그램을 허접하게 만들었는데 해당 로직은 바이트코드에 ERC-20 표준 함수셀렉터들이 모두 포함되어 있는 지를 검사하는 과정이였다. 그런데 ERC-165를 리서치하다보니 이 표준을 통해서도 ERC-20 표준 여부 만족을 체크할 수도 있겠다는 생각을 하였고 실제로 가능한지 확인해보았다.

목적

스마트 컨트랙트가 구현하는 인터페이스를 게시하고 감지하는 표준을 생성

기능

인터페이스를 식별하는 방법

검색 컨트랙트가 구현하는 인터페이스를 게시하는 방법

컨트랙트가 ERC-165를 구현하는지 감지하는 방법

컨트랙트가 특정 인터페이스를 구현하는 지 감시하는 방법

사양

  • 인터페이스를 식별하는 방법

함수셀렉터란?

스마트 컨트랙트의 함수 아이디라고 생각하면 되는데 ERC-20의 balanceOf(address)함수의 아이디는

“balanceOf(address)”를 keccak256으로 돌리면 나오는 값의 앞에 8자리를 함수셀렉터라고 한다.

ERC-20

functransfer(address,uint256)a9059cbb
funcapprove(address,uint256)095ea7b3
funcbalanceOf(address)70a08231
functransferFrom(address,address,uint256)23b872dd
funcallowance(address,address)dd62ed3e
functotalSupply()18160ddd

스마트 컨트랙트 내에서 셀렉터를 구하는 방법은 두 가지로 keccak256을 쓰거나

this.balance.selector를 사용하면 된다.

구한 셀렉터들을 xor연산(^)을 해주면 인터페이스 식별자 아이디를 구할 수 있게 된다.

  • 검색 컨트랙트가 구현하는 인터페이스를 게시하는 방법

erc165를 구현하는 컨트랙트는 다음과 같은 인터페이스를 구현해야 한다.

pragma solidity^0.4.20;

interface ERC165 {
/// @notice Query if a contract implements an interface
/// @param interfaceID The interface identifier, as specified in ERC-165
/// @dev Interface identification is specified in ERC-165. This function
///  uses less than 30,000 gas.
/// @return `true` if the contract implements `interfaceID` and
///  `interfaceID` is not 0xffffffff, `false` otherwise
function supportsInterface(bytes4 interfaceID)externalviewreturns (bool);
}

이 인터페이스의 인터페이스 식별자는 0x01ffc9a7이다. → bytes4(keccak256('supportsInterface(bytes4)'))

interfaceID가 0x01ffc9a7(EIP165 인터페이스)인 경우 true

interfaceID가 0xffffffff인 경우 false

  • 컨트랙트가 ERC-165를 구현하는지 감지하는 방법

소스 컨트랙트는 입력 데이터와 함께 대상 주소로 STATICCALL을 만든다.

입력데이터 ⇒ 0x01ffc9a701ffc9a7000000000000000000000000000000000000000000000000( contract.supportsInterface(0x01ffc9a7)) 및 가스 30,000.

호출이 실패하거나 거짓을 반환하면 대상 컨트랙트는 ERC-165를 구현하지 않는다..

호출이 참을 반환하면 입력 데이터 0x01ffc9a7ffffff00000000000000000000000000000000000000000000으로 두 번째 호출이 수행된다.

두 번째 호출이 실패하거나 참을 반환하면 대상 컨트랙트는 ERC-165를 구현하지 않고

그렇지 않으면 ERC-165를 구현해야 한다.

  • 컨트랙트가 특정 인터페이스를 구현하는지 확인하는 방법

컨트랙트가 ERC-165를 구현하는지 확실하지 않은 경우, 위의 절차를 통해 확인

ERC-165를 구현하지 않는다면 구식 방식으로 어떤 메소드를 사용하는지 확인해야 함(해당 방법은 복잡하고 에러가 나올 확률도 있음)

ERC-165를 구현하는 컨트랙트라면 supportsInterface(interfaceID)를 호출하여 사용할 수 있는 인터페이스를 구현하는지 확인하면 됨.

컨트랙트가 어떤 인터페이스를 제공하였는지 확인하는 테스트 코드

// SPDX-License-Identifier: MIT
pragma solidity^0.4.20;

contract ERC165Query {
    bytes4  InvalidID= 0xffffffff;
    bytes4  ERC165ID= 0x01ffc9a7;

    function doesContractImplementInterface(address _contract,bytes4 _interfaceId)external view returns (bool) {
	    uint256 success;
	    uint256 result;

        (success, result)= noThrowCall(_contract, ERC165ID);
        if ((success==0)||(result==0)) {    
            return false;
        }

        (success, result)= noThrowCall(_contract, InvalidID);
        if ((success==0)||(result!=0)) {
            return false;
        }

        (success, result)= noThrowCall(_contract, _interfaceId);
        if ((success==1)&&(result==1)) {
            return true;
        }
        return false;
    }

    function noThrowCall(address _contract,bytes4 _interfaceId) constant internal returns (uint256 success, uint256 result ) {
        bytes4 erc165ID= ERC165ID;

        assembly {
        let x:= mload(0x40)// Find empty storage location using "free memory pointer"
                mstore(x, erc165ID)// Place signature at beginning of empty storage
                mstore(add(x, 0x04), _interfaceId)// Place first argument directly next to signature
        success:= staticcall(
                            30000,// 30k gas
                            _contract,// To addr
                            x,// Inputs are stored at location x
                            0x24,// Inputs are 36 bytes long
                            x,// Store output over input (saves space)
                            0x20)// Outputs are 32 bytes long
        result:= mload(x)// Load the result
        }
    }
}

결론

로컬 테스트넷에서 테스트를 해봤는데

토큰과 nft를 테스트 해본 결과 토큰은 잘 찾아내지 못하고 nft는 잘 찾아냈다.

그래서 혹시 몰라 네트워크를 폴리곤에서 테스트 하였을 때도 NFT는 잘 찾아내지만 ERC20은 찾아내지 못했다.

그래서 공식문서를 보았을 때 erc20은 165에 대한 언급이 없지만 721은 있는 걸로 봐서 721들은 구현이 되었기에 찾을 수 있었던 것으로 확인되었다.

결론은 165를 통해서 토큰의 표준을 만족하는지에 대한 여부를 코드에 반영하는 것은 힘들것 같다.

profile
BlockChain R&D

0개의 댓글

관련 채용 정보