Skip to content

Instantly share code, notes, and snippets.

@m1noon
Last active October 19, 2017 03:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save m1noon/018df1a9970cf5a790953d5ea7ae70ab to your computer and use it in GitHub Desktop.
Save m1noon/018df1a9970cf5a790953d5ea7ae70ab to your computer and use it in GitHub Desktop.
Token(Ethereum.org)の日本語訳

Ethereum翻訳 https://ethereum.org/token

コイン

デジタルトークンをつくっていきましょう。Ethereumエコシステムにおけるトークンは任意の代替可能で取引可能なモノ(コイン、ロイヤルティ、ポイント、金券、借用証書、ゲーム内アイテム、etc..)として利用できる。すべてのトークンがいくつかの基本的な特徴を標準的な方法で実装することで、あなたのトークンも即座にethereumウォレットや、同じ標準に則った他のクライアントやコントラクトと互換性があることを意味する。

最小実行可能トークン(Minimum Viable Token)

標準的なトークンコントラクトは極めて複雑になる可能性がある。しかし、本質的には、非常に基本的なトークンは以下のようになる。

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) {
        require(balanceOf[msg.sender] >= _value);           // Check if the sender has enough
        require(balanceOf[_to] + _value >= balanceOf[_to]); // Check for overflows
        balanceOf[msg.sender] -= _value;                    // Subtract from the sender
        balanceOf[_to] += _value;                           // Add the same to the recipient
    }
}

コード

コピー&ペーストだけで動く完全なコードが必要であれば、それは以下のようになる。

pragma solidity ^0.4.16;

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

contract TokenERC20 {
    // Public variables of the token
    string public name;
    string public symbol;
    uint8 public decimals = 18;
    // 18 decimals is the strongly suggested default, avoid changing it
    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);

    /**
     * Constrctor function
     *
     * Initializes contract with initial supply tokens to the creator of the contract
     */
    function TokenERC20(
        uint256 initialSupply,
        string tokenName,
        string tokenSymbol
    ) public {
        totalSupply = initialSupply * 10 ** uint256(decimals);  // Update total supply with the decimal amount
        balanceOf[msg.sender] = totalSupply;                // Give the creator all initial tokens
        name = tokenName;                                   // Set the name for display purposes
        symbol = tokenSymbol;                               // Set the symbol for display purposes
    }

    /**
     * Internal transfer, only can be called by this contract
     */
    function _transfer(address _from, address _to, uint _value) internal {
        // Prevent transfer to 0x0 address. Use burn() instead
        require(_to != 0x0);
        // Check if the sender has enough
        require(balanceOf[_from] >= _value);
        // Check for overflows
        require(balanceOf[_to] + _value > balanceOf[_to]);
        // Save this for an assertion in the future
        uint previousBalances = balanceOf[_from] + balanceOf[_to];
        // Subtract from the sender
        balanceOf[_from] -= _value;
        // Add the same to the recipient
        balanceOf[_to] += _value;
        Transfer(_from, _to, _value);
        // Asserts are used to use static analysis to find bugs in your code. They should never fail
        assert(balanceOf[_from] + balanceOf[_to] == previousBalances);
    }

    /**
     * Transfer tokens
     *
     * Send `_value` tokens to `_to` from your account
     *
     * @param _to The address of the recipient
     * @param _value the amount to send
     */
    function transfer(address _to, uint256 _value) public {
        _transfer(msg.sender, _to, _value);
    }

    /**
     * Transfer tokens from other address
     *
     * Send `_value` tokens to `_to` in behalf of `_from`
     *
     * @param _from The address of the sender
     * @param _to The address of the recipient
     * @param _value the amount to send
     */
    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;
    }

    /**
     * Set allowance for other address
     *
     * Allows `_spender` to spend no more than `_value` tokens in your behalf
     *
     * @param _spender The address authorized to spend
     * @param _value the max amount they can spend
     */
    function approve(address _spender, uint256 _value) public
        returns (bool success) {
        allowance[msg.sender][_spender] = _value;
        return true;
    }

    /**
     * Set allowance for other address and notify
     *
     * Allows `_spender` to spend no more than `_value` tokens in your behalf, and then ping the contract about it
     *
     * @param _spender The address authorized to spend
     * @param _value the max amount they can spend
     * @param _extraData some extra information to send to the approved contract
     */
    function approveAndCall(address _spender, uint256 _value, bytes _extraData)
        public
        returns (bool success) {
        tokenRecipient spender = tokenRecipient(_spender);
        if (approve(_spender, _value)) {
            spender.receiveApproval(msg.sender, _value, this, _extraData);
            return true;
        }
    }

    /**
     * Destroy tokens
     *
     * Remove `_value` tokens from the system irreversibly
     *
     * @param _value the amount of money to burn
     */
    function burn(uint256 _value) public returns (bool success) {
        require(balanceOf[msg.sender] >= _value);   // Check if the sender has enough
        balanceOf[msg.sender] -= _value;            // Subtract from the sender
        totalSupply -= _value;                      // Updates totalSupply
        Burn(msg.sender, _value);
        return true;
    }

    /**
     * Destroy tokens from other ccount
     *
     * Remove `_value` tokens from the system irreversibly on behalf of `_from`.
     *
     * @param _from the address of the sender
     * @param _value the amount of money to burn
     */
    function burnFrom(address _from, uint256 _value) public returns (bool success) {
        require(balanceOf[_from] >= _value);                // Check if the targeted balance is enough
        require(_value <= allowance[_from][msg.sender]);    // Check allowance
        balanceOf[_from] -= _value;                         // Subtract from the targeted balance
        allowance[_from][msg.sender] -= _value;             // Subtract from the sender's allowance
        totalSupply -= _value;                              // Update totalSupply
        Burn(_from, _value);
        return true;
    }
}

コードの解説

// 画像

基本的なところから見ていきましょう。 ウォレットアプリを開いて、 Contractsタブを開き、Deploy New Contractをクリックします。Solidity Contract Source Code のテキストフィールド上に、以下のコードを入力してみてください。

contract MyToken {
    /* This creates an array with all balances */
    mapping (address => uint256) public balanceOf;
}

mappingは連想配列のことで、アドレスを残高に関連付けています。アドレスは基本的な16進数のethereumのフォーマットで、残高はintegerであり、範囲は0から115*10^75となっています。10^75という値がどれくらい大きいかわからないかもしれませんが、これはトークンを利用するときに想定するどんな値よりも大きいといえます。publicキーワードは、この変数がブロックチェーン上の誰からでもアクセス可能であることを意味しており、(クライアントがそれらを表示するために)すべての残高が公開されていることを意味する。

// 画像

もしコントラクトをすぐにでも公開した場合、動きはしますが、実用的なものとは言えません。コイン残高を調べることはできますが、一切のコインを生成することができないため、全員の残高は0のままです。そこで、これから起動時に少しだけトークンを生成してみましょう。最後のブラケットの手前に、以下のコードを追加して下さい。

    function MyToken() {
        balanceOf[msg.sender] = 21000000;
    }

function MyTokenというのが、 contract MyTokenの箇所と同じ名前であることに注意してください。これはとても重要で、もしこの名前の片方を変えた場合は、もう片方も同様に変更する必要があります。これは特別なもので、コントラクトがネットワークにアップロードされたときに一度だけ起動されます。この関数は msg.sender(コントラクトをデプロイしたユーザー)の残高に2100万のトークンを追加します。

2100万という値は任意なので、コードの中で好きに変えてしまっても問題ありません。ただし、もっとよい方法として、以下のように関数の引数として渡す方法があります。

    function MyToken(uint256 initialSupply) {
        balanceOf[msg.sender] = initialSupply;
    }

コントラクトの横の列にある、pick a contractと書かれた箇所をドロップダウンしてください。そこにある"My Token"コントラクトを選択すると、Constructor parametersという欄が表示されます。これらはトークンのための可変パラメータで、ここの値を変更するだけで同じコードを再利用することができます。

// 画像

トークン残高を生成できる機能的なコントラクトができあがりましたが、残高を移動する機能が存在しないため、全ての残高がずっと同じ口座に入れられたままで動かすことはできません。そこで、残高の移動を実装してみましょう。最後のブラケットの手前に以下のコードを追加してください。

    /* Send coins */
    function transfer(address _to, uint256 _value) {
        /* Add and subtract new balances */
        balanceOf[msg.sender] -= _value;
        balanceOf[_to] += _value;
    }

これはとても単純な関数です。受取側のアドレスと値が引数として渡され、いつだれがこの関数を呼び出そうと、 _value の値を送信者の残高から差し引いて、それを_toの残高に追加します。これに問題があることは明らかです。もし送信者が自分の残高より大きい額を送信しようとするとどうなるでしょうか?このコントラクトでは負債を扱うことを想定していないので、簡単なチェックを追加して送信者が十分な資産を持っていない場合には処理を中断するようにしたいと思います。また、残高の数値が大きくなりすぎて0に戻ってしまうようなことがないように、オーバーフローのチェックも行いましょう。

コントラクトの実行を途中で停止するために、returnthrow を使うことができます。前者は、ガス消費を抑えることができますが、コントラクトに対して行った変更がそのままの状態になることに悩まされるでしょう。一方、 'throw' はコントラクトの実行を全てキャンセルし、そのトランザクションによって引き起こされるあらゆる変更を巻き戻し、ガスのために送信されたetherは全て消費されます。しかし、ウォレットはコントラクトがthrowを実行するであろうことを検知できるため、常に警告を表示し、ehterが全く使われないように防ぐことができます。

    function transfer(address _to, uint256 _value) {
        /* Check if sender has balance and for overflows */
        require(balanceOf[msg.sender] >= _value && balanceOf[_to] + _value >= balanceOf[_to]);

        /* 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, string tokenSymbol, uint8 decimalUnits) {
        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
    }

最後に、Eventと呼ばれるものが必要になります。これは特別なもので、Ethereumウォレットのようなクライアントがコントラクト内で生じた活動を追跡するために利用される空の関数です。

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

これを有効に機能させるためには、以下の2行を 'transfer' 関数の中居に追加する必要があります。

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

これであなたのトークンの準備は万端です!

妙なコメントはなに?

上のコードにある @notice@param などのコメントはなんでしょう?これは Natspec と呼ばれ、自然言語仕様(ドキュメント)のための新興標準です。これによって、ウォレットはユーザーに自然言語で説明を表示することができます。現在はまだほとんどのウォレットでサポートされていませんが、将来的にはこの状況は改善されるでしょう。

デプロイの仕方

トークンの改善

基本機能についての詳細

あなたの基本的なトークンコントラクトに、approve , sendFrom のような関数がいくつかあることに気付くでしょう。これらの関数はあなたのトークンが他のコントラクトとやり取りするためのものです。例えば、分散型取引所にトークンを売りたい場合、単純にアドレスにトークンを送りつけるだけでは不十分です。なぜなら、取引所コントラクトは関数呼び出しのみのイベントを購読することができないため、新しいトークンやその送信者を認識することができないからです。したがってコントラクトに対して、最初にあなたの口座から一定量のトークンを移動させることを許可してから、pingを実行してコントラクトに処理の実行許可を通知します。あるいは、approveAndCallを仕様してその2つを同時に実行することもできます。

これらの関数の多くにおいて、トークンの送信を何度も実装する必要があるため、トークンの送信処理は内部関数として実装することをおすすめします。内部関数はコントラクト自身からのみ呼び出すことができます。

    /* Internal transfer, only can be called by this contract */
    function _transfer(address _from, address _to, uint _value) internal {
        require (_to != 0x0);                               // Prevent transfer to 0x0 address. Use burn() instead
        require (balanceOf[_from] > _value);                // Check if the sender has enough
        require (balanceOf[_to] + _value > balanceOf[_to]); // Check for overflows
        require(!frozenAccount[_from]);                     // Check if sender is frozen
        require(!frozenAccount[_to]);                       // Check if recipient is frozen
        balanceOf[_from] -= _value;                         // Subtract from the sender
        balanceOf[_to] += _value;                           // Add the same to the recipient
        Transfer(_from, _to, _value);
    }

ここで、コインの転送につながる全ての関数は、それぞれ独自のチェックを行ってから、正しいパラメータで転送を呼び出すことができます。 この関数は、誰の許可もなしに、コインをあらゆるアカウントからあらゆるアカウントへのコインを移動させることに注意してください。この関数を内部関数としてコントラクト自身からのみ呼び出せるようにする理由はまさにこれのためです。もしこの関数を呼び出す何らかの関数を追加する場合は、呼び出し元がそれらのトークンを移動させる権限を持っているかを正確に検証するように細心の注意を払いましょう。

中央管理者

全てのDAPPはデフォルトで完全に分散化されていますが、これはある種の中央管理者を一切持てないということではありません。あなたはおそらく、コインを追加発行したり、幾人かのユーザーを利用停止にしたりする必要が出てくるでしょう。こういった機能を追加することはできますが、それができるのは最初だけであるため、全てのトークン所持者は常に、トークンを所持しようと決意する前にゲームのルールを正確に知ることになるでしょう。

そうなるためには、通貨の中央管理者が必要となります。これは普通のアカウントであったりコントラクトであったりします。それゆえに、トークンの追加発行の決定はコントラクトに依存します。例えば投票を行うための民主的な組織の場合は、トークンのオーナーの能力に制限を加える手段となりえるでしょう。

これを実現するために、継承というとても便利なコントラクトの性質を学んでおくと良いでしょう。 継承を使うことで、コードの再定義することなく、あるコントラクトに親コントラクトのプロパティを持たせることができます。これによりコードは簡略化し、また再利用もしやすくなります。以下のコードをあなたのコードの最初、contract MyToken{ の前に追加してください。

    contract owned {
        address public owner;

        function owned() {
            owner = msg.sender;
        }

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

        function transferOwnership(address newOwner) onlyOwner {
            owner = newOwner;
        }
    }

これにより、"所有"されるコントラクトについての一般的な関数をいくつか定義しているだけの、基本的なコントラクトが生成されます。 次のステップは、 is owned というテキストをあなたのコントラクトに追加するだけです。

    contract MyToken is owned {
        /* the rest of the contract as usual */

これは MyToken内の関数が全て owner 変数や onlyOwner modifier にアクセス可能なことを意味します。このコントラクトはまた、所有権(ownership)の移動を行う関数も持っています。最初にコントラクトの所有権を設定するために、以下のようにコンストラクタ関数を追加することもできます。

    function MyToken(
        uint256 initialSupply,
        string tokenName,
        uint8 decimalUnits,
        string tokenSymbol,
        address centralMinter
        ) {
        if(centralMinter != 0 ) owner = centralMinter;
    }

中央造幣局(CENTRAL MINT)

流通しているコインの総量を変更したいときのことを考えてみましょう。これは、あなたのトークンがオフチェーンの資産(金証券や政府の通貨)を表していて、実在のi棚卸資産を反映する仮想棚卸資産が必要となるようなケースです。これはまた、通貨保有者が何らかの価格制御を期待しており、流通しているコインの削減や増加をしたいといったケースでもあります。

最初に、totalSupply(流通総量)を格納する変数を用意し、コンストラクタ関数で代入します。

    contract MyToken {
        uint256 public totalSupply;

        function MyToken(...) {
            totalSupply = initialSupply;
            ...
        }
        ...
    }

次に、オーナーが新規トークンの発行をできるように新しい関数を追加しましょう。

    function mintToken(address target, uint256 mintedAmount) onlyOwner {
        balanceOf[target] += mintedAmount;
        totalSupply += mintedAmount;
        Transfer(0, owner, mintedAmount);
        Transfer(owner, target, mintedAmount);
    }

関数名の最後にonlyOwnerという修飾子があることに注意してください。これにより、事前に定義しておいたmodifier onlyOwnerのコードを継承するようにコンパイル時に書き直されます。この関数のコードはmodifier関数の下線(_)の部分に挿入されます。つまり、今回の場合だとownerとして設定されたアカウントからのみ、この関数を呼び出せます。これをowner修飾子と一緒にコードに追加するだけで、コインを追加発行することができるようになります。

資産の凍結

実際に運用する上では、トークンを利用できる人、できない人を管理するための規制が必要になってくるでしょう。これを可能にするためには、コントラクトのオーナーに資産の凍結/凍結解除を可能にするパラメータを追加しましょう。

以下の変数と関数をコントラクト内の任意の場所に追加してください。場所はコントラクト内であればどこでもかまいませんが、mappingは他のmappingと同じ場所に、eventは他のeventと同じ場所に追加することをオススメします。

    mapping (address => bool) public frozenAccount;
    event FrozenFunds(address target, bool frozen);

    function freezeAccount(address target, bool freeze) onlyOwner {
        frozenAccount[target] = freeze;
        FrozenFunds(target, freeze);
    }

このコードによって、全てのアカウントはデフォルトで凍結解除された状態になりますが、オーナーは freezeAccount を呼び出すことで任意のアカウントを凍結状態にすることができます。transfer関数に何も変更を加えていないため、残念ながらこの凍結はまだ実用的な効果を発揮しません。以下のように修正しましょう。

    function transfer(address _to, uint256 _value) {
        require(!frozenAccount[msg.sender]);

これで、凍結されたアカウントはまだ資産を所有はしていますが、それらを移動することはできなくなりました。すべてのアカウントはあなたに凍結されるまではデフォルトで非凍結状態ですが、反対に、全てのアカウントを手動で許可してホワイトリストに追加していく、といった挙動に変更することも簡単にできます。frozenAccountapprovedAccountにリネームし、最後の一行を以下のように修正するだけです。

        require(approvedAccount[msg.sender]);

自動売買

これまでは、実用性と信頼に頼ることで、トークンの価値を決めていました。しかし、もし必要であれば、市場価格での自動売買を行うファンドを作成することで、トークンの価値をether(または別のトークン)で裏付けることができます。

まず最初に、売買のための価格を設定しましょう。

    uint256 public sellPrice;
    uint256 public buyPrice;

    function setPrices(uint256 newSellPrice, uint256 newBuyPrice) onlyOwner {
        sellPrice = newSellPrice;
        buyPrice = newBuyPrice;
    }

これは、新しい価格への変更に毎回トランザクションを発行し、少しのehterを消費する必要があるため、頻繁に変更されない価格であれば受け入れられます。浮動価格を一定に保ちたい場合は、標準のデータフィードを調査することをオススメします。

次のステップは、 buy関数とsell関数を作成することです。

    function buy() payable returns (uint amount){
        amount = msg.value / buyPrice;                    // calculates the amount
        require(balanceOf[this] >= amount);               // checks if it has enough to sell
        balanceOf[msg.sender] += amount;                  // adds the amount to buyer's balance
        balanceOf[this] -= amount;                        // subtracts amount from seller's balance
        Transfer(this, msg.sender, amount);               // execute an event reflecting the change
        return amount;                                    // ends function and returns
    }

    function sell(uint amount) returns (uint revenue){
        require(balanceOf[msg.sender] >= amount);         // checks if the sender has enough to sell
        balanceOf[this] += amount;                        // adds the amount to owner's balance
        balanceOf[msg.sender] -= amount;                  // subtracts the amount from seller's balance
        revenue = amount * sellPrice;
        require(msg.sender.send(revenue));                // sends ether to the seller: it's important to do this last to prevent recursion attacks
        Transfer(msg.sender, this, amount);               // executes an event reflecting on the change
        return revenue;                                   // ends function and returns
    }

これは新しいトークンを発行するのではなく、コントラクトの所有する残高を変更するのだということに注意してください。コントラクトはトークンとetherの両方を保持しており、コントラクトのオーナーは価格を設定したり、ある場合には新規トークンを発行することができますが、銀行(コントラクト)のトークンやエーテルに手を付けることはできません。このコントラクトが資産を移動する唯一の方法は、それらを売買することです。

注意 売買の"価格"はetherではなく、 wei というシステム上の最小通貨(ユーロやドルにおけるセント、Bitcoinにおけるsatoshiのようなもの)で設定されている。1ether=1000000000000000000weiです。トークンのether価格を設定する際は、0を18個つけましょう。

コントラクトを生成するとき、市場に出回っているトークンを全て買い戻せるように十分な量のetherを送信しましょう。そうしないと、あなたのコントラクトは支払い不能になり、ユーザーがあなたのトークンを売ることができなくなるかもしればせん。

先程の例では、単一の中央売買者によってコントラクトを説明していました。しかし、より興味深いコントラクトでは、誰もが異なる価格で取引できるような市場を可能にしたり、外部の情報源から直接価格を読み込んだりするでしょう。

自動補充

Ethereum上でトランザクションを作成するときはいつでも、スマートコントラクトの結果から計算された料金をマイナーに支払う必要があります。将来変更される可能性がありますが、現在はマイナーへの支払いはether以外ではできないため、あなたのトークンを利用するすべてのユーザーにetherが必要となります。残高が支払い額に満たないアカウントのトークンは、オーナーが必要な手数料を支払うことができるまでスタックされています。しかし実用的なケースでは、あなたはユーザー達にethereumやブロックチェーン、etherの取得方法を意識させたくはないでしょう。そこで可能なアプローチは、残高が非常に低いことを検出するとすぐに、コインが自動的にユーザーの残高を補充するというものです。

これを行うには、まず閾値を保存する変数と、それを変更する関数を作成する必要があります。特に値に気を使わないのであれば、5 finney(0.005ether) に設定しておいてください。

    uint minBalanceForAccounts;

    function setMinBalance(uint minimumBalanceInFinney) onlyOwner {
         minBalanceForAccounts = minimumBalanceInFinney * 1 finney;
    }

それから、送信者が払い戻しを受けられるようにtransfer関数に以下のコードを追加してください。

    /* Send coins */
    function transfer(address _to, uint256 _value) {
        ...
        if(msg.sender.balance < minBalanceForAccounts)
            sell((minBalanceForAccounts - msg.sender.balance) / sellPrice);
    }

また、上の変更の代わりに、送信者が受信者に対して料金を支払うように変更することでもこれを実現できます。

    /* Send coins */
    function transfer(address _to, uint256 _value) {
        ...
        if(_to.balance<minBalanceForAccounts)
            _to.send(sell((minBalanceForAccounts - _to.balance) / sellPrice));
    }

これは、マイナーへの支払いに必要なetherに残高が満たないアカウントがトークンを受け取ることがないことを保証します。

Proof Of Work

改善されたコイン

コインコードの完全版

全ての発展的なオプションも追加した最終的なコードは、以下のようになります。

pragma solidity ^0.4.16;

contract owned {
    address public owner;

    function owned() public {
        owner = msg.sender;
    }

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

    function transferOwnership(address newOwner) onlyOwner public {
        owner = newOwner;
    }
}

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

contract TokenERC20 {
    // Public variables of the token
    string public name;
    string public symbol;
    uint8 public decimals = 18;
    // 18 decimals is the strongly suggested default, avoid changing it
    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);

    /**
     * Constrctor function
     *
     * Initializes contract with initial supply tokens to the creator of the contract
     */
    function TokenERC20(
        uint256 initialSupply,
        string tokenName,
        string tokenSymbol
    ) public {
        totalSupply = initialSupply * 10 ** uint256(decimals);  // Update total supply with the decimal amount
        balanceOf[msg.sender] = totalSupply;                // Give the creator all initial tokens
        name = tokenName;                                   // Set the name for display purposes
        symbol = tokenSymbol;                               // Set the symbol for display purposes
    }

    /**
     * Internal transfer, only can be called by this contract
     */
    function _transfer(address _from, address _to, uint _value) internal {
        // Prevent transfer to 0x0 address. Use burn() instead
        require(_to != 0x0);
        // Check if the sender has enough
        require(balanceOf[_from] >= _value);
        // Check for overflows
        require(balanceOf[_to] + _value > balanceOf[_to]);
        // Save this for an assertion in the future
        uint previousBalances = balanceOf[_from] + balanceOf[_to];
        // Subtract from the sender
        balanceOf[_from] -= _value;
        // Add the same to the recipient
        balanceOf[_to] += _value;
        Transfer(_from, _to, _value);
        // Asserts are used to use static analysis to find bugs in your code. They should never fail
        assert(balanceOf[_from] + balanceOf[_to] == previousBalances);
    }

    /**
     * Transfer tokens
     *
     * Send `_value` tokens to `_to` from your account
     *
     * @param _to The address of the recipient
     * @param _value the amount to send
     */
    function transfer(address _to, uint256 _value) public {
        _transfer(msg.sender, _to, _value);
    }

    /**
     * Transfer tokens from other address
     *
     * Send `_value` tokens to `_to` in behalf of `_from`
     *
     * @param _from The address of the sender
     * @param _to The address of the recipient
     * @param _value the amount to send
     */
    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;
    }

    /**
     * Set allowance for other address
     *
     * Allows `_spender` to spend no more than `_value` tokens in your behalf
     *
     * @param _spender The address authorized to spend
     * @param _value the max amount they can spend
     */
    function approve(address _spender, uint256 _value) public
        returns (bool success) {
        allowance[msg.sender][_spender] = _value;
        return true;
    }

    /**
     * Set allowance for other address and notify
     *
     * Allows `_spender` to spend no more than `_value` tokens in your behalf, and then ping the contract about it
     *
     * @param _spender The address authorized to spend
     * @param _value the max amount they can spend
     * @param _extraData some extra information to send to the approved contract
     */
    function approveAndCall(address _spender, uint256 _value, bytes _extraData)
        public
        returns (bool success) {
        tokenRecipient spender = tokenRecipient(_spender);
        if (approve(_spender, _value)) {
            spender.receiveApproval(msg.sender, _value, this, _extraData);
            return true;
        }
    }

    /**
     * Destroy tokens
     *
     * Remove `_value` tokens from the system irreversibly
     *
     * @param _value the amount of money to burn
     */
    function burn(uint256 _value) public returns (bool success) {
        require(balanceOf[msg.sender] >= _value);   // Check if the sender has enough
        balanceOf[msg.sender] -= _value;            // Subtract from the sender
        totalSupply -= _value;                      // Updates totalSupply
        Burn(msg.sender, _value);
        return true;
    }

    /**
     * Destroy tokens from other ccount
     *
     * Remove `_value` tokens from the system irreversibly on behalf of `_from`.
     *
     * @param _from the address of the sender
     * @param _value the amount of money to burn
     */
    function burnFrom(address _from, uint256 _value) public returns (bool success) {
        require(balanceOf[_from] >= _value);                // Check if the targeted balance is enough
        require(_value <= allowance[_from][msg.sender]);    // Check allowance
        balanceOf[_from] -= _value;                         // Subtract from the targeted balance
        allowance[_from][msg.sender] -= _value;             // Subtract from the sender's allowance
        totalSupply -= _value;                              // Update totalSupply
        Burn(_from, _value);
        return true;
    }
}

/******************************************/
/*       ADVANCED TOKEN STARTS HERE       */
/******************************************/

contract MyAdvancedToken is owned, TokenERC20 {

    uint256 public sellPrice;
    uint256 public buyPrice;

    mapping (address => bool) public frozenAccount;

    /* This generates a public event on the blockchain that will notify clients */
    event FrozenFunds(address target, bool frozen);

    /* Initializes contract with initial supply tokens to the creator of the contract */
    function MyAdvancedToken(
        uint256 initialSupply,
        string tokenName,
        string tokenSymbol
    ) TokenERC20(initialSupply, tokenName, tokenSymbol) public {}

    /* Internal transfer, only can be called by this contract */
    function _transfer(address _from, address _to, uint _value) internal {
        require (_to != 0x0);                               // Prevent transfer to 0x0 address. Use burn() instead
        require (balanceOf[_from] > _value);                // Check if the sender has enough
        require (balanceOf[_to] + _value > balanceOf[_to]); // Check for overflows
        require(!frozenAccount[_from]);                     // Check if sender is frozen
        require(!frozenAccount[_to]);                       // Check if recipient is frozen
        balanceOf[_from] -= _value;                         // Subtract from the sender
        balanceOf[_to] += _value;                           // Add the same to the recipient
        Transfer(_from, _to, _value);
    }

    /// @notice Create `mintedAmount` tokens and send it to `target`
    /// @param target Address to receive the tokens
    /// @param mintedAmount the amount of tokens it will receive
    function mintToken(address target, uint256 mintedAmount) onlyOwner public {
        balanceOf[target] += mintedAmount;
        totalSupply += mintedAmount;
        Transfer(0, this, mintedAmount);
        Transfer(this, target, mintedAmount);
    }

    /// @notice `freeze? Prevent | Allow` `target` from sending & receiving tokens
    /// @param target Address to be frozen
    /// @param freeze either to freeze it or not
    function freezeAccount(address target, bool freeze) onlyOwner public {
        frozenAccount[target] = freeze;
        FrozenFunds(target, freeze);
    }

    /// @notice Allow users to buy tokens for `newBuyPrice` eth and sell tokens for `newSellPrice` eth
    /// @param newSellPrice Price the users can sell to the contract
    /// @param newBuyPrice Price users can buy from the contract
    function setPrices(uint256 newSellPrice, uint256 newBuyPrice) onlyOwner public {
        sellPrice = newSellPrice;
        buyPrice = newBuyPrice;
    }

    /// @notice Buy tokens from contract by sending ether
    function buy() payable public {
        uint amount = msg.value / buyPrice;               // calculates the amount
        _transfer(this, msg.sender, amount);              // makes the transfers
    }

    /// @notice Sell `amount` tokens to contract
    /// @param amount amount of tokens to be sold
    function sell(uint256 amount) public {
        require(this.balance >= amount * sellPrice);      // checks if the contract has enough ether to buy
        _transfer(msg.sender, this, amount);              // makes the transfers
        msg.sender.transfer(amount * sellPrice);          // sends ether to the seller. It's important to do this last to avoid recursion attacks
    }
}

デプロイ

次のステップ

ここまで、Ethereum上でトークンの発行をする手順の全てを学んできました。しかし、そのトークンを使っていったい何ができるのでしょうか?これはほんの一例ですが、会社の株式の代替としたり、インフレ管理のために新規コインを発行するときの投票を行う中央委員会として利用したりできます。また、クラウドセールを介して資金調達を行うためにも利用できます。さて、あなたは次になにを作りますか?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment