[프로그래밍 반] solidity 입문기 - 1. smart contract, NFT 민팅, 그리고 ERC-721 프로토콜의 삼각 관계👪

2022. 8. 23. 01:14NFT/창작모꼬지

내가 느낀 smart contract는 정말 계약서에 빈칸을 채우는 느낌이었다.

 

1. 왜 NFT를 발행할 때 smart contract가 필요할까?

 

solidity 적응기 1. 어렵지 않아요 헤치지 않아요.(기초 구조)

대망의 solidity를 처음으로 해봤습니다. 자료형을 쭉 읊어봤자 의미 없는 것 같고 일단 구조부터 크게 이해를 하고 넘어가는게 좋을 것 같습니다. 먼저, 이 시리즈는 개인적으로 이해한 내용을 가

dev-russel.tistory.com

 

저번 시리즈에서 우린 간단한 solidity의 사용법을 알아봤었다.

항상 문법을 배울 때 도대체 이 언어의 목적이 뭘까? 이런 생각이 항상 든다.

python을 배울때도, java(for android)를 배울 때도, JS를 배울 때도 항상 똑같았다.

 

이건 내 개인적인 의견인데, 간단한 프로젝트를 함께 해본다면 언어의 목적이나 사용성이 조금은 명확해진다. python은 처음으로 selenium을 써봤을 때, 안드로이드 스튜디오에서 액티비티를 처음 전환시켜봤을 때, javascript로 DOM에 처음 접근 해봤을 때 비로소 딱 드는 바로 그 느낌이다.

"아.. 이거 이렇게 쓰는 거구나!"

 

solidity도 마찬가지다. 그리고 그 시작은 NFT이다.

일단 NFT를 우리가 일반적으로 떠올린다면, 이런 아트워크가 생각난다.

Doodle!

 

그림을 잘 그리는 방법이나, Generative 아트를 생성하는 방법에 대해서 말하려는게 아니다.

웹에서 이런 이미지 파일을 클라이언트에서 관리할 때 이미지를 그대로 다루지 않는다.

https://nftgames.net/wp-content/uploads/2022/03/doodlesnft.png

이런 경로에 우리가 원하는 원본파일을 넣고 이 몇 바이트밖에 되지 않는 글자 주소로 사용한다.

 

184KB의 파일을 웹 상에 넣는 건 정말... 말도 안되는 일이다!

 

블록체인 생태계 역시 마찬가지이다. 이 이미지를 그대로 넣는게 아니라, 어딘가에(주로 IPFS 저장소)

저장되어 있는 이미지 링크를 블록에 데이터에 저장해준다. 그리고 그 블록에 담긴 데이터를 기반으로 우리의 판매 권한을 플랫폼(오픈씨나 업비트 NFT와 같은)에 위임해준다.

 

이런 과정을 우린 민팅(minting)이라고 한다.

그리고 이 과정에서 스마트 컨트랙트가 필요하고, 이 블록에 '적절한' 데이터를 넣어주고, 판매까지 지원해주는 과정을 스마트 컨트랙트로 하게 된다.

 

2. ERC-721 프로토콜에 대해서

2017년, 이더리움 생태계에서 최초로 NFT를 시도한 여러 프로젝트들이 나오기 시작했다.

크립토 펑크, 크립토 키티 등 인데, 이더리움 생태계에서 정해져 있던 토큰에 관련된 규약인 ERC-20만으론 부족한 부분들이 있었다. 왜냐하면 NFT는 말 그대로 각 토큰이 각자의 가치를 가진, 교환이 불가능한, Non-Fungible-Token이었기 때문이다.

 

여기까지는 그냥 어딘가에서 본 이야기들일 거다.

약간 더 나아가서, 어떤 interface들이 있는지, 그리고 각 interface들이 어떤 역할을 하는지를 소개해주고 싶다.

 

 

 

EIP-20: Token Standard

 

eips.ethereum.org

ERC-20의 token standard(interface도 있다!)

 

 

EIP-721: Non-Fungible Token Standard

 

eips.ethereum.org

ERC-721의 token standard(역시 interface가 있다)

 

3. ERC-721 톺아보기

링크에 들어가면 정말 잘 설명되어 있지만, 영어이기도 하고, 한글 자료로 만들어 놓는다면 훨씬 더 이해가 편할 것 같다.

이 규정에서 interface라는 것은 java에서 쓰는 바로 그것처럼(!) 사전에 정의된 함수를 override 해줘야함을 의미한다.

 

당연한 이야기지만, 이렇게 해야 블록에서 올려놓은 우리의 NFT 아트가 reveal되는 것을 확인할 수도 있고, 판매할 수도 있다. 만약 우리 입맛대로 그냥 구현해놓는다면 플랫폼에서 어디에서 이미지 url을 가져오고 어디에서 판매됨 처리를 해놓겠는가?

 

0. 모든 ERC-721 규악은 ERC-165 규약을 implement 해야한다. (매우 중요)

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);
}

 

사실 뭐 가져올 것도 없다. 이거 하나만 구현해주면 된다.

일반적으로 이 함수를 실행시키는 주체는 주로 플랫폼에서 이 contract가 ERC-721 프로토콜을 만족하는가?

를 확인하기 위해서 사용한다.

  • interfaceID가 0x01ffc9a7 (EIP165 interface)라면 true!
  • 이 contract를 implement한 contract의 interfaceID가 만족한다면 true!
    (참고로 ERC-721의 identifier는 0x80ac58cd이다.)

ERC-721 프로토콜 안에서 구현을 해준다면 이렇게 해주면 된다.

function supportsInterface(bytes4 interfaceID) external override view returns (bool) {
    return 
        type(ERC721).interfaceId == interfaceID || 
        type(ERC721Metadata).interfaceId == interfaceID;
}

 

type() 이라는 내장함수로 interfaceId를 접근할 수 있기 때문에(ERC721 프로토콜을 만족하는지 체크)

맘편히 이걸로 써주면 되겠다.

 

1. NFT 메타 데이터에 접근할 수 있는 ERC721Metadata interface

NFT의 사진 주소만 있으면 모든게 해결되는게 아니다.

사진 자체에 json 형식으로 asset명, asset설명, 그리고 source까지 metadata로 넣어줘야한다.

 

메타데이터 예시.json

{
    "title": "Asset Metadata",
    "type": "object",
    "properties": {
        "name": {
            "type": "string",
            "description": "Identifies the asset to which this NFT represents"
        },
        "description": {
            "type": "string",
            "description": "Describes the asset to which this NFT represents"
        },
        "image": {
            "type": "string",
            "description": "A URI pointing to a resource with mime type image/* representing the asset to which this NFT represents. Consider making any images at a width between 320 and 1080 pixels and aspect ratio between 1.91:1 and 4:5 inclusive."
        }
    }
}

 

이렇게 주소를 넣어놓는 건 ipfs라는 통신규약으로 넣어줘야한다.

처음에 사진 주소를 보여줬던 걸 기억하는가?

https://nftgames.net/wp-content/uploads/2022/03/doodlesnft.png

이런 형식의 데이터는 http 통신규약이다. http:// 이런식으로 시작하는 걸 볼 수 있다.

그렇다면 ipfs역시 ipfs://로 시작하는걸까? 정답이다!

 

ipfs와 분산데이터 설정에 대한 내용은 다음 포스트로 넘겨야 할 것 같다. 이것만 해도 한 세월이다ㅠㅠ

 

2. NFT의 원래, 현재 주인을 명시하고, 권한위임 설정을 도와주는 ERC721 interface

pragma solidity ^0.4.20;

interface ERC721 {
    // 거래를 감지하는 event.
    event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);

    // nft에 대해서 권한을 가지는 주소를 등록했음을 감지하는 event.
    event Approval(address indexed _owner, address indexed _approved, uint256 indexed _tokenId);

    // nft에 대한 모든 권한(판매나 접근 등)을 위임했음을 감지하는 event.
    event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);

    // owner 지갑 주소의 토큰의 수에 접근.
    function balanceOf(address _owner) external view returns (uint256);

    // 현재 nft의 주인 지갑 주소에 접근
    function ownerOf(uint256 _tokenId) external view returns (address);

    // nft 거래에 관한 함수 (minting X)
    function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes data) external payable;

    // nft 거래에 관한 함수 (과거 프로토콜)
    function safeTransferFrom(address _from, address _to, uint256 _tokenId) external payable;

    // nft 거래에 관한 함수 (과거 프로토콜)
    function transferFrom(address _from, address _to, uint256 _tokenId) external payable;

    // 현재 nft에 대해서 이 지갑(플랫폼 등)에 권한을 위임
    function approve(address _approved, uint256 _tokenId) external payable;

    // 현재 nft에 대해서 이 지갑(플랫폼 등)에 모든 권한을 위임
    function setApprovalForAll(address _operator, bool _approved) external;

    // 현재 nft에 대한 권한을 가진 지갑 주소를 return.
    function getApproved(uint256 _tokenId) external view returns (address);

    // 현재 nft에 대한 모든 권한을 가졌는지 여부를 return.
    function isApprovedForAll(address _owner, address _operator) external view returns (bool);
}

 

간단한 함수의 기능에 대해서 한글 주석을 달아봤다.

약간 틀리거나 잘못 생각했을 수도 있지만, 일단 내가 생각했을 때는 "내 nft asset에 관한 권리를 플랫폼에서 운영하는 지갑 계좌에 위임해주도록 관리해주는 계약서"라고 생각한다.

 

정말 하나하나 읽어볼 수록 NFT를 거래하기 위한 계약서를 플랫폼과 작성하는 것처럼 구성되어 있다. 모든 기본 조항이 작성되어 있고, 여기에서 우리가 하고 싶은 추가 기능을 넣을 수 있다. (예를 들어, 민팅이라던가...)

 

일단 이번 포스팅은 여기에서 마치겠다.

다음에는 이 interface를 구현해보는 과정과 민팅함수를 넣어보는 걸 소개해보면 좋을 것 같다.

그 전에 일단 ipfs에 대해서, 그리고 우리가 쓸 이미지를 분산 저장소에 메타데이터와 저장하는 과정을 소개해야할 것 같다.

 

그럼 이만!