# 누구나 '복붙하여' 만들 수 있는 이더리움 ERC20 코인/토큰 실전 개발
<p></p>
오랜만의 포스팅이네요. 드디어 번외편이자 본 연재의 백미인 *'복붙하여'* 만들기 편입니다. 오늘 내용은 사실 다음의 1편부터 3편까지를 다시 한 번 정리했다고 볼 수 있습니다.
- [누구나 만들 수 있는 이더리움 ERC20 코인/토큰 실전 개발 (1편)](https://steemit.com/kr-dev/@nida-io/2oduk2-erc20-1)
- [누구나 만들 수 있는 이더리움 ERC20 코인/토큰 실전 개발 (2편)](https://steemit.com/kr-dev/@nida-io/erc20-2)
- [누구나 만들 수 있는 이더리움 ERC20 코인/토큰 실전 개발 (3편)](https://steemit.com/kr-dev/@nida-io/erc20-3)
<p></p>
*'머리 아프고 복잡한 건 나중에, 일단 쉽게 따라해보고 싶다!'* 라고 생각하는 분들은 이번 번외편만 보셔도 됩니다.
한번에 복붙하기 좋게 여러 개의 .sol 파일을 하나로 합쳐두었으므로 다음 코드를 그냥 통째로 복사하여 사용하시면 됩니다.
## 다음 소스를 복붙하세요.
<p></p>
```
pragma solidity ^0.4.18;
/**
* @title ERC20Basic
* @dev Simpler version of ERC20 interface
* @dev see https://github.com/ethereum/EIPs/issues/179
*/
contract ERC20Basic {
function totalSupply() public view returns (uint256);
function balanceOf(address who) public view returns (uint256);
function transfer(address to, uint256 value) public returns (bool);
event Transfer(address indexed from, address indexed to, uint256 value);
}
/**
* @title SafeMath
* @dev Math operations with safety checks that throw on error
*/
library SafeMath {
/**
* @dev Multiplies two numbers, throws on overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
uint256 c = a * b;
assert(c / a == b);
return c;
}
/**
* @dev Integer division of two numbers, truncating the quotient.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
// assert(b > 0); // Solidity automatically throws when dividing by 0
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
/**
* @dev Subtracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend).
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
assert(b <= a);
return a - b;
}
/**
* @dev Adds two numbers, throws on overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
assert(c >= a);
return c;
}
}
/**
* @title Basic token
* @dev Basic version of StandardToken, with no allowances.
*/
contract BasicToken is ERC20Basic {
using SafeMath for uint256;
mapping(address => uint256) balances;
uint256 totalSupply_;
/**
* @dev total number of tokens in existence
*/
function totalSupply() public view returns (uint256) {
return totalSupply_;
}
/**
* @dev transfer token for a specified address
* @param _to The address to transfer to.
* @param _value The amount to be transferred.
*/
function transfer(address _to, uint256 _value) public returns (bool) {
require(_to != address(0));
require(_value <= balances[msg.sender]);
// SafeMath.sub will throw if there is not enough balance.
balances[msg.sender] = balances[msg.sender].sub(_value);
balances[_to] = balances[_to].add(_value);
emit Transfer(msg.sender, _to, _value);
return true;
}
/**
* @dev Gets the balance of the specified address.
* @param _owner The address to query the the balance of.
* @return An uint256 representing the amount owned by the passed address.
*/
function balanceOf(address _owner) public view returns (uint256 balance) {
return balances[_owner];
}
}
/**
* @title ERC20 interface
* @dev see https://github.com/ethereum/EIPs/issues/20
*/
contract ERC20 is ERC20Basic {
function allowance(address owner, address spender) public view returns (uint256);
function transferFrom(address from, address to, uint256 value) public returns (bool);
function approve(address spender, uint256 value) public returns (bool);
event Approval(address indexed owner, address indexed spender, uint256 value);
}
/**
* @title Standard ERC20 token
*
* @dev Implementation of the basic standard token.
* @dev https://github.com/ethereum/EIPs/issues/20
* @dev Based on code by FirstBlood: https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol
*/
contract StandardToken is ERC20, BasicToken {
mapping (address => mapping (address => uint256)) internal allowed;
/**
* @dev Transfer tokens from one address to another
* @param _from address The address which you want to send tokens from
* @param _to address The address which you want to transfer to
* @param _value uint256 the amount of tokens to be transferred
*/
function transferFrom(address _from, address _to, uint256 _value) public returns (bool) {
require(_to != address(0));
require(_value <= balances[_from]);
require(_value <= allowed[_from][msg.sender]);
balances[_from] = balances[_from].sub(_value);
balances[_to] = balances[_to].add(_value);
allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value);
emit Transfer(_from, _to, _value);
return true;
}
/**
* @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender.
*
* Beware that changing an allowance with this method brings the risk that someone may use both the old
* and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this
* race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
* @param _spender The address which will spend the funds.
* @param _value The amount of tokens to be spent.
*/
function approve(address _spender, uint256 _value) public returns (bool) {
allowed[msg.sender][_spender] = _value;
emit Approval(msg.sender, _spender, _value);
return true;
}
/**
* @dev Function to check the amount of tokens that an owner allowed to a spender.
* @param _owner address The address which owns the funds.
* @param _spender address The address which will spend the funds.
* @return A uint256 specifying the amount of tokens still available for the spender.
*/
function allowance(address _owner, address _spender) public view returns (uint256) {
return allowed[_owner][_spender];
}
/**
* @dev Increase the amount of tokens that an owner allowed to a spender.
*
* approve should be called when allowed[_spender] == 0. To increment
* allowed value is better to use this function to avoid 2 calls (and wait until
* the first transaction is mined)
* From MonolithDAO Token.sol
* @param _spender The address which will spend the funds.
* @param _addedValue The amount of tokens to increase the allowance by.
*/
function increaseApproval(address _spender, uint _addedValue) public returns (bool) {
allowed[msg.sender][_spender] = allowed[msg.sender][_spender].add(_addedValue);
emit Approval(msg.sender, _spender, allowed[msg.sender][_spender]);
return true;
}
/**
* @dev Decrease the amount of tokens that an owner allowed to a spender.
*
* approve should be called when allowed[_spender] == 0. To decrement
* allowed value is better to use this function to avoid 2 calls (and wait until
* the first transaction is mined)
* From MonolithDAO Token.sol
* @param _spender The address which will spend the funds.
* @param _subtractedValue The amount of tokens to decrease the allowance by.
*/
function decreaseApproval(address _spender, uint _subtractedValue) public returns (bool) {
uint oldValue = allowed[msg.sender][_spender];
if (_subtractedValue > oldValue) {
allowed[msg.sender][_spender] = 0;
} else {
allowed[msg.sender][_spender] = oldValue.sub(_subtractedValue);
}
emit Approval(msg.sender, _spender, allowed[msg.sender][_spender]);
return true;
}
}
/**
* @title SimpleToken
* @dev Very simple ERC20 Token example, where all tokens are pre-assigned to the creator.
* Note they can later distribute these tokens as they wish using `transfer` and other
* `StandardToken` functions.
*/
contract SimpleToken is StandardToken {
string public constant name = "TaeKimCoin"; // solium-disable-line uppercase
string public constant symbol = "TKC"; // solium-disable-line uppercase
uint8 public constant decimals = 18; // solium-disable-line uppercase
uint256 public constant INITIAL_SUPPLY = 20180326 * (10 ** uint256(decimals));
/**
* @dev Constructor that gives msg.sender all of existing tokens.
*/
function SimpleToken() public {
totalSupply_ = INITIAL_SUPPLY;
balances[msg.sender] = INITIAL_SUPPLY;
emit Transfer(0x0, msg.sender, INITIAL_SUPPLY);
}
}
```
<p></p>
###### ( 참고 : zeppelin 소스에서 수정된 내용이 있습니다. 솔리디티 컴파일러 버전 [0.4.21 업그레이드의 내용](https://github.com/ethereum/solidity/releases/tag/v0.4.21) 에 따라 이벤트 함수를 호출할 때 앞에 `emit` 을 붙여주도록 추가하였습니다. zeppelin 소스에도 아마 곧 업데이트 될 것입니다. )
<p></p>
## 여기서 잠깐~! 수정하고 가실게요.
<p></p>
제가 이전 포스팅에서 **3군데는 수정**하여 사용해야 한다고 말씀드렸죠?
해당 부분에 대해 설명드리겠습니다.
- `string public constant name = "TaeKimCoin";`
- `name` 변수에 저장되는 값이 코인의 이름이 됩니다. zeppelin 의 원래 소스는 `"SimpleToken"` 이었는데 제 이름의 코인으로 변경하여 보았습니다.
- `string public constant symbol = "TKC"; `
- ERC20 토큰은 기본적으로 심볼을 가지는데요, 원하는 심볼 문자를 `symbol` 변수에 저장하시면 됩니다. 꼭 세 글자가 아니어도 됩니다.
- 심볼에 이모지(emoji) 를 저장하는 경우를 본 적 있는데요, 그렇다면 아마 한글도 될 겁니다. 또한 `name` 값도 한글로 지정해도 되지 않을까 싶네요. (해보진 않았습니다).
- `uint256 public constant INITIAL_SUPPLY = 20180326 * (10 ** uint256(decimals));`
- `INITIAL_SUPPLY` 는 최초에 발행할 코인의 개수입니다. 그리고 이 코인은 코인을 만든 주인에게 할당됩니다.
- 오늘 날짜에 맞춰 20180326 개의 TaeKimCoin 을 만들어 보았습니다.
- `(10 ** uint256(decimals))` 는 소수점 이하 자리수에 해당하는 부분입니다. 무시하셔도 됩니다.
<p></p>
딱 이 세 부분만을 용도에 맞게 수정해서 사용하시면 됩니다.
자! 이제 실제로 코인을 배포해보겠습니다.
## Ethereum Wallet 으로 코인 배포하기
<p></p>
실제 개발한다면 *remix* 에디터 혹은 *geth* 콘솔에서 코드를 배포하게 되실 겁니다. 하지만 오늘은 최대한 쉬운 방법인 *Ethereum Wallet* 에서 코드를 배포하는 방법을 보여드리겠습니다.
### 1. 신규 컨트랙트 설치
<p></p>
이더리움 월렛에 접속하면 우측 상단에 *'컨트랙트'* 메뉴가 있습니다. 해당 메뉴를 누르시면 화면 가장 윗 부분에 *'신규 컨트랙트 설치'* 버튼이 있습니다. 이 버튼을 누르세요.

<p></p>
### 2. 컨트랙트 설치하기 (상)
<p></p>
버튼을 눌러서 *'컨트랙트 설치하기'* 화면으로 들어오면 맨 위 *'송신처'* 에서 컨트랙트를 만들 계정을 고릅니다.
- 이 때 **여기서 고르는 계정이 코인의 주인이 됩니다**!
- 컨트랙트를 만들기 위해서는 소액이더라도 **이더(ETHER)** 가 있어야 합니다.
<p></p>
*'금액 '* 부분은 그대로 0 으로 두시면 됩니다. 그 후, *'솔리더티 컨트랙트 소스 코드'* 부분에 위의 코드 전부를 복사해서 덮어 씌웁니다.
코드를 붙여 넣고 잠시 기다리면 우측에 *'설치할 컨트랙트를 선택하세요.'* 항목이 생깁니다. 소스 내의 컨트랙트가 여러 개이기 때문에 그 중에 배포를 원하는 컨트랙트를 골라야 합니다. 우리의 경우에는 *'Simple Token'* 을 선택하시면 됩니다.

<p></p>
### 3. 컨트랙트 설치하기 (하)
<p></p>
이제 아래 쪽에 컨트랙트를 만드는데 필요한 예상 수수료가 계산되어 보입니다. 게이지를 좌우측으로 움직이면서 수수료를 조정할 수 있는데요, ~~이론적으로는~~ 비쌀 수록 빨리 처리됩니다. 저는 보통 1분 이내에 처리되는 수수료 중에 가장 저렴한 것으로 선택하는 편입니다.

<p></p>
저의 경우는 약 0.00067 이더 정도의 수수료가 든다고 나오네요. 최근 1 이더가 60만원 정도 하기 때문에 계산해보면 수수료가 400원 정도가 됩니다.
400원을 들여서 나만의 코인을 만듭니다. 과감하게 *'설치'* 를 눌러보세요! Go Go~!
### 4. Create Contract!
<p></p>
*'설치'* 를 누르면 콘트랙트를 생성하는 최종 창이 뜨면에 비밀번호를 넣으라고 합니다. 여기에 이더리움 계정의 비밀번호를 넣으시면 됩니다.

<p></p>
비밀번호를 넣고 *'SEND TRANSACTION'* 까지 모두 실행하고 나면 이제 블록이 만들어지기를 기다리시면 됩니다.
이 때, 아까 수수료 설정할 때 보였던 기준인 *'보통 1분 이내'* 라는 말에 속지 마세요. p2p 노드가 어떻게 연결되었느냐에 따라 빨리 처리될 수도 있고 오래 걸릴 수도 있습니다. 그런데 제 경험상 __오래 걸릴 때는 게이지를 최대한 *'높음'* 으로 올려도 오래 걸립니다__. 그냥 저가로 걸어두고 맘 편히 기다리시는게 낫습니다. 저는 이래저래 테스트해보다가 몇 만원 날렸습니다. ㅠ.ㅠ
만들어진 후 다시 *'컨트랙트'* 메뉴로 들어가면 *'주문형 콘트랙트'* 와 *'주문형 토큰'* 에 항목에 나의 코인이 보일 것입니다.

<p></p>
제가 참여하고 있는 KStarCoin 프로젝트(실제 상용화된 코인, 프리세일 중)와 새로 생성한 TaeKimCoin 이 보이네요 ^.^! 어디 TaeKimCoin 을 한번 눌러볼까요?

<p></p>
코인의 상세 내용을 보면 위와 같이 *'토큰 콘트랙트 주소'* 를 알 수 있습니다. 이 주소를 복사해서, 이더스캔에서 검색해보겠습니다. 다음 링크를 눌러보세요.
- https://etherscan.io/address/0xcf8778c354fc10f5adb05d2b959121fd5270eb04
<p></p>
어때요? 이더스캔에서도 아주 잘 검색되죠?
오늘 연재 내용은 여기까지입니다. 그런데 기왕 토큰을 만든 김에 써봐야 하지 않겠어요? 그래서 다음 이벤트를 준비해보았습니다.
## 전격 TaeKimCoin 에어드랍 이벤트! 10,000 TKC 를 받아가세요!
<p></p>
댓글로 이더리움 계좌 주소를 남겨주시는 분들께 10,000 TKC 를 보내드립니다. 연재 내용이 이어지면서 **이 코인으로 실습을 할 수 있게 될테니 받아두시면 좋을 겁니다**. 제가 이더리움 가스비를 소모하면서 보내드리는 거니, 받는 분들은 꼭 열심히 공부하셔야 합니다. ~~차후 실습에 따라 오시는지 트랜잭션 보면 다 나와요!~~
- (팁 1) 계좌를 오픈하는 것이 꺼려지는 분은 새로운 계좌를 하나 만들어서 요청하시면 될 것 같습니다.
- (팁 2) **토큰을 거래소 지갑 주소로 받으시면 절대 안됩니다!** 꼭 개인 지갑 주소를 보내주셔야 합니다. 보통 이더리움월렛 혹은 마이이더월렛(MEW)을 이용하니 참고하시기 바랍니다.
----------------
TaeKim(@nida-io) 의 프로젝트를 구경하세요.
- [니다닷컴](http://니다.com) : 쉽게 읽히는 "한글 멘트 그대로의 링크" 를 만들어드립니다. 마케팅 콘텐츠, 홈페이지, 쇼핑몰, 블로그, 청첩장, 포트폴리오 등의 링크에 사용하여 가독성과 클릭율을 높여보세요.
- [케이스타코인](http://kstarcoin.com) : 830만 팔로어 전세계 1위 한류 미디어 KStarLive 와 함께 만든 한류 플랫폼에 사용되는 코인입니다. 스팀잇처럼 커뮤니티 활동을 하면서 코인을 얻을 수 있으며, 한류 콘텐츠 구매, 공연 예매, 한국 관광 관련, 기부 및 팬클럽 활동 등에 사용될 계획입니다.