【Web3.0】ブロックチェーン技術入門② ~自動販売機のスマートコントラクト~


<h2>Solidityとは</h2>
Solidity は、スマートコントラクトを記述するためのプログラミング言語です。JavaScriptに似た文法を持つ高レベルの静的型付け言語であり、Ethereum Virtual Machine (EVM) で実行されるバイトコードにコンパイルされます。

<h2>スマートコントラクトとは</h2>
スマートコントラクトは、コンピュータープログラムの形での契約です。これはブロックチェーン上で実行され、プログラムによって定義されたルールに従って自動的にトランザクションを実行することができます。

スマートコントラクトの主な特徴は以下の通りです。

1. 自動化
   事前に定義されたルールや条件に基づいて、自動的に実行されます。

2. 信頼性
   スマートコントラクトはブロックチェーン上で実行されるため、一度デプロイされると、内容の改ざんや削除は非常に困難です。

3. 透明性
 使用されるブロックチェーンに応じて、スマートコントラクトのコードやトランザクション履歴は公開され、誰でも閲覧することができます。

4. 中央の仲介者不要: スマートコントラクトは自動的に動作するため、従来の契約や取引で必要だった中央の仲介者や第三者が不要になります。
例として、ある人Aが人BにEtherを送るという条件でスマートコントラクトを作成した場合、その条件が満たされると、スマートコントラクトは自動的にEtherを人Bに送付します。

Solidityは、このようなスマートコントラクトをEthereumブロックチェーン上で効率的に実行するための言語として設計されました。スマートコントラクトの普及とともに、Solidityも多くの開発者にとってEthereumの主要な開発ツールとなっています。

<h2>自動販売機のスマートコントラクト</h2>

今回作成する機能は、仮想的な自動販売機を模したEthereumスマートコントラクトです。

<h3>システムの概要</h3>
ユーザーはEtherを支払ってジュースを購入でき、所有者は在庫の追加やジュースの価格変更ができます。
<h4>ユーザー</h4>
Etherを使ってこの仮想的な自動販売機でジュースを購入できます。具体的には、ユーザーはコントラクトに定められたジュースの価格分のEtherを送金することで、ジュースを1本購入することができます。購入が成功すると、在庫のジュースの数が1本減少します。
<h4>所有者</h4>
所有者は特別な権限を持っており、以下の2つの操作が行えます。

1. 在庫追加
   所有者は、ジュースの在庫が減少した際や品切れの際に、新たにジュースを追加することができます。例えば、restock関数を使って10本追加すると、在庫数が10本増えます。

2. 価格変更
  ジュースの価格は変動するかもしれません。市場の状況や原材料の価格変動など、様々な要因によって価格を調整したい場面が考えられます。この自動販売機の所有者は、changePrice関数を使ってジュースの価格を上げたり下げたりすることができます。例えば、1 etherから1.5 etherに価格を上げることも可能です。

このように、一般のユーザーはジュースを購入するだけの操作ができるのに対し、所有者は在庫や価格に関する管理操作ができるという特徴があります。

<h2>実装</h2>

今回は自動販売機のスマートコントラクトの簡単な実装をしていきたいと思います。
Ethereum IDEのRemixというツールを使って実装をしていきたいと思います。

https://remix.ethereum.org/

まずは、URLを開き、以下の添付画像のように、「vendingMachine.sol」という名前を作成します。拡張子がsolなのは、Solidityの略です。

![スクリーンショット 2023-10-12 14.49.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2782718/58320bf0-fa61-e842-f931-7c868f4c0313.png)

「vendingMachine.sol」に以下のコードを入力してください。
コメントに処理を記載しています。

```solidity:vendingMachine.sol
//ライセンス情報の指定
// SPDX-License-Identifier: MIT
//Solidityのバージョン指定
pragma solidity ^0.8.0;

//自動販売機のスマートコントラクト
contract VendingMachine {
    //自動販売機の所有者のアドレス
    address payable public owner;
    //ジュースの価格(デフォルトでは1ether)
    uint public juicePrice = 1 ether; 
    //ジュースの在庫数(デフォルトでは5本)
    uint public stockCount = 5; 
     // ジュース購入時に起こるイベント
    event Purchase(address indexed buyer, uint amount);
    //デプロイ時に呼び出されるコンストラクタ
    constructor() {
        //デプロイした人を所有者として設定する
        owner = payable(msg.sender);
    }
    //指定された金額と送られて金額が一致するかをチェックする
    modifier costs(uint _amount) {
        require(msg.value == _amount, unicode"無効な金額が送金されました");
        _;
    }
    // 在庫があるかどうかをチェックする修飾子
    modifier inStock() {
        require(stockCount > 0, unicode"在庫がなくなりました");
        _;
    }
    // ジュースを購入する関数
    function purchaseJuice() public payable costs(juicePrice) inStock {
        // 在庫数を1本減らす
        stockCount -= 1;
        // ジュースの代金を自動販売機の所有者に送金
        owner.transfer(msg.value);
        // 購入イベントを発火
        emit Purchase(msg.sender, msg.value);
    }
    // 在庫を追加する関数（所有者のみ実行可能）
    function restock(uint _amount) public {
        // この関数を呼び出した人が所有者であるかチェック
        require(msg.sender == owner, unicode"所有者のみ在庫を補充することができます。");
        stockCount += _amount;
    }
    // ジュースの価格を変更する関数（所有者のみ実行可能）
    function changePrice(uint _newPrice) public {
        // この関数を呼び出した人が所有者であるかチェック
        require(msg.sender == owner, unicode"所有者のみ価格を変更することができます。");
         // ジュースの価格を更新
        juicePrice = _newPrice;
    }
}
```

<h2>操作方法</h2>
次に、Remixの操作手順について説明をしていきます。

1. コンパイル

    最初に「VendingMachine」コントラクトをコンパイルします。
    
    ![スクリーンショット 2023-10-12 15.37.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2782718/ee031181-7289-5b7e-47a6-e105a442b21a.png)

2. デプロイ

    次に「VendingMachine」コントラクトをデプロイします。デプロイボタン（青色のボタン）をクリックして、トランザクションを確認します。
    
    ![スクリーンショット 2023-10-12 15.41.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2782718/634bb0c8-7559-07cf-7a11-d33b102fe63a.png)


3. ジュースの在庫と価格と所有者を確認
    
    juicePriceボタン
    ジュースの現在の価格を確認できます。
    juicePriceの戻り値が0x1d4c5ce2d386f254917e84e4e9493f39138となっています。ただし、実際には関心を持つ部分は、uint256の値として表示されている部分です。その値は1000000000000000000です。
    EtherはWeiで表されます。1 Ether = 10^18 Weiです。そのため、juicePriceが1000000000000000000 Weiと表示されていることは、これが1 Etherであることを意味します。

    ![スクリーンショット 2023-10-12 15.48.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2782718/1d829545-e42e-9d2e-9a57-015e40889e3b.png)

    ownerボタン
    自動販売機の所有者を確認できます。つまり、スマートコントラクトをデプロイしたアカウントのEthereumアドレスを示しています。ownerボタンをクリックすると、VendingMachineスマートコントラクトの所有者のEthereumアドレスが取得され、そのアドレスが画面上に表示されます。

    ![スクリーンショット 2023-10-12 15.50.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2782718/197d2bcc-526b-fd76-d09c-7f4374afafd6.png)

    stockCountボタン
    ジュースの現在の在庫数を確認できます。stockCountボタンをクリックすると、VendingMachineスマートコントラクトのジュースの現在の在庫数が取得され、その数値が画面上に表示されます。![スクリーンショット 2023-10-12 15.58.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2782718/10a8d0ff-2c03-1a2b-023e-8b008689942f.png)

4. ジュースを購入
では、まず、ACCOUNTを所有者以外のアカウントに設定をし、VALUEを1にし、Etherと選択しましょう。次に、purchaseJuice ボタンをクリックして、ジュースを購入します。![スクリーンショット 2023-10-12 16.01.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2782718/93a58466-6ead-1225-6a82-052ea1e91f22.png)
stockCountを見ると、4に変更されていることがわかります。![スクリーンショット 2023-10-12 16.02.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2782718/1ae5af64-0ac7-d1b9-2be0-405825286116.png)


5. 在庫を補充
では、所有者アカウントに戻って、restockボタンを押し、ジュースを補充しましょう。10を追加し、stockCountを押すと、14になっていることがわかります。![スクリーンショット 2023-10-12 16.04.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2782718/0f83d642-a75a-14cf-a8b7-76a80b8a81a7.png)


6. ジュースの価格を変更
  changePriceで価格を変更すると、juicePriceボタンをクリックすると、値が変わっていることがわかります。そこで、purchaseJuiceボタンをクリックすると、2etherで購入できました。![スクリーンショット 2023-10-12 16.29.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2782718/29eb21f0-8f33-e48e-6583-a7bf888c9932.png)




以上の手順で、このシミュレーションされた自動販売機コントラクトが実現できます。