ERC-165란 뭘까?

김병하
None
Published in
7 min readAug 17, 2018

안녕하세요. 휴먼스케이프의 김병하입니다.

이 번 포스팅은 이더리움의 표준 중 하나인 ERC-165에 대한 내용입니다.

이더리움에는 ERC 20, ERC 223, ERC 621, ERC 667, ERC 721 등 다양한 표준이 존재하고 각 표준은 인터페이스 형태로 정의되어 있습니다. 표준이 되는 인터페이스에 맞춰 스마트 컨트랙트를 작성하면 해당 컨트랙트를 표준에 맞게 사용할 수 있게 됩니다.

하지만 컨트랙트의 주소만으론 해당 스마트 컨트랙트가 어떤 표준을 실제로 구현했는지 또는 몇 버전의 표준을 구현했는지 알 수 없습니다. 그래서 등장한 것이 ERC-165입니다.

ERC 165

ERC-165 역시 인터페이스로 정의되어 있는데요. 사용하는 로직은 아주 간단합니다. 아래는 open zeppelin 에 정의되어있는 ERC-165 인터페이스입니다.

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) external view returns (bool);
}

solidity의 함수들은 모두 selector를 가지고 있습니다. selector는 간단히 생각하시면 함수의 아이디라고 생각하셔도 무방합니다. 이 아이디를 만드는 방법은 함수의 시그니처를 해싱하는 건데요. 예를 들어서

function balanceOf(address _owner) external view returns (uint256){
//...
};

위와 같은 함수의 아이디를 얻기 위해서는

bytes4(keccak256("balanceOf(address)"))

위처럼 keccak256을 이용해서 해싱하거나 혹은

this.balanceOf.selector

이렇게 컨트랙트 내부에서 각 메소드의 selector를 호출해서 확인하실 수 있습니다. 기본적으로 함수의 시그니처에 따라 다른 해시값이 나오고 이 해시값이 해당 함수의 selector가 됩니다.

ERC-165에선 인터페이스의 구현 여부를 확인하기 위해 이 selector를 사용합니다.

이 때 함수의 selector가 해당 함수의 ID라고 한다면 우리는 인터페이스에서 정의한 함수를 모두 구현했는지 여부를 확인하기 위해 인터페이스의 ID에 해당하는 값이 필요한데요. 이 아이디는 인터페이스를 구성하는 모든 함수의 selector를 비트 단위 xor 연산을해서 구합니다.

pragma solidity ^0.4.20;

import "./ERC165.sol";

interface Simpson {
function is2D() external returns (bool);
function skinColor() external returns (string);
}

contract Homer is ERC165, Simpson {
function supportsInterface(bytes4 interfaceID) external view returns (bool) {
return
interfaceID == this.supportsInterface.selector || // ERC165
interfaceID == this.is2D.selector
^ this.skinColor.selector; // Simpson
}

function is2D() external returns (bool){}
function skinColor() external returns (string){}
}

위 코드에서 보시면 Simpson이라는 인터페이스의 인터페이스 id를 구하기 위해

this.is2D.selector ^ this.skinColor.selector

와 같이 연산하는 것을 보실 수 있습니다. 해당 스마트 컨트랙트 Homer가 Simpson 인터페이스를 구현했는지 여부를 확인하고 싶다면 function supportsInterface(bytes4 interfaceID) {} 에 Simpson의 인터페이스 id를 파라미터로 넣어서 호출합니다. true를 반환한다면 컨트랙트 Homer 는 Simpson 인터페이스를 구현한 것이고, false 를 반환한다면 구현하지 않은 것입니다.

ERC-165는 어디에 쓰지?

사실 특정 컨트랙트가 어떤 메소드를 구현했는지 일반적으로 확인하는 방법은 etherscan.io를 확인하는 것입니다. etherscan.io에 특정 컨트랙트의 주소로 접근해보면 해당 컨트랙트가 구현한 메소드들을 확인하실 수 있습니다. 아래는 HUM 토큰을 etherscan.io에서 조회했을 때의 예입니다.

하단에 있는 [Read Contract] 탭을 선택하면 읽기 전용으로 정의된 메소드를 확인하고 또 호출할 수 있다
하단에 있는 [Write Contract] 탭을 선택하면 쓰기용으로 정의된 메소드를 확인하고 또 호출할 수 있다

그렇다면 이렇게 쉽게 확인할 수 있는데 ERC-165는 왜 쓰는 걸까요? etherscan.io에서 확인할 수 있는 정보는 onchain이지만, 이 정보를 보여주는 etherscan.io는 offchain입니다. 이런 서비스가 없어도 스마트 컨트랙트는 사용될 수 있고 또 그래야 합니다. 그렇기 때문에 온전히 onchain 방식으로 특정 인터페이스를 정의했는지 확인하는 기능은 필요합니다. 그래야 특정 컨트랙트와 상호작용할 때 인터페이스를 온전히 정의했는지 확인하고 기능을 수행할 수 있습니다.

감사합니다.

Get to know us better!
Join our official channels below.

Telegram(EN) : t.me/Humanscape
KakaoTalk(KR) : open.kakao.com/o/gqbUQEM
Website : humanscape.io
Medium : medium.com/humanscape-ico
Facebook : www.facebook.com/humanscape
Twitter : twitter.com/Humanscape_io
Reddit : https://www.reddit.com/r/Humanscape_official
Bitcointalk announcement : https://bit.ly/2rVsP4T
Email : support@humanscape.io

--

--