ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • ERC721 구현
    Blockchain 2022. 1. 9. 11:45
    반응형

    ERC721 Interface

    EIP-721

    NFT 를 위한 인터페이스를 eip721 에서 규약 하고 있으며, 해당 인터페이스를 직접 구현 해보도록 한다.
    개발시 payable 키워드는 필요한 경우에만 사용 하도록 하고, 불필요한 함수들에서 제거 하도록 한다.
    또한 external 을 public 으로 변경하여 구현 하도록 한다.
    참고: 인터페이스 전체 함수를 구현 하지는 않고, 기본적인 함수만 구현 하도록 한다.

    EIP-721

    EIP721 인터페이스는 아래와 같다.

    pragma solidity ^0.4.20;
    
    interface ERC721 /* is ERC165 */ {
        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);
        function balanceOf(address _owner) public view returns (uint256);
        function ownerOf(uint256 _tokenId) public view returns (address);
        function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes data) public;
        function safeTransferFrom(address _from, address _to, uint256 _tokenId) public;
        function transferFrom(address _from, address _to, uint256 _tokenId) public ;
        function approve(address _approved, uint256 _tokenId) public;
        function setApprovalForAll(address _operator, bool _approved) public;
        function getApproved(uint256 _tokenId) public view returns (address);
        function isApprovedForAll(address _owner, address _operator) public view returns (bool);
    }
    

    ERC721 구현

    ERC721Impl.sol

    ERC721 구현 - mint 함수

    ERC721 을 상속받아서 구현

    contract ERC721Impl is ERC721 {
        //토큰아이디와 소유자 주소 매핑 
        mapping (uint256 => address) tokenOwner;
        //소유자 토큰 개수 매핑
        mapping (address => uint256) ownedTokensCount;
    
        function mint(address _to, uint _tokenId) public {
            //토큰 아이디를 토큰 주소로 매핑 시켜 준다.
            tokenOwner[_tokenId] = _to; 
            //소유자 토큰 개수를 증가 시켜 준다.
            ownedTokensCount[_to] += 1;
        }
    }
    

    ERC721 구현 - balanceOf 함수

    ERC721 을 상속한 ERC721Impl 클래스에 구현 하도록 한다.
    사용자의 주소에 매핑된 토큰의 개수를 리턴 하도록 구현

    function balanceOf(address _owner) public vioew returns (uint256) {
            return ownedTokensCount[_owner];
        }

    ERC721 구현 - ownerOf 함수

    토큰아이디로 소유자 주소 리턴하도록 한다.

    function ownerOf(uint256 _tokenId) public view returns (address){
            return tokenOwner[_tokenId];
        }

    ERC721 구현 - transferFrom 함수

    유효성 검사 및 토큰 소유자의 주소와 갯수를 증감 / 차감 하도록 구현

        function transferFrom(address _from, address _to, uint256 _tokenId) public {
            address owner = ownerOf(_tokenId);
            //함수를 호출한 계정이 소유자 인지 검증 
            require(msg.sender == owner 
            || getApprove(_tokenId) == msg.sender
            || isApproveForAll(owner, msg.sender));
            //비어있는지 검증
            require(_from != address(0));
            require(_to != address(0));
    
            //토큰의 갯수를 차감
            ownedTokensCount[_from] -= 1;
            //토큰의 아이디의 기존 주소를 제거
            tokenOwner[_tokenId] = address(0);
            //전송받은 소유자의 토큰 갯수 증가
            ownedTokensCount[_to] += 1;
            //토큰소유자의 새로운 소유자로 매핑
            tokenOwner[_tokenId] = _to;
        }

    ERC721 구현 - safeTransferFrom 함수

    해당 컨트랙이 토큰을 받을수 있는지 검증하기 위한 ERC721Receiver 인터페이스 구현

    ERC721Receiver

    interface ERC721TokenReceiver {
        function onERC721Received(address _operator, address _from, uint256 _tokenId, bytes _data) public returns(bytes4);
    }
    
    //ERC721TokenReceiver를 구현한 컨트랙트
    contract ERC721TokenReceiverImpl is ERC721TokenReceiver {
        function onERC721Received(address _operator, address _from, uint256 _tokenId, bytes _data) public returns(bytes4) {
            //keccak256 에 함수시그니쳐를 넘겨주면 0x150b7a02 값을 리턴 하게 된다. 
            return bytes4(bytes4(keccak256("onERC721Received(address,address,uint256,bytes)")));
        }
    }
    
    //컨트랙 계정을 구분하고, 컨트랙이 토큰을 받을수있는지 확인 할수 있는 전송 함수
            function safeTransferFrom(address _from, address _to, uint256 _tokenId) public {
                transferFrom(_from,_to, tokenId);
                if(isContract(_to) {
                    //ERC721 인터페이스 검증을 위해 아래 리턴받은 값이 인터페이스 식별값과 동일한지 비교하도록 한다.
                    //참고: ERC721TokenReceiver 인터페이스 
                    bytes4 returnValue = ERC721TokenReceiver(_to).onERC721Received(msg.sender, _from, _tokenId, ''); 
                    require(returnValue == 0x150b7a02);
                }
            }
    
            function isContract(address _addr) private view returns (bool) {
                uint256 size;
                //extcodesize 를 사용하여 해당 주소의 사이즈가 0보다 크면 true
                //참고1: extcodesize 는 계정의 코드 사이즈를 반환 한다. 현재 실행 환경의 컨트랙트를 대상으로 한다.
                //참고2: assembly 는 solidity 에서 evm 의 low level 연산을 수행 할수있도록 도와준다.
                assembly {size:=extcodesize(_addr)}
                return size >0;
            }

    ERC721 구현 - approve 함수

    소유자의 토큰을 다른 계정이 대신 전송 해주도록 승인 해주는것

    토큰 전송 권한이 있는 (승인받은) 계정 정보를 담을 수 있도록  매핑 정의

    mapping (uint256 => address) tokenApprovals;

    전송 승인 권한 함수 구현

    function approve(address _approved, uint256 _tokenId) public {
                address owner = ownerOf(_tokenId);
                //소유자 계정이 아니어야 한다.
                require(_approved != owner);
                //호출한 계정이 토큰 아이디의 소유자여야 한다.
                require(msg.sender == owner);
                tokenApprovals[_tokenId] = _approved;
            }

    ERC721 구현 - getApprove 함수

    토큰 전송 승인 권한

    function getApprove(uint256 _tokenId) public view returns (address) {
                return tokenApprovals[_tokenId];
    }

     

    ERC721 구현 - setApprovalForAll  함수

    토큰의 전송 권한을 여러명에게 넘겨주기 위해 매핑 선언 및 함수 구현 
    //누가 누구에게 권한 부여를 했는가를 정의하는 매핑 정보 (여러명에게 권한을 부여하기 위해 정의)
            mapping (address => mapping(address => bool)) operatorApprovals;
            function setApprovalForAll(address _operator, bool _approved) public {
                //권한을 부여받게될 operator 계정이 현재 함수를 호출한 계정인지 확인
                require(_operator != msg.sende
                operatorApprovals[msg.sender][_operator] = _approved;
            }

    ERC721 구현 - isApprovalForAll  함수

    토큰의 소유자가 오퍼레이터에게 권한을 부여했는지 검증 하는 함수  
    function isApprovalForAll(address _owner, address _operator) public view returns (bool) {
                return operatorApprovals[_owner][_operator];
            }

     

    ERC165 Interface

    ERC165 의 목적은 컨트랙트가 어떤 인터페이스를 상속 받고 있는지 확인 하는 용도이다.
    interface ERC165 {
        function supportsInterface(bytes4 interfaceID) external view returns (bool);
    }

    ERC165 Interface 구현

    //인터페이스 식별자 매핑 정보
    mapping (bytes4 => bool) supportedInterfaces;
    constructor() public {
    //ERC721 인터페이스의 식별자 키 값을 0x80ac58cd 로 정의하고 값을 true 로 함으로써 ERC721 인터페이스를 구현 하고 있는것을 생성자를 통해 정의 함.
      supportedInterfaces[0x80ac58cd] = true;
    }
    function supportsInterfacebytes4 interfaceID) public view returns (bool) {
    	return supportedInterfaces[interfaceID];
    }

     

     

    반응형

    'Blockchain' 카테고리의 다른 글

    Smart contract Dev setup  (0) 2022.01.09
Designed by Tistory.