ERC-721 인터페이스
ERC-721 인터페이스에 명시된 함수는 ERC-20에 비해 상대적으로 많은 편이다. 토큰 정보 확인부터 전송, 권한 설정, 주소 추적 기능이 명시되어 있다.
인터페이스 함수 목록
- balanceOf : 파라미터로 전달받은 주소가 보유하고 있는 NFT 수량 반환
- ownerOf : 파라미터로 전달 받은 NFT 토큰 주소를 소유하고 있는 주소 반환
- safeTransferFrom : 전전송받을 주소(_to)가 NFT 토큰을 받을 수 있는지 검증하고 전송 진행(주소 종류가 EOA, CA에 따라 전달 파라미터 형식이 달라진다. 아래 코드 참조)
- transferFrom : NFT 소유 주소로부터 다른 주소로 토큰 전송
- approve : 파라미터로 전달 받은 주소에 NFT 전송 권한 부여
- setApprovalForAll : 파라미터로 전달 받은 주소에 모든 NFT 전송 권한 부여 및 해제
- getApproved : 파라미터로 전달 받은 NFT 토큰 주소에 대한 권한이 있는 주소 반환
- isApprovedForAll : setApprovalForAll 함수 실행 권한 설정
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// ERC-721 인터페이스
interface ERC721 {
/************************************ 인터페이스 함수 ************************************/
// 해당 주소가 보유하고있는 NFT 수량 반환
function balanceOf(address _owner) external view returns (uint256);
// NFT를 소유하고 있는 주소 반환
function ownerOf(uint256 _tokenId) external view returns (address);
// 전송받을 주소(_to)가 ERC-721 토큰을 받을수 있는지 검증하고 전송 [지갑(EOA, Externally Owned Account)에 전송]
function safeTransferFrom(address _from, address _to, uint256 _tokenId) external payable;
// 전송받을 주소(_to)가 ERC-721 토큰을 받을수 있는지 검증하고 전송[컨트렉트(CA, Contract Account)에 전송]
function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes data) external payable;
// NFT 소유자로부터 다른 주소로 NFT 토큰 전송
function transferFrom(address _from, address _to, uint256 _tokenId) external payable;
// 특정 주소에 NFT 전송 권한 부여
function approve(address _approved, uint256 _tokenId) external payable;
// 특정 주소에 NFT 소유자가 모든 NFT 전송 권한 부여 및 해제
function setApprovalForAll(address _operator, bool _approved) external;
// 특정 토큰 전송 권한을 갖고 있는 주소 반환
function getApproved(uint256 _tokenId) external view returns (address);
// setApprovealForAll 권한 여부 반환(true/false)
function isApprovedForAll(address _owner, address _operator) external view returns (bool);
/************************************ 이벤트 ************************************/
event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);
event Approval(address indexed _owner, address indexed _approved, uint256 indexed _tokenId);
event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);
}
ERC-165 인터페이스
ERC-165 인터페이스를 구현한 컨트렉트는 해당 컨트렉트에서 지원(Support)하는 인터페이스를 확인할 수 있게 된다. 인터페이스 식별자(Identifier) 값이 참/거짓으로 매핑되어 관리된다. ERC-165에 명시된 인터페이스 식별자를 전달하면 지원 여부를 true / false 값으로 확인할 수 있다.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// ERC-165 인터페이스 : 스마트 컨트렉트 탐색(Detecting) 표준
// https://eips.ethereum.org/EIPS/eip-165
interface ERC165 {
/*
* @dev 특정 인터페이스가 스마트 컨트렉트에 포함되어 있는지 확인(가비스 30,000 이하)
* @param _interfaceID : ERC-165에 명시된 인터페이스 식별자(Identifier)
* @return true/false : 지원가능 유무
*/
function supportsInterface(bytes4 _interfaceID) external view returns (bool);
}
Supports Interface 컨트렉트
ERC-165 인터페이스를 구현해 ERC-721 토큰에 상속되어 사용되는 유틸리티 컨트렉트다. 인터페이스 지원(Support) 여부를 바이트 타입 인터페이스 식별자 값과 bool 타입 값으로 매핑하여 관리한다.
ERC-165 인터페이스에 대한 설정을 생성자에서 기본적으로 지정해주며, 개발하고자 하는 ERC-721에 따라 추가적인 설정이 가능하다.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Import
import "./ERC165.sol"; // ERC165 인터페이스
// 스마트 컨트렉트 인트페이스 탐색(Detecting) 유틸 (ERC-165 인터페이스 구현)
contract SupportsInterface is ERC165 {
// 지원 인터페이스 매핑 (인터페이스 바이트코드 -> true/false)
// 0xffffffff 바이트 코드에 true 값 금지
mapping(bytes4 => bool) internal supportedInterfaces;
// 생성자 -> ERC-165 인터페이스 바이트코드를 고정값으로 입력 (가스비 절감)
constructor() {
supportedInterfaces[0x01ffc9a7] = true;
}
/*
* @dev 이 컨트렉트에 어떤 인터페이스를 지원하지는 확인
* @param _interfaceID : 인터페이스 바이트코드
* @return true/false : 지원가능 유무
*/
function supportsInterface(bytes4 _interfaceID) external override view returns (bool) {
return supportedInterfaces[_interfaceID];
}
ERC-721 Receiver (지갑 인터페이스)
이 인터페이스는 컨트렉트에 토큰이 락업되는 상황을 방지한다. 만약 토큰을 받은 컨트렉트가 다른 컨트렉트로 토큰 전송이 불가능한 상태라면 문제가 된다. 특정 계정에 한 해 토큰 유통이 막혀버리는 상황이 발생하는 것이다.
Receiver 인터페이스는 ERC-721 토큰 컨트렉트에서 토큰 전송(safeTransferFrom 함수)이 이루어질 때 받는 계정이 토큰을 전송할 수 있는 계정인지 아닌지에 대한 검증 표준을 제공한다. 해당 인터페이스를 구현한 컨트렉트는 지갑 및 옥션과 같은 서비스 로직을 구현할 수 있다.
NFT 토큰 전송(tranfer)이 이루어졌을 때, 토큰을 전달받은 계정(recipient) 쪽에서 이 인터페이스 함수를 호출하게 된다. 해당 함수는 토큰 전달 주소와 받는 주소, 토큰 ID, 기타 바이트 코드를 인자값으로 받는다. 이때, 인자값들이 데이터 타입에 맞게 입력되었는지를 검증한다.
반환값은 함수 식별자(Identifier)
이며 bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))
형식이다. 해당 인터페이스를 구현한 ERC-721 토큰 컨트렉트에서 Magic Value라는 함수 식별자에 대한 상수값(0x150b7a02
)과 일치하는지 비교한다.
이 인터페이스 구현에 특징에 대해 아래와 같이 정리할 수 있다.
- 컨트렉트 주소는 항상 msg.sender과 동일
- Safe Transfer 기능을 위해 해당 인터페이스 구현 필수
- NFT 토큰 전송에서 영수(receipt) 처리
- 유효하지 않은 토큰 전송에 대해 거절(reject) 및 롤백(revert) 처리 (예외 처리)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// 지갑 인터페이스 : Safe Transfer 허용
// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
interface ERC721TokenReceiver {
/*
* @param _operator : `safeTransferFrom` 함수를 호출한 주소
* @param _from : NFT 토큰 전소유자
* @param _tokenId : NFT 토큰 아이디
* @param _data : 추가 데이터(정해진 포멧은 없음)
* @return `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))` 반환
*/
function onERC721Received(address _operator, address _from, uint256 _tokenId, bytes calldata _data) external returns(bytes4);
}
Address Utills
주소(Address) 관련 유틸리티 라이브러리로 주어진 주소값이 유효한지 검증한다. 주소의 해쉬값이 유효한 값인지 이더리움 가상머신(EVM)에서 해석하여 결과를 반환한다.
EIP-1052라는 이더리움 가상머신의 Opcode를 사용하는데, 코드 상에서 extcodehash
로 표기되어 있음을 확인할 수 있다.
이 라이브러리의 유효성 검증은 주소 해쉬값이 공백인지 아닌지만 검증한다. 0x0
혹은 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470
값은 주소가 존재하지 않음을 나타내는 해쉬값으로 아래 코드에서 단순 비교를 통해 결과를 반환함을 확인할 수 있다.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// 주소(Address) 관련 유틸리티 라이브러리
// EIP-1052 규칙을 따름
// https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/Address.sol
library AddressUtils {
/*
* @dev 지정한 주소값이 유효한 컨트렉트인지 검증
* @param _addr : 검증하고자하는 주소값
* @return addressCheck : 검증 여부를 true/false로 반환
*/
function isContract(address _addr) internal view returns (bool addressCheck) {
/*
* 코드는 생성자 실행이 완료되고 나서 저장되므로, 생성 중인 컨트랙트에 대해 0을 반환하는 extcodesize에 의존
*
* EIP-1052에 따라 "0x0"값은 존재하지 않는 어카운트에 대한 결과값이며,
* "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"값은 `keccak256('')`와 같은 코드가 없는 어카운트의 결과값
*/
bytes32 codehash;
bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
assembly { codehash := extcodehash(_addr) }
addressCheck = (codehash != 0x0 && codehash != accountHash);
}
}
'Blockchain > Ethereum' 카테고리의 다른 글
[Ethereum] ERC-721 소스 분석(3) - 메타 데이터 (0) | 2022.03.25 |
---|---|
[Ethereum] ERC-721 소스 분석(2) - 토큰 (0) | 2022.03.24 |
[Ethereum] ERC-721 (0) | 2022.03.23 |
[Ethereum] ERC-20 (0) | 2022.03.23 |
[Ethereum] ERC란? (0) | 2022.03.22 |
댓글