5일차-[ERC721]스마트컨트랙트 NFT 발행, 전송

스마트컨트랙트 ERC721 방식으로 NFT 발행과 전송 실습을 진행하여 NFT에 대해 자세히 알아보자.

참고 : Metamask를 Remix에 연동할때 현재 Metamask가 실행한 계정을 자동으로 등록

  • 만약 변경 안 될 시? 그림처럼 연결시도

Metamask


햇갈리는 용어

  • deploy는 전개하다라는 뜻인데, 여기서 토큰을 발행한다라고 이해하자.

  • mint는 nft토큰을 발행해서 사람에게 주는 것

  • pending은 어떤 블록에 집어넣을지 고르고 있는 것.
    시간 지나면 Indexing이 뜨고 이는 블록이 거의 정해진 것.
    그 후 완료 상태는 Success


이전 시간에 smart contract배포ERC20 형식 -> 4일차 스마트컨트랙트 ERC20 실습

  • 우리가 토큰의 주인이며 마음대로 생성할 수 있고, 다른 이에게 줄 수 있다


이번 시간에 smart contract배포ERC721 형식

  • 1번째 방법은 하나의 서버가 있고 관련 데이터인 토큰들이 서버에 관련시킨 것

ERC721 방식


  • 2번재 방법은 스마트 컨트랙트 소유자가 따로, 디지털 작품 생산자가 따로 있다.
    토큰 하나에 관련 토큰들이 연결되어 있다.

ERC721 방식

ERC721 방식

그림의 Data URL이 Opensea에서 볼 수 있는 사진처럼 해당 사진 URL을 적어서 배포하는 게 보통의 NFT이다.

Besu로는 이번에 안 했지만 방식은 똑같으므로 연습해볼 것



스마트컨트랙트 NFT 발행 및 전송 실습(1)

  • 아래 코드는 기존 ERC721 방식을 완전히 따른 건 아니고 강사님이 좀 더 간단히 구성하신 거라고 한다.
    완전한 ERC721 방식을 따른 건 아니란 점을 참고.
  • Ganache와 연동



Remix 코드 작성

  • Remix 접속
  • Contract721.sol 파일 생성
  • 컴파일 버전 0.4.20 으로 하면 error발생
    ` 0.4.26`으로 하면 error는 발생 안 하고 warning 으로 해결
  • 항상 constructor인 생성자가 먼저 실행
// Contract721.sol
pragma solidity ^0.4.20;

contract Contract721 {
    string private _name; // 토큰 이름
    string private _symbol; // 토큰 기호라고 생각

    // mapping은 주머니를 만드는것
    mapping(uint256 => address) private _owners; // 주인
    mapping(address => uint256) private _balances; // 잔고
    mapping(uint256 => string) private _metadata; // NFT경우 그림 URL

    constructor(string name_, string symbol_, string metadata_, uint256 tokenId) {
        _name = name_;
        _symbol = symbol_;
        _mint(tokenId, metadata_);
    }

    function _mint(uint256 tokenId, string metadata_) internal {
        require(!_exists(tokenId), "Token already minted");
        _balances[msg.sender] += 1;
        _owners[tokenId] = msg.sender;
        _metadata[tokenId] = metadata_;
    }

    function name() public view returns (string) {
        return _name;
    }

    function symbol() public view returns (string) {
        return _symbol;
    }

    function _exists(uint256 tokenId) internal returns (bool) {
        return _owners[tokenId] != address(0);
    }

    function additionalMint(uint tokenId, string metadata) external {
        _mint(tokenId, metadata);
    }

    function balanceOf(address _owner) external view returns (uint256) {
        return _balances[_owner];
    }

    function ownerOf(uint256 _tokenId) external view returns (address) {
        address owner = _owners[_tokenId];
        return owner;
    }
    
    function getMetaData(uint256 _tokenId) external view returns (string) {
        string metadata = _metadata[_tokenId];
        return metadata;
    }

    function transferFrom(address _from, address _to, uint256 _tokenId) external payable {
        _transfer(_from, _to, _tokenId);
    }

    function _transfer(address from, address to, uint256 tokenId) internal {
        _balances[from] -= 1;
        _balances[to] += 1;
        _owners[tokenId] = to;
    }
}



스마트컨트랙트 배포

테스트를 위해 Ganache, metamask로 확인해보겠다.

  • 개념 체크 - Ganache를 킨 순간부터 가상 서버가 돌아가는 것. 키고 metamask도 접근바람


Remix에서 Deploy(배포)

Remix에서 테스트를 위해 DEPLOY

remix deploy


Ganache에서 0xbA… 이 Public Key(계정주소)를 사용해 보았고, 항상 수수료인 GAS가 든다는 점을 볼 수 있다.
처음에 Ganache 프로젝트 생성 때 719ETH로 했었는데, 718.99ETH로 바꼈다는건
스마트 컨트랙트 배포를 통해서 CONTRACT CREATION 되어서 GAS를 사용했다고 알 수 있다.


Ganache에서 테스트한 Public Key

ganache test


BLOCKS 탭에서 BLOCK 트랜잭션이 잘 시행되었는지, GAS 얼마, 언제 사용했는지 정보 확인

ganache test2


TRANSACTIONS 탭에서 자세한 정보를 알 수 있다.

  • FROM ADDRESS가 어디서 보냈는지 나타낸 주소(위의 PUBLIC KEY)
  • CREATED CONTRACT ADDRESS가 핵심인데, 스마트 컨트랙트 배포통해 생성되는 그 주소이다.

ganache test3


Remix에서 Deploy할때 컨트랙트 주소 생성되는데 바로 이것과 동일(사진 참고)

remix deploy address


Ganache에서 문제없는걸 잘 확인했고, Metamask에서도 지갑 확인

  • Metamask에선 네트워크는 Ganache로 설정
  • 계정은 가져오기를 통해 Private Key(Ganache에 있는)를 사용 -> public key였나? 헷갈리니까 둘다 ㄱ

metamask


그리고 아까 생성한 토큰을 가져와 확인(이때 Contract주소를 사용)

  • 토큰 십진수는 자리수였던 걸로 기억하는데, 이번에 배울 때
    TokenId를 적으라고 하셔서 이번엔 TokenId로 계속 적겠음
    (별루 상관은 없는 것 같긴 하다)

metamask


이렇게 가져왔으면 성공!

metamask



Remix에서 함수들 사용

  • balanceOf() - 확인하고 싶은 계정 주소를(PUBLIC KEY) 입력
    • 토큰을 소유하지 않은 계정은 잔고(balance) 0으로 출력
  • getMetaData() - 토큰을 생성(deploy)할 때 포함한 데이터를 의미
    • NFT 사용하는 것 보면 이미지 URL을 여기 작성한다.
    • tokenId는 토큰 생성 시 입력한 토큰 Id를 넣어줘야 한다.
    • tokenId는 URL에서 http://…/0 또는 http://…/1 또는 http://…/2 이런식으로 구별
  • name() - 토큰을 생성(deploy)할 때의 이름을 출력
  • ownerOf() - 해당 토큰 주인의 주소를 알려준다.
    • tokenId는 토큰 생성시 입력한 토큰 Id를 넣어줘야 한다.
  • symbol() - 토큰을 생성(deploy)할 때의 symbol(기호)을 출력


실행사진

remix test


Redix에서 토큰 추가 생성 함수

  • additionalMint() - 토큰을 추가 생성하는 함수
    • tokenId는 기존에 생성된 토큰과는 다른 Id로 할 것(2로 하겠다)
    • metadatahello world2 로 입력 후
      getMetaData함수 사용(사진)
    • Metamask의 변화도 보여줌(사진)

remix test


현재 토큰 1, 2로 두 개 있어서 0.2 ST

test


Remix에서 전송함수

  • transferFrom() - 원하는 사용자에게 원하는 토큰을 전송할 수 있음
    • from : 소유자의 계정
    • to : 원하는 사용자의 계정
    • tokenId : 원하는 토큰사용(현재 토큰 1, 2로 두 개 있음)

test


to에 적었던 계정에 토큰 가져오기

  • to에 적었던 계정인 Account 6에 접속하고,
  • 가져올 때 주소란에는 스마트컨트랙트 주소를 기입
  • 십진수에는 tokenId 를 기입 (이건 별다른 의미 없음. 원하는 데로 표기 바꿔도 됨.)
    • 0.01 ST 확인 (십진수 2로 해서 단위 바꼈을 듯)

test

test


토큰의 주인도 잘 바뀐점을 알 수 있다

test



스마트컨트랙트 NFT 발행 실습(2)

  • 테스트 넷으로 연동
  • 실습 내용은 위 방법과 유사하므로 여기선 생략을 좀 많이 하겠음. 꼭 위에 방법 먼저 숙지 바람



GIT 오픈소스 활용

  • OpenZeppelin-git 에서 release -v4.5 브랜치에서 아래 파일들 전부 가져올 것
    => Remix로 가져오기
  • 경로 수정 반드시 잘 해줘야 한다(import)

오픈소스



연동 시작

아래 view on etherscan 클릭하게 되면 ehterscan 홈페이지에서 해당 블록체인 볼수 있다.
=> 테스트넷을 사용하기 때문에 ehterscan 홈페이지 이용할 수 있는 것이다.

그리고 ERC721PresetMinterPauserAutoId.sol을 사용

Deploy는 Account1 계정, 테스트넷 네트워크를 사용중이다. 메타마스크와 연동해서 transact

testnet 활용


메타마스크 Account1 계정 지갑 확인해보면(토큰 가져오기는 생략)

  • 잔고 0으로 나오며 이는 아마도 스마트컨트랙트 생성만 해주고 NFT 토큰은 디자인 생성자 분에게 주기 위해 일부러 함수 코드를 잔고 0으로 작성했을 것이다.
    • 잔고는 0 0722_jw 에서 앞에 0이 잔고
  • mint를 통해 NFT 토큰 생성한 것을 디자인 생성자 분에게 넘겨주면 그 사람의 잔고는 0이 아닌것을 보면 일부러 이렇게 함수 코드를 작성한 거라고 생각한다.
    • 아래 테스트에서 Account2로 mint하는데 이때 잔고 1이 되는 걸 확인할 수 있다.
  • 즉, 스마트컨트랙트 생성 및 토큰 발행자는 서버의 역할을 한다고 생각한다.
  • 추가로 JW토큰을 하나 더 생성해봤는데 마찬가지로 잔고 0이었고, 이런 식으로 NFT 토큰을 주고받는다.

testnet 활용


mint함수account 2번 계정에게 보내기 위해 함수 트랜잭션 한다면?

아래처럼 성공한 걸 볼 수 있고(etherscan페이지)

etherscan


메타마스크에서도 Account2 계정 들어가서 토큰 가져와 보면 잘 얻는것을 알 수 있다.

testnet 활용


토큰의 주인도 Account2 계정이라고 잘 나타난다.

test



흐름 이해를 위한 보충 설명

컨트랙트 오너가 NFT 토큰을 만들건대 BASETOKENURI디자인 생성자분의 이미지를 URI로 넣어주는것

토큰 id는 0, 1, 2,,, 로 자동으로 붙게끔 코드가 이미 작성되어 있다.

deploy(배포) 하게 되면, 컨트랙트 오너 계정 지갑을 보면 할수 있는게 아무것도 없으므로 잔고 JW 0이다.

그 후 디자인 생성자분에게 에게 mint를 하게 되면 토큰을 줄 수 있다. 주인도 디자인 생성자가 된다.

또 다른 이에게도 토큰 mint 할 수 있다.

추가로 approve 함수를 통해 토큰 접근권한 줄 수 있다. (접근 허용 함수)

테스트넷의 경우(or 메인) 이더스캔 홈페이지에서 토큰들 확인할 수 있다.


buyer가 토큰 사고 싶을 때는? 메타마스크에서는 불가하다.
왜냐하면 remix에서 한 것처럼 safeTransferFrom 함수로 스마트 컨트랙트 사용해서 보내야 하며
함수의 from, to 에 주소도 넣고, tokenId를 파는이가 가지고 있는 토큰 Id로 해야 하고, 다른 이의 토큰아이디 사용해봤자 보내기 불가능하다.

참고로 buyer한테도 스마트컨트랙트 주소를 줘서 토큰 가져오기 하면 토큰 내용을 볼 수 있다.



besu에서 실습

besu를 제외한, 테스트넷과 Ganache에서는 테스트를 완료했다. 따라서 besu만 정상적으로 테스트가 되는지 위의 내용들을 똑같이 실습하고 마무리하겠다.



besu private network 실행

  • 네트워크를 구성(채굴 및 node(컴퓨터)간의 블록연결 => 블록체인)

besu 동작 모습



Remix에서 Deploy(배포)

  • Metamask 네트워크 besu 연결 및 Account 4 계정 사용하며 Remix에 연동
  • DEPLOY 토큰은 JW_NFT 이름으로 지정
  • 보낼 URI데이터는 그냥 test로 지정

besu 활용

besu 활용


Metamask에서 확인 위해 토큰 가져오기(컨트랙트 주소로 가져와야 한다)

  • 토큰 십진수는 자리수 입력이지만 여기선 토큰ID로 입력하겠다.

컨트랙트 주소

besu 활용

besu 활용


해당 토큰을 Account5 계정에 주기 위해 mint 한다.

mint 테스트


토큰 가져와 보면 얻은 것을 알 수 있다.

nft 확인


해당 토큰의 주인이 Address5 라는 점을 알 수 있다.

besu 활용



이때까지 실습 정리

참고 강사님 git 주소 : GIT

  1. geth 설치 => 테스트넷 연동
  2. ethereum private network => 메타마스크 연동
  3. ganache(가상머신) => 메타마스크, 리믹스 연동
  4. besu private network => 메타마스크, 리믹스 연동

  5. smart contract 배포
    => java의 getter, setter 형태로 스마트 컨트랙트에 데이터 저장 및 호출
    => ERC 20 생성 with 오픈 제플린(git 소스) + Dapp 뼈대 만들기(지갑)
    => ERC 721 생성 with 샘플 코드 and 오픈 제플린(git 소스)
  6. 생성된 ERC 721로 테스트넷에 배포하여 Token ID 확인 및 다른 사람에게 전송(NFT)



마무리

블록체인 분야

블록체인 분야 -> 네트워크 전문가, 암호학 전문가, 소프트웨어 전문가
따라서 관심이 있다면 이 중에 하나를 파고드는 게 좋치, 이 모든 걸 하는 전문가는 없다고 봐도 된다.
공부를 한다면 이런 기술을 중점으로 하기를 바란다. 블록체인을 응용하는 분야인 코인, NFT, 메타버스 등은 다음에 해도되니까

코인 - 수수료 절감 방법 공부 등등… 이 분야는 MIS(경영정보학)와 컴공 지식이 필요

Dapp - 소프트웨어 공학쪽
software productline을 수정해서 Dapp을 좀더 편하게 만들게 구성해보는 아이디어

시간이 된다면 리액트 네이티브나 리액트 등 클라이언트 기술로 UI를 구성해서 따로 지갑을 만드는 프로젝트를 해보는 것도 재밌겠다.

댓글남기기