ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 나만보는 블록체인 개념 (3) 토큰 컨트랙트 -1
    BlockChain/Technology 2021. 7. 27. 15:19

    오늘의 주제

    1. 토큰

    2. 토큰의 컨트랙트

     

     


    1. 토큰

     

    ERC-20 

    Ethereum Request for Comment 20의 약자. EIPs에서 관리하는 공식 프로토콜이며 이더리움 블록체인 네트워크에서 정한 표준 토큰 스펙이다. ERC-20 토큰은 이더리움과 교환 가능하며 이더리움 지갑으로 전송이 가능하다.

    코인과 토큰의 차이

     

    코인 : 블록체인 네트워크(메인넷)을 구축하여 독립적인 블록체인 생태계 구성이 가능하다. 

    토큰 : 특정 블록체인을 기반으로 한 dApp*분산화 애플리케이션에 사용되는 암호화폐이다.

     

    토큰은 스마트 계약을 통해 생성된다. 스마트 계약은 불가역적으로 전개되는 기능을 통해 중앙관리가 배제된 서비스를 구현한다. 이더리움 블록체인 플랫폼을 활용해 토큰을 발행하여 자신의 비즈니스를 구현하고, 자금모집 및 거래체계, 플랫폼 사용료를 이더리움으로 지불하는 체계를 만들 수 있다. ERC-20 토큰은 이더리움과 교환이 가능하며 이더리움 지갑으로 전송 또한 가능하다.

     

    하지만 이는 다소 아쉬운 설명이다.

    먼저 Transaction의 구성요소을 본다.

     Transaction {
    nonce: 해당 계좌에서 발생된 Transaction의 수
    from: 발신자 주소
    to: 수신자 주소
    value: 수신자에게 보내는 이더 수량
    data: 가변 길이의 바이너리 데이터, smart contract가 실림
    v, r, s: ECDSA 서명 구성 요소 }

     

    이더리움을 예로들면 코인은 이더이고, 토큰은 스마트 컨트랙트에서 ERC-20 표준으로 발행한 암호화폐이다.

    이더는 계좌의 balance에서 움직이지만 토큰은 컨트랙트 내부에만 존재하며, 이더와 독립적인 데이터베이스를 갖는다.

    이더는 EOA와 연결되 잔액정보를 갖는 반면, 토큰은 CA와 연결되 잔액정보를 갖게 된다. CA에서도 이더의 잔액정보 연결이 가능하다. 

     

    조금 더 직관적인 비교를 위해 일반적인 송금의 트랜잭션과 계약 이행 트랜잭션을 설명한다. 

     

    Transfer Tansaction

    var transaction = { 
    nonce: nonce, 
    gasPrice: gasPrice, 
    gasLimit: gasLimit, 
    to: 'toAddress', 
    value: value, 
    data: '0x0' };

    일반적인 송금에서 data에 아무런 값을 넣어주지 않아도 된다. 이더는 value에 담긴다.

     

    Contract Execution Transaction

    var transaction = { 
    nonce: nonce, 
    gasPrice: gasPrice, 
    gasLimit: gasLimit, 
    to: 'conractAddress', 
    value: '0x0', 
    data: '0x26f0e4d3' };

    컨트랙트의 경우 받는 주소가 EA가 아닌 CA이고, data에 해당 내용이 들어간다. 이곳에 토큰의 데이터가 담긴다.

     

    코인과 토큰의 차이를 알았으니 ERC-20의 contract를 들여다보자.

     


    2. 토큰의 컨트랙트

    오늘 이해해볼 컨트랙트의 내용은 이와 같다. 하나씩 뜯어 이해해 본다.

    pragma solidity >=0.4.22 <0.6.0;
    
    contract ERC20Basic {
    
        string public constant name = "name";
        string public constant symbol = "ABC";
        uint8 public constant decimals = 0;  
    
        event Approval(address indexed tokenOwner, address indexed spender, uint tokens);
        event Transfer(address indexed from, address indexed to, uint tokens);
    
        mapping(address => uint256) balances;
    
        mapping(address => mapping (address => uint256)) allowed;
        
        uint256 totalSupply_;
    
        using SafeMath for uint256;
    
       constructor(uint256 total) public {  
    		totalSupply_ = total;
    		balances[msg.sender] = totalSupply_;
        }  
    
        function totalSupply() public view returns (uint256) {
    	return totalSupply_;
        }
        
        function balanceOf(address tokenOwner) public view returns (uint) {
            return balances[tokenOwner];
        }
    
        function transfer(address receiver, uint numTokens) public returns (bool) {
            require(numTokens <= balances[msg.sender]);
            balances[msg.sender] = balances[msg.sender].sub(numTokens);
            balances[receiver] = balances[receiver].add(numTokens);
            emit Transfer(msg.sender, receiver, numTokens);
            return true;
        }
    
        function approve(address delegate, uint numTokens) public returns (bool) {
            allowed[msg.sender][delegate] = numTokens;
            emit Approval(msg.sender, delegate, numTokens);
            return true;
        }
    
        function allowance(address owner, address delegate) public view returns (uint) {
            return allowed[owner][delegate];
        }
    
        function transferFrom(address owner, address buyer, uint numTokens) public returns (bool) {
            require(numTokens <= balances[owner]);    
            require(numTokens <= allowed[owner][msg.sender]);
        
            balances[owner] = balances[owner].sub(numTokens);
            allowed[owner][msg.sender] = allowed[owner][msg.sender].sub(numTokens);
            balances[buyer] = balances[buyer].add(numTokens);
            emit Transfer(owner, buyer, numTokens);
            return true;
        }
    }
    
    library SafeMath { 
        function sub(uint256 a, uint256 b) internal pure returns (uint256) {
          assert(b <= a);
          return a - b;
        }
        
        function add(uint256 a, uint256 b) internal pure returns (uint256) {
          uint256 c = a + b;
          assert(c >= a);
          return c;
        }
    }

     


    • pragma solidity + 버전정보 → 솔리디티는 버전업이 활발하게 되고 있으며, 배포된 시점의 버전의 문법과 호환되기 때문에 버전 정보를 명시해준다.
    • contract ERC20Basic → 컨트랙트의 이름
    • string public constant name → 발행할 토큰의 이름
    • string public constant symbol → 발행할 토큰의 심볼
    • uint8 public constant decimals → 토큰에서 허용할 소수점 자릿수 
    pragma solidity >=0.4.22 <0.6.0;
    
    contract ERC20Basic {
    
        string public constant name = "name";
        string public constant symbol = "ABC";
        uint8 public constant decimals = 0;

     


    • event → 어플리케이션에서 쓸 때 컨트랙트에서 무슨 일이 일어나고 있는지 추적하고 알려준다.
     event Approval(address indexed tokenOwner, address indexed spender, uint tokens);
     event Transfer(address indexed from, address indexed to, uint tokens);

    • mapping(address => uint256) balances → balances라는 이름의 어카운트에 address(Key)-unit256(value)타입의 데이터들이 매핑된다. 매핑은 Key-value 형태의 쌍으로 저장되고 제공된 Key를 가지고 value를 얻어낼 수 있다.
    • uint256 totalSupply_ → 전체 발행량을 담을 공간 선언
    • constructor → 스마트 컨트랙트를 배포할 때 최초 한번만 실행된다. totalSupply_에는 total(총 발행량)이 담기고, balances[msg.sender]에는 컨트랙트를 만든 사람의 계좌에 발행한 토큰이 들어간다.
        mapping(address => uint256) balances;
    
        mapping(address => mapping (address => uint256)) allowed;
        
        uint256 totalSupply_;
    
        using SafeMath for uint256;
    
       constructor(uint256 total) public {  
    		totalSupply_ = total;
    		balances[msg.sender] = totalSupply_;
        }

     


    선언된 함수들의 목록

    • function totalSupply() → 총 토큰 발행량을 조회한다

          - view는 트랜잭션을 실행시키지 않고, 조회할 수 있게 한다.

    • function balanceOf → 토큰 소유자의 토큰량을 조회한다

          - balances[ tokenOwner ] : tokenOwner의 주소가 Key로 입력되면 value인 그 주소의 토큰량이 반환된다.

    • function transfer( address receiver, unit numTokens ) -> 수신자에게 토큰을 보낸다

          - require은 if문과 같이 참일 경우에만 실행이 가능하도록 조건을 걸어준다. 

            보내려는 numTokens의 양이 msg.sender의 토큰량보다 적어야만 transfer함수가 실행된다.

          - emit은 Transfer 이벤트를 발생시켜 변경된 토큰량들이 컨트랙트에 기록되게 한다.

     function totalSupply() public view returns (uint256) {
    	return totalSupply_;
        }
        
    function balanceOf(address tokenOwner) public view returns (uint) {
        return balances[tokenOwner];
        }
    
    function transfer(address receiver, uint numTokens) public returns (bool) {
        require(numTokens <= balances[msg.sender]);
        balances[msg.sender] = balances[msg.sender].sub(numTokens);
        balances[receiver] = balances[receiver].add(numTokens);
        emit Transfer(msg.sender, receiver, numTokens);
        return true;
        }

     

     

    댓글

Designed by Tistory.