FlutterでDApp開発 その3（野良パッケージ化）

## はじめに

https://qiita.com/hhatto/items/13f8313b6f5aa8a00d15

昨日はDev Protocolのコントラクト実行を試してみましたが、今日はその処理をパッケージ化して使ってみたいと思います。
いずれは[Dev ProtocolのJavaScript/TypeScriptのインターフェースであるdev-kit-js](https://github.com/dev-protocol/dev-kit-js)のDart実装のようなものを提供できればなと考えています。

パッケージ化に当たっては、[web3dart](https://pub.dev/packages/web3dart)を参考にさせていただきました。

## 実装

### パッケージ化の準備

まず昨日までに実装したDev Protocolのステーキング数を取得する処理をパッケージ化します。

`devkit.dart`フォルダを作成し、プロジェクトを開始します。
（`dart create`コマンドでも作成できるみたいなのですが、今回は小さいプロジェクトのなの何もない状態から作成していきます。）

まずはパッケージの情報や依存パッケージの定義を行う `pubspec.yaml`を作成します。

```yaml:pubspec.yaml
name: devkit
description: (Unofficial) Dart library to Dev Protocol Interface
version: 0.0.1
homepage: https://github.com/hhatto/devkit.dart

environment:
  sdk: '>=2.12.0 <3.0.0'

dependencies:
  web3dart: ^2.3.3
```

見様見真似なので、sdkバージョンの違いとかまだよく分かってない部分が多々あります。

これもよく分かってないですが、パッケージの依存解決するために `dart pub get`しておきました。

```shell-session
$ dart pub get
```

これで`pubspec.lock`作成されるのですが、他の言語とかだとlock系のファイルは保存しなかったりすると思うのですが、Dartはどうなんでしょうか。
一旦ファイルは作っておきます。（`.gitignore`とかに追加しておくのかな...？）

これでパッケージ作成の準備は整いました。

### パッケージの処理実装

先にディレクトリ構成だけ記載しておきます。

```
$ tree -L 4
.
├── lib
│   ├── devkit.dart
│   └── src
│       ├── abi
│       │   ├── lockup.dart
│       │   └── registry.dart
│       └── core.dart
├── pubspec.lock
└── pubspec.yaml

3 directories, 6 files
```

`lib` ディレクトリ以下を少し説明しておきます。

* `lib/devkit.dart` はパッケージの定義やimport/exportを管理したりする最小限のファイルです。（別にここに処理書いてもいいとは思いますが、web3dartに合わせました。）
* `lib/src/core.dart` はコントラクト実行等を行うメインの処理
* `lib/src/abi/...` はコントラクト用のABIの定義（昨日の記事で書いたものと同じです。）

まず`lib/src/devkit.dart`は以下のような感じです。

```dart:lib/devkit.dart
library devkit;

export 'src/core.dart';
```

`library` でライブラリ(パッケージ)定義してるのと、`src/core.dart`の処理をエクスポートして、パッケージ使用者側から使えるようにしています。

次に`lib/src/core.dart`は以下のような感じです。
処理自体は昨日実装したものをほぼ転用した形です。

```dart:lib/src/core.dart
import 'package:http/http.dart';
import 'package:web3dart/web3dart.dart';

import './abi/lockup.dart';
import './abi/registry.dart';

class DevKitClient {
    DevKitClient(String apiUrl) {
        var httpClient = new Client();
        var ethClient = new Web3Client(apiUrl, httpClient);
        _client = ethClient;
    }

    late final Web3Client _client;

    registryContractFactory() {
        EthereumAddress contractAddress = EthereumAddress.fromHex('0x1D415aa39D647834786EB9B5a333A50e9935b796');
        final contract = DeployedContract(ContractAbi.fromJson(registryAbi, 'registry'), contractAddress);
        return {
            'lockup': () {
                return _client.call(contract: contract, function: contract.function('lockup'), params: []);
            }
        };
    }

    lockupContractFactory(String lockupContractAddress) {
        EthereumAddress contractAddress = EthereumAddress.fromHex(lockupContractAddress);
        final contract = DeployedContract(ContractAbi.fromJson(lockupAbi, 'lockup'), contractAddress);
        return {
            'getPropertyValue': (String propertyAddress) {
                EthereumAddress address = EthereumAddress.fromHex(propertyAddress);
                return _client.call(contract: contract, function: contract.function('getPropertyValue'), params: [address]);
            }
        };
    }
}
```

`DevKitClient`にEthereumプロバイダ用のURLを渡しつつ、クライアント生成して、各コントラクトの処理を呼び出す感じです。Dart語彙力低いので、ここもよくわからず実装している感じはあります。

一応パッケージの実装はこれで終わりです。

### Flutter側から呼び出す

今回は[pub.dev](https://pub.dev/)やGitHubにもあげていないので、ローカル環境のファイルパスを指定して、使います。

`pubspec.yaml`に対象となるディレクトリのファイルパスを指定して、`flutter pub get`します。

```diff
diff --git a/pubspec.yaml b/pubspec.yaml
index 1de5307..23d2968 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -35,6 +35,8 @@ dependencies:
   # Use with the CupertinoIcons class for iOS style icons.
   cupertino_icons: ^1.0.2
   web3dart: ^2.3.3
+  devkit:
+    path: /Users/hattori/work/devkit.dart

 dev_dependencies:
   flutter_test:
```

```shell-session
$ flutter pub get
```

`pubspec.lock`にも反映されました。これで使用可能になります。

```diff
diff --git a/pubspec.lock b/pubspec.lock
index 536017b..730c4fe 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -141,6 +141,13 @@ packages:
       url: "https://pub.dartlang.org"
     source: hosted
     version: "2.2.1"
+  devkit:
+    dependency: "direct main"
+    description:
+      path: "/Users/hattori/work/devkit.dart"
+      relative: false
+    source: path
+    version: "0.0.1"
   fake_async:
     dependency: transitive
     description:
```

アプリのメインの処理がある`lib/main.dart`を以下のように変更しました。

```diff
diff --git a/lib/main.dart b/lib/main.dart
index de3c27b..4f10121 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -1,6 +1,7 @@
 import 'package:flutter/material.dart';
 import 'package:http/http.dart';
 import 'package:web3dart/web3dart.dart';
+import 'package:devkit/devkit.dart';

 import 'abi/lockup.dart';
 import 'abi/registry.dart';
@@ -39,21 +40,16 @@ class _MyHomePageState extends State<MyHomePage> {

   void getBalance(String address) async {
     var apiUrl = "http://localhost:8545";
-    var httpClient = new Client();
-    var ethClient = new Web3Client(apiUrl, httpClient);
+    var client = new DevKitClient(apiUrl);

     EthereumAddress propertyAddress = EthereumAddress.fromHex(address);

-    EthereumAddress registryContractAddress = EthereumAddress.fromHex('0x1D415aa39D647834786EB9B5a333A50e9935b796');
-    final registryContract =
-      DeployedContract(ContractAbi.fromJson(registryAbi, 'registry'), registryContractAddress);
-
-    final lockupContractAddresses = await ethClient.call(contract: registryContract, function: registryContract.function('lockup'), params: []);
-    print(lockupContractAddresses);
-    final lockupContract =
-      DeployedContract(ContractAbi.fromJson(lockupAbi, 'lockup'), lockupContractAddresses[0]);
-    final lockupValue = await ethClient.call(contract: lockupContract, function: lockupContract.function('getPropertyValue'), params: [propertyAddress]);
-
+    final registryContract = client.registryContractFactory();
+    final lockupContractAddress = await registryContract['lockup']();
+    print(lockupContractAddress);
+    final lockupContract = client.lockupContractFactory(lockupContractAddress[0].toString());
+    final lockupValue = await lockupContract['getPropertyValue'](propertyAddress.toString());
+    print(lockupValue);
     _balance = (lockupValue[0] / BigInt.from(10).pow(18)).toDouble();
   }

```

これで実行すると昨日と同じプロパティのステーク数が表示されるようになります。

## おわりに

最小限の処理をパッケージ化し、Flutter側から使ってみました。
devkit.dart拡充したいですね。まずは冬休みにDart勉強してDart力を高めようと思います。

Dart/Flutter、サクサク開発できておもしろいです!!