見出し画像

Solidityの基礎とERC-20トークンの発行について

EVMを使ってスマートコントラクトを組む際、プログラミング言語としてSolidityの知識は必須になります。そこで、今回、Solidityの基礎を説明し、実際にERC-20トークンを作成してみます。

1 いちばんさいしょに

まずsolidityを使うには、PCへの環境構築を行います。これは各自調べてください。また、環境構築が不要なツールもありますので、環境構築が面倒な方はRemixを使うといいでしょう。https://remix.ethereum.org/

まず、Solidityの書式はまず以下になります。

pragma solidity >=0.4.0 <0.6.0;

contract コントラクト名 {
    処理内容
}

0.4.0や0.6.0はSolidityのバーションを表します。

2 値と演算について

値とは、数値や文章のことを言います。また、文章の時は" "で括ります。

(例) 123、0.32、"あうい" 、"thie is it"

またプログラミングは本来、計算するためにあるものです。なので、四則演算を行う事ができます。これは、数字同士だけでなく、テキストと演算することもできます。

int x = 1 + 4 - 2 × 3;
int y = 'abc' + 123;

3 変数について

変数の宣言

変数とはいわゆる、値を入れるための箱のようなものです。z
その変数はどのようなタイプ(型)であるのかを宣言する必要があります。そのため、以下のように定義します。

型名 修飾子 変数名 ;

修飾子の一つにmemoryとstorageあり、 memoryはEVM内で完結される一時変数、storageはブロックチェーンに 保管される変数です。

★基本的な変数の型の例
 ・整数型:int (整数を定義)
 ・固定小数点型:flixed (固定小数点を定義)
 ・アドレス型:adress (イーサリアムのアドレスを定義)

また値を変数に保管する際、以下のように記述します。

型名 修飾子 変数名 = 値;

ここで、=(イコールと呼ぶ)の意味は右辺を左辺に代入するということです。
つまり、Aを変数、Bを値とすると、A=B は「BをAに代入する」という意味になります。
数学でのイコールとは意味が違いますので、注意してください。

int x = 1 ;
adress y = 0x1d009CBBc2c5f336226a122D8d7d9baF7508407E ;
uint memory abc = 3 ;

具体的な変数の型の説明

ブーリアン型 - bool
trueまたはfalseを代入するための変数です。

bool a = true;
bool b = false;

整数型 - int、unit
整数を代入するための変数であり、符号付きを定義するintと符号なしを定義するunitがあります。また、int8からunit256までの8ビット刻みで宣言します。(intとかunit単体で利用するときは、int256およびunit256になります)

int a;
unit8 b;

文字列型
文字列型は任意の長さの文字列を保存することができ、stringと書きます。

string str;
str = "hello Ethereum !";

固定小数点型
固定小数点を定義します。これは、fixedMxNまたはufixedMxNと宣言され、Mは8〜256までのビット単位のサイズ、Nはそのポイント以降の少数の数(最大18)です。
MとNを省略した場合はそれぞれ、ufixed128x19 とfixed128x19を意味します。

fixed a;
ufixed32x8 b;

アドレス型 - adress
イーサリアムのアドレスを保管できる型です。

adress y = 0x1d009CBBc2c5f336226a122D8d7d9baF7508407E ;

4 制御文について

制御文とは、プログラムを分岐させたりループさせるの文章を言います。ここでは、代表的なものであるif文とfor文について紹介します。

(1) if文
if文は条件分岐をする際の構文であり、条件に応じて実行する処理を変更します。
この文は、以下のように記述します。

if(条件式){
条件式を満たしているときに、この処理を実行する
} else{
条件式を満たしていないとき、この処理を実行する}

int x = 15 ;
if(x>=10){
    命令a ;
} else{
    命令b ;
}

この例文は、変数xが10以上だと、命令aが実行され、そうではない場合は命令bが実行されます。
ここで、条件式を満たしていない処理を実行しない場合は、else以下の文を省略することができます。
ここで、比較演算子について説明します。ここではAとBを値として考えます。

  • A==B AとBは等しい

  • A != B AとBは等しくない

  • A < B AはB未満である

  • A <= B AはB以下である

  • A > B AはBより大きい

  • A >= B AはB以上である

これらの比較演算子をif文の条件式に書きます。

(2) for文
for文は繰り返し処理をする際に用いられる構文であり、以下のようにして記述されます。

for(初期値 ; 繰り返し処理をする条件 ; 処理した後の処理){
繰り返し処理をする内容
}

説明すると、まず初期値の所で変数と繰り返し処理をする際の一番最初の値を設定します。それから「繰り返し処理をする条件」に当てはめ、条件式を満たした場合に「繰り返し処理をする内容」を実行し、その次に「処理した後の処理」に入ります。それから「繰り返し処理する条件」にあてはめ、その条件を満たしたら、また「繰り返し処理をする内容」を実行し、その次に「処理した後の処理」に入ります。それを繰り返し条件をする条件を満たさなくなるまで継続します。
つまり、
初期値→繰り返し処理をする条件→繰り返し処理をする内容→処理した後の処理→繰り返し処理をする条件→繰り返し処理をする内容→処理した後の処理・・・
とループをしていくということです。

for(int x=8;x<10;x++)
 命令a;
}

5 配列について

配列

プログラミングコードを書く際、変数をいくつも定義すると、コードが複雑になることがあります。そのときに役に立つのが配列です。配列は、複数の値をまとめて保管できる変数です。ここで定義した変数を配列名といい、以下のように記述します。

型名[index] 修飾子 配列名

uint[10] array ;

ここで、indexは配列の要素を表します。配列にはindex番号が割り振られていて、先頭より0、1、2と割り振られています。その定義した配列より値を表示したい場合は、以下のように記述します。 

配列名[インデックス番号];

array[0] = 1 ;

固定バイト配列

任意の値をなんでも保管でき、bytesNと表します。またNはサイズを表し、1~32まで指定できます。

bytes8 num = 1;

動的バイト配列

要素数が未定の場合は、サイズに応じて変えられるものがあります。
それは、bytesまたはbytes[]と表されます。

bytes raw;

マッピング

マッピングとは値の入った変数に名前を付けて、「名前」と「中身に入っている値」をセットで管理する方法です。
その名前をkeyといい、中身に入っている値をvalueといいます。

mapping(key_type => value_type) 配列名

これはkey_Type(キーの型)をキーとしたvalue_Type(値の型)の配列を意味します。

//addressをキーとしたuintの配列
mapping(address => uint)  balance;
balance[0x00001234abcd] = 100;

この記述例は、addressを指定することで対応するuint型の値が取得できる配列を意味し、アドレスからハッシュ値を得るために用いることができます。

6 構造体と列挙体

構造体


構造体とは、複数の型のデータを一つにまとめたものであり、構造体を構成する要素をメンバといいます。
構造体の書式は以下のようにして表します。

struct 構造体タグ{
型 メンバ名;
型 メンバ名;
…
};

ここで、この構造体に付けられる名前を構造体タグといいます。

struct nemlog {
uint8 age;
string name;
}

構造体を利用する際は、以下のようにして変数を用意します。

構造体タグ 変数名 ;

構造体の中にある使いたい変数を利用する時は以下のように書きます。

変数名.メンバ名 ;

struct nemlog {
uint8 age;
string name;
}

nemlog YUTO;
YUTO.age = 32;

列挙体


列挙体とはいくつかの定数をひとまとめにしたものであり、書式は以下のようにして表します。

enum 列挙体タグ{
定数1 ;
定数2 ;
…
};

(例文)

enum lubin { GoLeft, GoRight, GoStraight, SitStill }

列挙体を利用する際は、以下のようにして変数を用意します。
列挙体タグ 変数名 ;
列挙体の中にある使いたい定数を利用する時は以下のように書きます。
変数名 = 列挙体タグ.定数 ;

enum lubin { GoLeft, GoRight, GoStraight, SitStill }

xymcity hatchet;
hatchet = xymcity.GoStraight;

7 オブジェクト指向

配列にはindex番号が割り振られていて、先頭より0、1、2と割り振られています。その定義した配列より値を表示したい場合は、以下のように記述します。
 配列名[インデックス番号];
ここで、インデックス番号の代わりに文字を使った場合の配列をオブジェクトといいます。オブジェクトの中には複数の情報(属性)を持ちます。その情報を使いたい場合は以下のように記述します。

 オブジェクト名.属性名

msgオブジェクト
トランザクションまたはメッセージに関する属性が含まれています。
 msg.sender : 直近の呼び出し元のアドレスを返します。
 msg.value : 送られたイーサの量を返し、単位はweiであります
 msg.gas : 実行環境にて残っているガスの量を返します。
 msg.data : msg の完全なバイナリデータを返します。

txオブジェクト
トランザクション関連の情報にアクセスする手段を提供する。
 tx.gasprice : 呼び出し元のトランザクションでのガス価格を返す。
 tx.origin : トランザクションの呼び出しアドレスを返します

blockオブジェクト
現在のブロックに関する情報が含まれています。block.coinbase (address): 現在のブロックの coinbase(
 block.coinbase : 現在のブロックの料金とブロック報酬の受取人のアドレス(coinbase)を返します。
 block.difficulty : 現在のブロックの難易度(difficulty)を返します。
 block.gaslimit : 現在のブロックに含まれる全てのトランザクションで消費できるガス量の最大値を返します。
 block.number : 現在のブロック高(ブロックチェーンの番号)を返します。
 block.timestamp : 現在のブロックのタイムスタンプを返します。

addressオブジェクト
 address.balance
: アドレスの残高を返します。単位はweiです。 
 address.transfer(amount) : アドレスに金額(単位:wei)を送金します。エラーの場合は例外を投げます。
 address.send(amount):アドレスに金額(単位:wei)を送金します。エラーの場合はfalseを返します。

8 関数について

関数とは、ある処理をひとまとめにして書いたものであり、入力された値と出力された値のパイプ役となるものです。これはSolidityにあらかじめ組み込まれた関数(組み込み関数)と、自分で定義する関数(ユーザー定義関数)があります。

組み込み関数について

組み込み関数は

関数の名前 (parameter types) [returns (return types)]

のようにして書きます。ここでは、どんな関数の名前があるのか紹介します。

addmod: (x + y) % k を計算します。
mulmod: (x * y) % k を計算します。
keccak256 : SHA3(kec- cak256) を計算します。
sha3 : keccak256のエイリアス。
sha256 : sha256 を計算します。
ecrecover : 署名からメッセージの署名に利用されたアドレスを返します。selfdestruct(address recipient): 現在のコントラクトを削除し、アカウントにあるイーサの残高を受信者アドレスに送金します。
this : 現在実行中のコントラクトアカウントのアドレスを返します。

ユーザー定義関数について

関数を定義する場合は、基本的に以下のようになります。

  function FunctionName (parameter types) {public|external|internal|private} [view|pure|payable]
     [returns (return types)]

ここで
FunctionName:関数の名前。名前は自由に設定できます。
parameter types:関数に渡す引数。引数には型を指定します。

アクセス修飾子(public, private, internal, external)について
 public(パブリック関数)
: publicで定義された関数や変数は、コントラクト内からの呼び出しやコントラクト外部からの呼び出しにも対応する。
 external(外部関数):コントラクト外部からの呼び出しのみに対応する。
 internal(内部関数)
: コントラクト内部からの呼び出のみに対応する。  
 private(プライベート関数) : 内部関数と機能が似ているが、継承されたコントラクトからの呼び出しには対応しない。

状態装飾子について
 view
: 関数が動作してもデータの保存や変更は一切行わず、データの閲覧のみを行うと宣言します。
 pure : 関数が動作してもデータの保存や変更を行わないだけでなく、読み取りすらも行わないことを宣言します。
 payable : 送金を受け取る事ができる機能

[returns (<return types>)] について
処理内容には、戻り値を設定することがあります。戻り値とは関数内で処理された結果として得られた値です。つまり、出力値です。<return type>に型を宣言した引数を書きます。
 戻り値を設定することで、処理内容に書かれた処理を終わらせ、そして呼び出し先の関数が出力値として値となります。
 ここでごはんに例えると、ごはんを炊く前の米が引数、炊飯器が関数、炊いた米が戻り値になります。

//要求する人にイーサを与える
function withdraw(unit withdraw_amount) public{
  
       //引出額を制限する
        require (withdraw <= 10000000000000) ;
 
     //リクエストしたアドレスへ指定した金額を送る
       msg.sender.transfer(withdraw_amount) ;
}

//入金額をもらう
function ( ) public payable { }
  function transfer(address _to, uint256 _value) public returns (bool success) {
       _transfer(msg.sender, _to, _value);
       return true;
   }

コンストラクトについて

スマートコントラクトを以下の書式で書くとします。

contract コントラクト名 {
     function 関数名( ) {
        }
}

ここで、「コントラクト名 = 関数名」となった時の関数をコンストラクト関数といいます。
この時、以下のように記述する事でコンストラクト関数である事を明確にでき、エラーを少なくできます。

contract コントラクト名 {
     function 関数名( ) {
           constructor() {
              }
        }
}

つまり、constructor( ){ }を加えるという事です。

関数装飾子

modifier修飾子を関数につけると、修飾された関数を実行する前に、modifierの処理を予め実行させることができます。
modifier修飾子をつけた関数を関数装飾子と言い、以下の書式で定義します。

modifier 関数名{
  modifierの処理部分;
  _;
}

ここで、_; では、処理部分全体が終了したことを意味するので、必ず必要です。
(例文)

modifier onlyOwner {
  require(msg.sender == owner);
  _;
  }

ここで、関数装飾子onlyOwnerを利用してdestroy関数を利用してみます。

 function destroy() public onlyOwner {
  selfdestruct(owner);
  }

これは、destroy関数はonlyOwnerに修飾されている事を意味し、destroy()を実行する前にonlyOwnerを実行することになります。

9 コントラクトの継承について

Solidityではあるコントラクト(親コントラクト)の機能や変数を別のコントラクト(子コントラクト)へ引き継ぐ事ができ、これを継承といいます。
継承を利用するには、isを用いて親コントラクトを指定し、以下のようにします。

contract 子コントラクト is 親コントラクト {
  処理内容
}

また、あるコントラクトAをコントラクトBを通してコントラクトCへ継承する事を多重継承といい、以下のように書きます。

contract C is A , B {
  処理内容
}

つまり、isの後にコンマで区切ります。また、AとBの順番は逆にしてはいけません。

(例文)

contract Owned {
 constructor() public { owner = msg.sender; }
     address payable owner;
 }

 contract Destructible is Owned {
     function destroy() virtual public {

if (msg.sender == owner) selfdestruct(owner);
    }
 }
contract Named is Owned, Destructible {
    constructor(bytes32 name) public {
        Config config = Config(0xD5f9D8D94886E70b06E474c3fB14Fd
43E2f23970); NameReg(config.lookup(1)).register(name);
}

10 イベントについて

Eventとは、ブロックチェーンで何かが生じたときに、web上やアプリ上に伝えることができる仕組みを言います。

function add(uint _x, uint _y) public {
   uint result = _x + _y; 
   return result;
}

この関数の実行結果をweb上に伝える事ができないので、eventを使ってweb上と連絡を取ります。
まずは、この関数と関係ないところでevent IntegersAddedを宣言します。引数をx,y,resultとします。

event IntegersAdded(uint x, uint y, uint result);

先ほどの関数に一行IntegersAddedイベントを追記する事で。関数が呼ばれたことをアプリに伝えるためにイベントを発生させる事ができます。

function add(uint _x, uint _y) public {
    uint result = _x + _y; 
    IntegersAdded(_x, _y, result);//ここでeventを入れる
    return result;
}

11 ERC-20トークンの作成

ここではイーサリアムプロトコルを用いた自作の暗号資産(ERC20トークン)の作り方を紹介します。

11.1 ERC-20トークンとは

ERC20とは”Ethereum Request for Comments: Token Standard #20”の略称で、2015年11月に提案されたイーサリアムでトークンを発行するための規格のことです。そのERC20を用いて開発されたトークンの総称をERC20トークンといいます。
そのERC20トークンの標準的な仕様は以下のようになります。

contract ERC20Interface {
    function totalSupply() public view returns (uint256);
    function balanceOf(address _owner) public view returns (uint256 balance);
    function allowance(address _owner, address _spender) public view returns (uint256 remaining);
    function transfer(address _to, uint _value) public returns (bool success);
    function approve(address _spender, uint256 _value) public returns (bool success);
    function transferFrom(address from, address to, uint tokens) public returns (bool success);

    event Transfer(address indexed _from, address indexed _to, uint256 _value);
    event Approval(address indexed _owner, address indexed _spender, uint256 _value);
}

この仕様に従ってERC-20トークンが作られます。それぞれの関数と意味について紹介します。

functionについて

totalSupply
トークンの総供給量を返します。

balanceOf
所有者アドレスの別の口座の残高を返します。

transfer
トークンの _value 量をアドレス _to に転送し、Transfer イベントを発生させなければならない(MUST)。メッセージ呼び出し元のアカウント残高に使用するのに十分なトークンがない場合、この関数はthrowする必要があります(SHOULD)。

allowance
spenderが_ownerからまだ引き出すことができる金額を返します。

approve
spenderがアカウントから_valueの金額まで複数回引き出しできるようにします。この関数が再び呼ばれた場合、現在の引当金を _value で上書きする。

transferFrom
アドレス _from からアドレス _to へトークンを _value 分転送し、Transfer イベントを発生させなければならない。

eventについて

Transfer
ゼロバリュー転送を含む、トークンが転送されたときに起動しなければならない

Approval
approve(address _spender, uint256 _value)の呼び出しが成功するとトリガされなければならない

以上の意味は、GitHubのリポジトリにて紹介されているので、以下のURLにて確認できます。
https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md#totalsupply

実装例として以下のコードがあり、これをコピペするだけでトークンを作成できます。ここでは以下のコードを用いて、テストネットでのトークン作成を実践してみます。

pragma solidity ^0.6.0;

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

contract TokenERC20 {
   string public name;
   string public symbol;
   uint8 public decimals = 18;
   uint256 public totalSupply;

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

   event Transfer(address indexed from, address indexed to, uint256 value);
   event Approval(address indexed _owner, address indexed _spender, uint256 _value);
   event Burn(address indexed from, uint256 value);

   constructor(
       uint256 initialSupply,
       string memory tokenName,
       string memory tokenSymbol
   ) public {
       totalSupply = initialSupply * 10 ** uint256(decimals);
       balanceOf[msg.sender] = totalSupply;
       name = tokenName;
       symbol = tokenSymbol;
   }

   function _transfer(address _from, address _to, uint _value) internal {
       require(_to != address(0));
       require(balanceOf[_from] = _value);
       require(balanceOf[_to] + _value = balanceOf[_to]);
       uint previousBalances = balanceOf[_from] + balanceOf[_to];
       balanceOf[_from] -= _value;
       balanceOf[_to] += _value;
       emit Transfer(_from, _to, _value);
       assert(balanceOf[_from] + balanceOf[_to] == previousBalances);
   }

   function transfer(address _to, uint256 _value) public returns (bool success) {
       _transfer(msg.sender, _to, _value);
       return true;
   }

   function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) {
       require(_value ;= allowance[_from][msg.sender]);     // Check allowance
       allowance[_from][msg.sender] -= _value;
       _transfer(_from, _to, _value);
       return true;
   }

   function approve(address _spender, uint256 _value) public
       returns (bool success) {
       allowance[msg.sender][_spender] = _value;
       emit Approval(msg.sender, _spender, _value);
       return true;
   }

   function approveAndCall(address _spender, uint256 _value, bytes memory _extraData)
       public
       returns (bool success) {
       tokenRecipient spender = tokenRecipient(_spender);
       if (approve(_spender, _value)) {
           spender.receiveApproval(msg.sender, _value, address(this), _extraData);
           return true;
       }
   }

   function burn(uint256 _value) public returns (bool success) {
       require(balanceOf[msg.sender] = _value);
       balanceOf[msg.sender] -= _value;
       totalSupply -= _value;
       emit Burn(msg.sender, _value);
       return true;
   }

   function burnFrom(address _from, uint256 _value) public returns (bool success) {
       require(balanceOf[_from] = _value);
       require(_value = allowance[_from][msg.sender]);
       balanceOf[_from] -= _value;
       allowance[_from][msg.sender] -= _value;
       totalSupply -= _value;
       emit Burn(_from, _value);
       return true;
   }
}

以上のコードは、この記事より引用しています。


11.2 METAMASKを用いてウォレットを作成する

ERC20トークンを作るには、METAMASKが必要ですので、METAMASKをPCに導入してみます。
1 Google Chromeをインストールしてください:こちら
2 METAMASKを追加する
Chromeの拡張機能として、イーサリアムのウォレットであるMETAMASKがあるので、それを追加します。
まずは、以下のリンクをクリックしてください。

それから、「Chromeに追加」と書かれている所をクリックしてください。

追加すると以下のようになります。

拡張機能を追加をクリックし、しばらくすると、以下の画面になります。
そうなったら、Get Startedと書かれている所をクリックしてください。

クリックすると、以下の画面になります。
ここでは新しくウォレットを作るので、赤く四角で囲んだ部分をクリックしてください。

すると、以下の画面になります。
ここでは、I agree にクリックをしてください。

クリックしたら、以下の画面になります。

ここでパスワードを作成し、チェック蘭にチェックを入れたら作成と書かれたところをクリックしてください。

作成をクリックすると、以下の画面になります。

ここで、赤く四角で囲んだ部分をクリックしてください。
すると、12個の英単語が表示されます。
この英単語は必ずメモをしてください。また、この英単語の並びは必ず外部に漏らさないようにしてください。
この英単語の並びをニーモニックコードといいます。

ここで、次へと書かれている所をクリックしてください。
すると、以下の画面になります。そうしたら、

先ほどメモした内容と同じ順番になるように英単語をクリックしてください。同じ順番で選び終わったら「確認」をクリックします。

確認をクリックすると、以下の画面になります。

「全て完了」をクリックすると、MetaMaskの登録は完了です。

11.3 テストネット環境にする

MERAMASKを起動したら、赤い四角で囲んだ部分をクリックしてください。

すると、以下の画面が表示されます。
テストネット環境はどれを選んでもいいですが、ここでは「Ropstenテストネットワーク」をクリックしたとします。

すると、以下の画面になります。

これで「Ropstenテストネットワーク」になっていることになります。
次にテスト用イーサを手に入れます。この方法はテストネット環境に合わせて各自調べてください。

11.4 プログラミング

ここではRemixと呼ばれる、イーサリアム用プログラミング言語(solidity(ソリディティー))を書くことができるツールを使ってERC-20トークン をつくります。そこで、以下のURLをクリックしてください。

アクセスしたらまず Environmentsで「SOLIDITY」選択してください。

その後、左端のファイルアイコンを選択してください。

次は「+」ボタンで新規のファイルを作成します。

すると名前がつけれられます。

ここではとりあえずerc20.sol という名前をつけます。
(名前自体はなんでもいいですが〇〇.solと必ず .sol をつけてください)

そして「OK」をクリックします。
ここから、プログラミングをしていきます。
先ほど紹介した以下のコードをコピーしてください。

pragma solidity ^0.6.0;

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

contract TokenERC20 {
   string public name;
   string public symbol;
   uint8 public decimals = 18;
   uint256 public totalSupply;

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

   event Transfer(address indexed from, address indexed to, uint256 value);
   event Approval(address indexed _owner, address indexed _spender, uint256 _value);
   event Burn(address indexed from, uint256 value);

   constructor(
       uint256 initialSupply,
       string memory tokenName,
       string memory tokenSymbol
   ) public {
       totalSupply = initialSupply * 10 ** uint256(decimals);
       balanceOf[msg.sender] = totalSupply;
       name = tokenName;
       symbol = tokenSymbol;
   }

   function _transfer(address _from, address _to, uint _value) internal {
       require(_to != address(0));
       require(balanceOf[_from] = _value);
       require(balanceOf[_to] + _value = balanceOf[_to]);
       uint previousBalances = balanceOf[_from] + balanceOf[_to];
       balanceOf[_from] -= _value;
       balanceOf[_to] += _value;
       emit Transfer(_from, _to, _value);
       assert(balanceOf[_from] + balanceOf[_to] == previousBalances);
   }

   function transfer(address _to, uint256 _value) public returns (bool success) {
       _transfer(msg.sender, _to, _value);
       return true;
   }

   function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) {
       require(_value ;= allowance[_from][msg.sender]);     // Check allowance
       allowance[_from][msg.sender] -= _value;
       _transfer(_from, _to, _value);
       return true;
   }

   function approve(address _spender, uint256 _value) public
       returns (bool success) {
       allowance[msg.sender][_spender] = _value;
       emit Approval(msg.sender, _spender, _value);
       return true;
   }

   function approveAndCall(address _spender, uint256 _value, bytes memory _extraData)
       public
       returns (bool success) {
       tokenRecipient spender = tokenRecipient(_spender);
       if (approve(_spender, _value)) {
           spender.receiveApproval(msg.sender, _value, address(this), _extraData);
           return true;
       }
   }

   function burn(uint256 _value) public returns (bool success) {
       require(balanceOf[msg.sender] = _value);
       balanceOf[msg.sender] -= _value;
       totalSupply -= _value;
       emit Burn(msg.sender, _value);
       return true;
   }

   function burnFrom(address _from, uint256 _value) public returns (bool success) {
       require(balanceOf[_from] = _value);
       require(_value = allowance[_from][msg.sender]);
       balanceOf[_from] -= _value;
       allowance[_from][msg.sender] -= _value;
       totalSupply -= _value;
       emit Burn(_from, _value);
       return true;
   }
}

以上のコードを空白部分に貼り付けます(ペーストします)。

貼り付けると・・・

そうしたら、画面左側にあるSolidityマークをクリックします。

「Compile erc20.sol」をクリックします。

その後、画面左のEthereumマークをクリックします。

「Environment」の項目を「Injected Web3」に変更します。

Ropstenテストネットを利用している場合「Ropsten」になっていれば、MetaMaskの設定をRopstenに変更してください。
またメインネットを利用している場合「mainnet」になっています

次に「Deploy」の右端「 v 」をクリックします。

すると、以下のようなものが出ます。
ここで、供給量、トークン名、ティッカーシンボルを設定します。

InitialSupply
トークンの初期発行量(例. 2000)
自分の発行したい枚数を半角数字で書くこと

tokenName
トークンの名前(例. YUTON)
自分の好きなトークンの名前を英語で書くこと

tokenSymbol
トークンのティッカーシンボル
自分でつけたトークンの短縮表記を書きます。(例. YTN)

入力したら、「transact」をクリックし、登録します。

数分後、登録が完了すると、画面左下「Deployed Contracts」に表示されます。これで、自作の暗号資産が誕生です!!

ここで、赤く四角で囲んだ所をクリックしてください。
ここをクリックすると、トークンコンタラクトアドレスを言われるものをコピーでき、METAMASKに発行したトークンを表示できるようになります。

11.5 自作の暗号資産を送金する

次に、METAMASKを起動し、赤い四角で囲んだ部分をクリックしてください。

すると、以下のようになります。

「トークンを追加」をクリックします。

①カスタムトークンと書かれている所をクリックします。
②トークンコンタラクトアドレスを貼り付ける

そうすると、トークンシンボルと小数点数が自動的に追加されます。
そしたら、「次へ」をクリックしてください。

「トークン を追加」をクリックします。

「送信」をクリックすると、トークンを送信できます。

ここで送信先のアドレスを入力してください。
もし、ここでは「0x1d009CBBc2c5f336226a122D8d7d9baF7508407E」へ送金してみてみます。

そしたら、送金したい金額と送金手数料を設定できます。
設定したら、「次へ」をクリックしてください。

「確認」をクリックして、送金が完了です。

参考資料

マスタリング・イーサリアム
誰でも簡単!オリジナル仮想通貨を発行する方法(https://note.com/toshitoissho/n/n93eadf07fd47)
「初心者向け」プログラミング未経験からERC20トークンを発行する方法(https://magazine.blockchaincodecamp.jp/erc20-no2/)



いいなと思ったら応援しよう!