블록체인2017.07.07 13:34

https://www.facebook.com/groups/1429149120539506/



이 글은 https://ethereum.org/token 의 발번역입니다.

참고용으로만...


이글은 비트코인과 이더리움에 대한 이해가 있다는것을 가정합니다. 이더리움으로 만들 수 있는 여러가지 유형의 애플리케이션중에 토큰베이스에 대해서 설명합니다.


우리는 디지털토큰을 만들것이다. 이더리움 생태계에서 토큰은 모든 대체가능한 상품(동전, 누적포인트, 금인증서, 게임아이템등)을 나타낼수 있다. 모든 토큰들은 표준방식으로 몇가지 기본기능을 구현하기 때문에 이더리움 지갑, 다른 클라이언트 또는 동일한 표준을 사용하는 계약과 바로 호환될 수 있습니다.


아래 코드를 복붙해서 바로 사용 가능하다


pragma solidity ^0.4.8;

contract tokenRecipient { function receiveApproval(address _from, uint256 _value, address _token, bytes _extraData); }


contract MyToken {

    /* Public variables of the token */

    string public standard = 'Token 0.1';

    string public name;

    string public symbol;

    uint8 public decimals;

    uint256 public totalSupply;


    /* This creates an array with all balances */

    mapping (address => uint256) public balanceOf;

    mapping (address => mapping (address => uint256)) public allowance;


    /* This generates a public event on the blockchain that will notify clients */

    event Transfer(address indexed from, address indexed to, uint256 value);


    /* This notifies clients about the amount burnt */

    event Burn(address indexed from, uint256 value);


    /* Initializes contract with initial supply tokens to the creator of the contract */

    function MyToken(

        uint256 initialSupply,

        string tokenName,

        uint8 decimalUnits,

        string tokenSymbol

        ) {

        balanceOf[msg.sender] = initialSupply;              // Give the creator all initial tokens

        totalSupply = initialSupply;                        // Update total supply

        name = tokenName;                                   // Set the name for display purposes

        symbol = tokenSymbol;                               // Set the symbol for display purposes

        decimals = decimalUnits;                            // Amount of decimals for display purposes

    }


    /* Send coins */

    function transfer(address _to, uint256 _value) {

        if (_to == 0x0) throw;                               // Prevent transfer to 0x0 address. Use burn() instead

        if (balanceOf[msg.sender] < _value) throw;           // Check if the sender has enough

        if (balanceOf[_to] + _value < balanceOf[_to]) throw; // Check for overflows

        balanceOf[msg.sender] -= _value;                     // Subtract from the sender

        balanceOf[_to] += _value;                            // Add the same to the recipient

        Transfer(msg.sender, _to, _value);                   // Notify anyone listening that this transfer took place

    }


    /* Allow another contract to spend some tokens in your behalf */

    function approve(address _spender, uint256 _value)

        returns (bool success) {

        allowance[msg.sender][_spender] = _value;

        return true;

    }


    /* Approve and then communicate the approved contract in a single tx */

    function approveAndCall(address _spender, uint256 _value, bytes _extraData)

        returns (bool success) {

        tokenRecipient spender = tokenRecipient(_spender);

        if (approve(_spender, _value)) {

            spender.receiveApproval(msg.sender, _value, this, _extraData);

            return true;

        }

    }        


    /* A contract attempts to get the coins */

    function transferFrom(address _from, address _to, uint256 _value) returns (bool success) {

        if (_to == 0x0) throw;                                // Prevent transfer to 0x0 address. Use burn() instead

        if (balanceOf[_from] < _value) throw;                 // Check if the sender has enough

        if (balanceOf[_to] + _value < balanceOf[_to]) throw;  // Check for overflows

        if (_value > allowance[_from][msg.sender]) throw;     // Check allowance

        balanceOf[_from] -= _value;                           // Subtract from the sender

        balanceOf[_to] += _value;                             // Add the same to the recipient

        allowance[_from][msg.sender] -= _value;

        Transfer(_from, _to, _value);

        return true;

    }


    function burn(uint256 _value) returns (bool success) {

        if (balanceOf[msg.sender] < _value) throw;            // Check if the sender has enough

        balanceOf[msg.sender] -= _value;                      // Subtract from the sender

        totalSupply -= _value;                                // Updates totalSupply

        Burn(msg.sender, _value);

        return true;

    }


    function burnFrom(address _from, uint256 _value) returns (bool success) {

        if (balanceOf[_from] < _value) throw;                // Check if the sender has enough

        if (_value > allowance[_from][msg.sender]) throw;    // Check allowance

        balanceOf[_from] -= _value;                          // Subtract from the sender

        totalSupply -= _value;                               // Updates totalSupply

        Burn(_from, _value);

        return true;

    }

}


토큰 컨트랙트는 상당히 복잡하다. 다음은 토큰을 구현하기 위한 최소의 코드이다.


contract MyToken {

    /* This creates an array with all balances */

    mapping (address => uint256) public balanceOf;


    /* Initializes contract with initial supply tokens to the creator of the contract */

    function MyToken(

        uint256 initialSupply

        ) {

        balanceOf[msg.sender] = initialSupply;              // Give the creator all initial tokens

    }


    /* Send coins */

    function transfer(address _to, uint256 _value) {

        if (balanceOf[msg.sender] < _value) throw;           // Check if the sender has enough

        if (balanceOf[_to] + _value < balanceOf[_to]) throw; // Check for overflows

        balanceOf[msg.sender] -= _value;                     // Subtract from the sender

        balanceOf[_to] += _value;                            // Add the same to the recipient

    }

}


코드를 이해해 보자



지갑앱을 실행 > Contract탭 > Deploy New Contract 선택

Solidity Contract Source code 입력필드에 아래 코드 입력


contract MyToken {

    /* This creates an array with all balances */

    mapping (address => uint256) public balanceOf;

}


매핑이란 주소를 잔액과 연결하는 연관 배열을 의미한다. 주소는 16진수 이더리움 포맷이며 잔액은 0에서 115*1027 범위의 정수이다 얼마나 큰수인지 상상이 안간다면 네가 계획산 전체 토큰수보다 많을거다. public 키워드는 블록체인에서 어디서든 접근 가능하게 한다. 즉 모든 잔액이 공개되어야 한다(클라이언트가 표시해야 하는 경우).


컨트랙트를 발행 했다면 효과가 있지만 유용하지는 않다. 즉 토큰의 잔액을 주소로 조회할 수 있는 컨트랙트가 될수 있으나 토큰 하나를 만들지 않았기 때문에 모두가 0을 반환한다. 그래서 시작할때 몇개의 토큰을 만들것이다. mapping... 바로 아래 코드를 추가하라.


contract MyToken {

    /* This creates an array with all balances */

    mapping (address => uint256) public balanceOf;

    

    function MyToken() {

        balanceOf[msg.sender] = 21000000;

    }

}


function MyToken과 contract MyToken이 동일한것을 눈여겨 보아야 한다. 하나를 변경하면 나머지 하나도 변경해야 한다. 생성자 역할을 하며 네트워크에 컨트랙트가 업로드될때 한번만 호출된다. 이 함수는 컨트랙트를 전개한 사용자인 msg.sender의 잔액을 2100만 잔고로 설정한다.

이 값은 이렇게 고정된 수가 아니라 파라메터로 초기화를 하는것이 좋다.


function MyToken(uint256 initialSupply) {

    balanceOf[msg.sender] = initialSupply;

}


컨트랙트 옆에 오른쪽에 보면 드롭다운에서 "My Token"을 선택하면 Constructor parameters를 볼수 있다. 변경가능한 파라메터로 같은코드를 재사용하여 변수를 변경할 수 있다.


지금은 토큰의 잔액을 만든 계약을 가지고 있지만 아무런 변화가 없기 때문에 이제 이동에 대해 구현하겠다.


/* Send coins */

function transfer(address _to, uint256 _value) {

    /* Add and subtract new balances */

    balanceOf[msg.sender] -= _value;

    balanceOf[_to] += _value;

}


이것은 매우 직관적인 기능이다.  수신자와 값을 파라메터로 갖고 호출될때마다 해당 값을 잔액에서 차감하고 수신자의 잔액은 증가시킨다. 여기에는 명백한 문제가 있다. 잔액이 부족할 경우 어떤일이 일어날까? 이 계약에 대해 부체를 처리할것이 아니기 때문에 빠르게 확인하고 송금자의 자금이 충분치 않으면 계약 실행이 중지된다. 또한 오버플로가 있는지 확인하여 0이 되게 하는 너무 큰숫자를 피한다.


계약의 실행을 중간에 정지하려면 return 이나 throw를 호출한다. 전자는 가스비용은 적은 반면 계약의 실행에 의한 변경사항이 적용되면서 더 머리가 아파질수 있다. 반면에 throw는 모든 계약실행을 취소하고 모든 변경을 원상복구하고 가스비용으로 사용된 이더를 잃게된다. 하지만 지갑이 throw를 예측하고 경고를 함으로써 더이상 이더를 소모하는것을 막아준다.


 이 글 작성 이후 2017년 8월 현재 throw는 deprecated되었다. revert(), require(), assert()의 사용이 권장된다.


function transfer(address _to, uint256 _value) {

    /* Check if sender has balance and for overflows */

    if (balanceOf[msg.sender] < _value || balanceOf[_to] + _value < balanceOf[_to])

        throw;


    /* Add and subtract new balances */

    balanceOf[msg.sender] -= _value;

    balanceOf[_to] += _value;

}


이제 남은것은 계약에 대한 기본정보에 대한 것이다. 이 정보는 추후 토큰의 등록으로 조작이 가능하겠지만 우리는 계약에 직접 추가할것이다


string public name;

string public symbol;

uint8 public decimals;


그리고 생성자 함수를 시작할 때 업데이트한다.


/* Initializes contract with initial supply tokens to the creator of the contract */

function MyToken(uint256 initialSupply, string tokenName, uint8 decimalUnits, string tokenSymbol) {

    balanceOf[msg.sender] = initialSupply;              // Give the creator all initial tokens

    name = tokenName;                                   // Set the name for display purposes

    symbol = tokenSymbol;                               // Set the symbol for display purposes

    decimals = decimalUnits;                            // Amount of decimals for display purposes

}


마지막으로 Events로 불리는 것이 필요하다. 이더이움 지갑같은 클라이언트가 계약에서 일어나는 활동을 추적할수 있도록 도와준다. Events는 대문자로 시작해야 하고 다음과 같이 이벤트를 선언하라.


event Transfer(address indexed from, address indexed to, uint256 value);


이제 transfer 함수내에서 호출해주면 된다.


    /* Notify anyone listening that this transfer took place */

    Transfer(msg.sender, _to, _value);


자 이제 토근이 준비가 다 되었다.



배포방법


이더리움지값을 열어서 컨트랙트탭의 "deploy new contract"를 선택하라.

Solidity source filed에 위에서 작성한 소스코드를 복사 및 붙여넣기를 하라. 코드 컴파일이 문제가 없다면 "pick a contract"를 볼수 있을것이고 "MyToken"를 드롭박스에서 선택하라. 그러면 파라메터를 입력할 수 있는 컬럼이 보일것이다.  원하는대로 조정할 수 있지만 튜터리얼의 목적에 따라 공급 매개변수 10000개 기호는 %, 소수자릿수 2개를 선택한다. 아래 처럼.

페이지 끝으로 스크롤하면 해당 계약의 계산 비용이 대략적으로 표시되며 이더비용을 지불할 의사가 있는 요금을 선택할 수 있다. 쓰지 않는 초과 이더는 돌려 보내질 것이므로 원한다면 기본 설정을 그대로 둘 수 있다. "배포"를 누르고 계정 암호를 입력 한 후 거래가 시작될 때까지 몇 초 기다린다.


그러면  첫페이지로 이동하여 거래 확인을 기다리는 것을 볼 수 있다. Etherbase(당신의 주계정)을 클릭하면 1분이 지나면 방금 만든 공유의 100%를 계정에 표시한다. 일부를 친구에게 보내려면 친구의 주소를 복사하여 "to"필드에 집어넣고 "send"를 눌러라.


친구에게 보내면 아직 지갑에는 아무것도 표시되지 않는다. 지갑이 알고 있는 토큰만 추적하기 때문에 지갑을 수동으로 추가해야 한다. 이제 '컨트랙트'탭으로 이동하면 새로 생성 된 컨트랙트 링크가 표시된다. 해당 페이지로 이동하려면 클릭한다. 이것은 매우 간단한 컨트랙트 페이지이므로 여기서 할 일이 많지 않다. "주소 복사"를 클릭하고 컨트랙트 주소를 텍스트 편집기에 붙여 넣는다.

확인하고자하는 토큰을 추가하려면 컨트랙트 페이지로 이동 한 다음 "Watch Token"을 클릭하라. 팝업 창이 나타나면 계약서 주소 만 붙여넣는다. 토큰 이름, 기호 및 십진수는 자동으로 채워 져야하지만 그렇지 않은 경우 원하는 것을 넣을 수 있다 (지갑에 표시되는 방식에만 영향을 미침). 이렇게하면 토큰의 잔액이 자동으로 표시되어 다른 사람에게 보낼 수 있다.


이제 당신만의 암호화된 토큰을 가지게 되었다. 토큰은 로컬커뮤니티내에서 가치교환에 유용하게 사용될수 있고, 근무시간 또는 다른 로열티프로그램을 추적할 수 있다. 하지만 통화를 만들어서 본질적인 가치를 만들 수 있는가?






Posted by 삼스

티스토리 툴바