C#でethereumのクライアントを実装する

# はじめに
Nethereumを使って、C#でethereumのクライアントを作成します。
Quorumのクライアントも同様に作成できます。

https://nethereum.com

以下の手順は、Macで確認しました。Windowsでも基本的に同じだと思います。
また、リモートから開発する場合は、以下の記事を参考にしてください。

https://qiita.com/akira_kawahara/items/6c57ad7d07e65fdcdb85

# 用意するもの
以下の環境を事前に準備してください。

1. .Net Core3.1 SDK
1. VS Code

（NuGetしたnethereum3.8.0は、.Net 5に対応していませんでした。）

:::note warn
2021年前半に書いたので内容的に古くなっている可能性があります。
:::

# スマートコントラクトのコンパイルとC#コードの生成
VS Codeと.Net Core3.1を使用して、ethereumのクライアントを作成します。
スマートコントラクトアクセス用のC#のコードを生成する方法は、以下の記事を参考にしました。

https://docs.nethereum.com/en/latest/nethereum-codegen-vscodesolidity/

生成されたC#のコードを.Net Frameworkで利用することもできます。


## 拡張機能のインストール
VS Codeで、拡張機能のsolidityをインストールします。
似た名前の拡張がいくつかあるので気をつけてください。
![スクリーンショット 2021-05-07 185307.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/613072/9b55c7f9-9c20-ad11-03ba-7b2a758b7348.png)
## フォルダの作成
以下のようにフォルダを作成してから、VS CodeでEthereumSmartContractsフォルダを開きます。すでにフォルダを開いている場合は、一旦閉じで開き直します。このフォルダがカレントパスになり、ファイルが生成されます。出力するフォルダが設定できないのが少し不便です。
Contractsフォルダに、Solidityで記述したスマートコントラクトのsolファイルを配置します。


```
NethereumCodeGenDemo/
├ EthereumSmartContracts/
  ├ Contracts/
    | SimpleStorage.sol
```

## 設定ファイルの生成
VS CodeのView->Command Paletteから
Solidity:Create 'nethereum-gen.settings' with default values at root
を選択し、C#のコード生成の設定ファイルを生成します。

![スクリーンショット 2021-05-07 190403.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/613072/e4d7a07b-343a-1962-afe4-014d08b745e9.png)
projectNameとnamespaceは、C#のコード生成時に使用されます。作成するプロジェクトに応じて変更してください。ここでは、projectPathをCSharpに変更しています。

```:nethereum-gen.setting
{
    "projectName": "EthereumSmartContracts",
    "namespace": "EthereumSmartContracts.Contracts",
    "lang": 0,
    "autoCodeGen": true,
    "projectPath": "./CSharp"
}
```
## コンパイルとコード生成
solファイルを選択した状態で、VS CodeのView->Command Paletteから
Solidity:Compile and Code generate CSharp contract definition
を選択し、solファイルのコンパイルとC#のコード生成を行います。
Contractsフォルダに、スマートコントラクトをコンパイルした結果、CSharpフォルダにC#のコードがそれぞれ作成されます。
![スクリーンショット 2021-05-10 10.44.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/613072/78e056fa-3d9d-4bf4-84f7-53b9235fe74b.png)

## ターゲットフレームワークの修正
TargetFrameworkを確認して、netcoreapp3.1でなければ変更します。

```xml:EthereumSmartContracts.csproj
<Project Sdk="Microsoft.NET.Sdk">

    <PropertyGroup>
        <TargetFramework>netcoreapp3.1</TargetFramework>     
    </PropertyGroup>

    <ItemGroup>
        <PackageReference Include = "Nethereum.Web3" Version="3.8.0" />
    </ItemGroup>
    
</Project>
```

また、複数のバージョンの.Netがインストールされている場合は、global.jsonをNethereumCodeGenDemoフォルダに置きます。

```json:global.json
{
    "sdk": {
      "version": "3.1"
    }
}
```

コード生成時に追加されるNethereum.Web3のバージョンが古いので最新に更新します。

```bash
dotnet add package Nethereum.Web3
```
# テストプロジェクトの作成

ターミナルでNethereumCodeGenDemoフォルダに移動して、以下のコマンドを実行します。

```bash
ソリューションファイルを作成します
dotnet new sln --name NethereumCodeGenDemo

テスト用のコンソールアプリプロジェクトを作成します。
dotnet new console --name SimpleStorageConsole

ソリューションにプロジェクトを追加します。
dotnet sln add ./SimpleStorageConsole/SimpleStorageConsole.csproj
dotnet sln add ./EthereumSmartContracts/EthereumSmartContracts.csproj

cd SimpleStorageConsole

Nethereum.Web3のパッケージを追加します。
dotnet add package Nethereum.Web3

生成されたスマートコントラクトのC#クラスを参照する設定を行います。
dotnet add reference ../EthereumSmartContracts/CSharp/EthereumSmartContracts.csproj
```

## テストコードの追加
スマートコントラクトから値を読み込むコードをProgram.csに追加します。

```cs:Program.cs
using QuorumSample.Contracts.simplestorage;
using Nethereum.Web3;
using System;
using System.Threading.Tasks;

class Program
{
    static void Main(string[] args)
    {
        Demo().Wait();
    }

    static async Task Demo()
    {
        try
        {
            // アクセスするノードのIP
            var url = "http://127.0.0.1:8545";
            var web3 = new Web3(url);
            //スマートコントラクトのアドレス
            var contractAddress = "0x0123456789ABCDEF0123456789ABCDEFF0123456";
            //生成されたスマートコントラクトのクラス
            var service = new SampleService(web3, contractAddress);
            // スマートコントラクトのget()メソッドで値を取得します
            var currentStoredValue = await service.GetQueryAsync();
            Console.WriteLine($"Contract has value stored: {currentStoredValue}");
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.ToString());
        }

        Console.WriteLine("Finished");
        Console.ReadLine();
    }
}
```

## 実行
VS CodeのRun and Debugで実行します。

# まとめ

Netereumを使って、C#からスマートコントラクトにアクセスすることができました。スマートコントラクトからC#のコードを生成する必要があるので少し面倒です。

:::note warn
Quorumの場合は、書き込みの際注意が必要です。

Raftを使用するとタイムスタンプの単位がmsになってうまくいかないためIBFTを使用します。
nethereumでは、プロトコルはEIP1559がデフォルトになっているので、エラーになる場合はレガシーモードに設定します。
書き込みについては、別の記事で説明したいと思います。
:::