簡単なスマートコントラクトを書いて、Polygonネットワークにデプロイ、UIをHTMLに記述しブラウザからアクセス、というのをやってみました。
最初なのでなるべく利用するツールを少なくする方向で方法を探しました。
スマートコントラクトの作成にはRemixというIDEを利用しました。Remixはインストール不要でブラウザ上でSolidiy言語でスマートコントラクトを作成、デプロイが可能です。スマートコントラクトの開発にはtruffle、hardhatといった統合ツールを利用するのが一般的のようですが簡単なスマートコントラクトをお試しするにはまずRemixを使うのが簡単らしいのでそうしました。実際簡単でした。
デプロイ先のネットワークはPolygonにしました。イーサリアムだとガス代が何万円もかかるので。それでも何千円とかかかるのかと思ってましたが短いスマートコントラクトのデプロイにかかったがガス代は10円以下でした。
呼び出し側のUIはethers.jsというJavascirptライブラリを利用し、メタマスク経由でアクセスしました。ローカルファイルからだとメタマスクが呼び出されないので、作成したHTMLファイルはWebサーバ上に置く必要があります。UIは、ちゃんとしたアプリの場合、ReactやVue(よく知らない)といったUIフレームワークを利用するらしいですが、HTMLファイルにテキストエディタでちょろっとJavaScriptを書くだけのシンプルなやり方をまずはお試ししてみました。
作ったもの
- スマートコントラクト MyStrage.sol
- クライアント1(ethers.js) mystrage.html
- クライアント2(web3.py) sample1.py
スマートコントラクトの作成とデプロイ
はじめてのGeth
今回の最終目的の”Polygonにデプロイしてブラウザからアクセス”には必要はなかったですが、仕組みの理解のために、ローカルでイーサリアム環境を構築するGeth(Go Etherium)というのをまず使ってみました。
以下の記事を参考に、Gethをインストールして、マイニングしたり送金したり。Solidityコンパイラもダウロード、テキストエディタでSolidtyを書いてコマンドラインでコンパイル、Gethコンソール?上から呼び出しをしてみました。
ステップバイステップの手順が書いておりとても分かりやすく、また最近の記事なのでバージョンが上がってAPIが変わって動かない、といったこともありませんでした。
Windowsの場合は、アタッチするipcファイルの引数はデフォルトで以下のようになっていました。
geth attach \.\pipe\geth.ipc
あと、なんかの表示で”アクセス権がありません”という表示されてOS再起動してもなおらなくなったのでGeth自体を再インストールしました。初心者のうちは解決する知識もないですし、テスト環境的なものは何かおかしくなったら全部消してやり直した方が早いと思います。
Remixの利用
次にRemixを利用してみました。
起動するとcontractsの下に最初からサンプルが3つ入ってます。これのStrage.solというのがHelloWorld的な簡単サンプルです。上のブログ記事のNumberRegister.sol的なもの。
これをデフォルトのローカル環境で、コンパイルして、デプロイして、Remix上からデプロイしたコントラクトを呼び出すことができます。
↓Storage.solが数字を取得、保存するだけの簡単サンプル
↓コンパイル。DetailsからABIをコピーできる。
↓デプロイも簡単。デプロイした後コントラクトを呼び出せます。
今回はStorage.solの数字を文字列にしただけのMyStorage.solというファイルを作って、それをデプロイして試していきました。ソースコード→ MyStrage.sol
Gethローカル環境にデプロイ(Web3 Provider)
次にRemixのデプロイ先をデフォルトの組み込み環境から、ローカルで実行しているGethに切り替えてみました。
Geth
Remixから接続できるように起動オプションを指定してGethを起動します。以下は起動オプションの例です。
geth --networkid "1234" --nodiscover --datadir . --http --http.corsdomain * --http.api web3,eth,debug,personal,net --vmdebug --allow-insecure-unlock
参考
- Geth – JSON-RPC Server
- Remix – Deploy and Run – More about Web3 Provider
1234は私が適当に設定したネットワークIDなので自分のGethで指定したIDに置き換えてください。RPCエンドポイントのオプションは昔は –rpc~というコマンドラインオプションだったらしいですが、–http~に変わっています。
httpアクセスだとアカウントロック解除ができず”Fatal: Account unlock with HTTP access is forbidden!”というエラーがでるので”–allow-insecure-unlock”オプションをつけています(参考)。
–http.corsdomainは*はセキュリティ上ガバガバらしいですが、まぁ家のLAN上のローカルPCのテスト環境なのでガバガバでもいいでしょうということで、ワイルドカードにしています。
Gethのデフォルトのhttpアドレスは127.0.01、ポートは8545になっているので、特にオプションを指定しなければ、http://127.0.01:8545 がRemix側やメタマスクから接続するエンドポイントの値になります。
上記オプションだと、アタッチしてマイニングを開始 miner.start()、アカウントロック解除 personal.unlockAccount(eth.accounts[0]) という操作が必要になります。そのあたりもコマンドラインオプションで指定できそうでしたが、まぁとりあえず上のオプションで動いて接続できましたという一例です。
Remix
Remix側では、EnvironmentでWeb3 Providerを選び、EndpointにGethのアドレスを指定します。これで実行、デプロイ環境が切り替わります。その他は組み込み環境のときと同じように利用できます。
Gethでマイニングをしていないと処理が進まなかったり、アカウント許可をしていないとデプロイや書きこみができなかったりと、少しずつイーサリアムの概要がつかめてくる感じです。
Infra.ioなど利用してイーサリアムテストネットに接続する場合もこのエンドポイントに値を指定しWeb3 Providerを利用する形になるようです。ただ次のWeb3 Injectionでメタマスク経由の方がネットワーク切替で簡単で、私は以降はInjected Web3を使いました。
Injected Web3
Injected Web3に切り替えると、メタマスクで現在選択中のチェーンに対してRemixから操作が行えます。
メタマスクでローカルGethのネットワークを追加します。
なお、Geth上でメタマスクアドレスが存在しないので、メタマスクからprivateキーをエクスポートし、適当な名前のテキストファイルに保存して、以下のコマンドでアカウントを追加しました。
geth account import <key file>
参考 Geth – Managing Your Accounts
(keystore以下のjsonファイルをメタマスクにインポートする方法もあるみたいだけどうまくいかなかった)
またこのアカウントを miner.setEtherbase(“0x…ワレットのアドレス”) してマイニングするか、他のアカウントから送金していくらかETHを入れておきます。(ガス代がないとデプロイや書き込み処理ができないので)
なお、Remix上でInjected Web3が選択できない(選択しても何もエラーメッセージもでずにはじかれて元のProviderにもどる)現象に2回ほど出会いましたが、うまい対処方法が分かりませんでした。ブラウザのキャッシュをクリア、メタマスクも入れ直し、で復旧できましたが、何だか微妙に不安定感を感じます。
テストネット(Mumbai)にデプロイ
次にPolygonのテストネット(Munbai)にデプロイしてみました。Injected Web3を使えばメタマスクでネットワークを切り替えるだけで、他のRemixからの操作は同じです。
chainlistでMumbaiを検索してメタマスクに追加します。
ガス代が必要なので、faucetで自分のメタマスクのアドレスを入力しMATICをもらいます。
https://faucet.polygon.technology/
Munbaiの他にRopstenにもデプロイしてみましたが、特に問題なく同じ手順で行けました。faucetが止まっているところが多くここでETHを入手しました。複雑なコントラクトだと細かい違いで問題がおこるのかもしれませんが、今回の超シンプルサンプルはスムーズにデプロイできました。
また、後で知りましたが、MunbaiテストネットのRPC公開エンドポイントは込み合ってることが多いので、他のRPCを使った方がテストがやりやすいようです↓
https://docs.polygon.technology/docs/develop/network-details/network/
本番チェーン(Polygon)にデプロイ
本番環境の流れも同じでした。メタマスクでPolygonネットワークに切り替え、デプロイするだけです。
実際にMATICが消費されるので、アカウントに5000円分ぐらいのMATICを入れてからデプロイしたのですが、0.015MATIC(≒4円)しかかりませんでした。
あと、テストネットと違って少し時間がかかりました。Remix上で処理完了が自動反映されなかったのでPolygon Scanで確認して、デプロイされたコントラクトをコピペして At Adressにはって読み込んで動作を確認できます。
参考 Remixを使ってJPYCスマートコントラクト貯金箱をPolygonにデプロイする
UIの作成
次にスマートコントラクトのUIを作成しました。
ethers.jpクライアント
いろいろフレームワークがあるようですが、なるべくシンプルに行きたいということでここではethers.jsを利用しました。テキストエディタでHTMLファイルを作り、その中にJavaScriptをべた書きしてみました。
最初ぐぐったときにドキュメントも多かったweb3.jsを利用したのですが、メタマスクがセキュリティ上の理由からweb3.jsへのinjectionの対応を止めたらしく、代替案はあるようですが、何だかうまくいかなかったので、今回はethers.jsを利用しました。
参考 Metamsk – Replacing window.web3
作成したHTMLは以下です。何か多分おかしな書き方をしてる気まんまんしますが、なるべくシンプルに書いたつもりです。ソース→ mystrage.html
ちょくちょく例外が出ますが、一応動きました。
普通のHTMLからethers.jsを利用するには以下のようにします(node.jsなどでは別の書き方みたいです)。参考
<script src="https://cdn.ethers.io/lib/ethers-5.2.umd.min.js"></script>
providerを作成する箇所
const provider = new ethers.providers.Web3Provider(window.ethereum);
await provider.send("eth_requestAccounts", []);
const signer = provider.getSigner();
スマートコントラクトのアドレスとABIはRemixからコピーしてきます。
const cnt = "0x8b02EB24A5b1af257E22C520406C3dD9c1B55fd8";
abi = ...
以下がスマートコントラクト作成部分。getだけならsingerでなくproviderでも可。
myContract = new ethers.Contract(cnt, abi, signer);
web3.jsと書き方が違って、書きこみのときでもsendTransactionみたいなのにガス代指定とかせずに、ABIのメソッド呼ぶだけでいいようです。
myContract.get();
myContract.set(message);
ローカルファイルを直接開くと(file://~のURLで開くと)メタマスクが動かないので、Webサーバに配置して(http(s)://~)のURLでブラウザからアクセスする必要があります。
ここではローカルで試すときには手元にあったpythonの組み込みhtmlサーバを使いました。
python -m http.server 80
ローカルでテストした後、外部のWebサーバに配置しても動きました。
なお、メタマスク経由でなくローカルのgethにアクセスする場合は、以下のようにJsonRcpProviderでもアクセスできました。この場合Webサーバ上に置かなくても動作しました。
const provider = new ethers.providers.JsonRpcProvider();
const signer = provider.getSigner();
このhtmlファイルを外部からアクセスできるwebサーバ上に置くことで、外からブラウザでアクセスできるDApp的なものが出来上がります(でもそれをDAppと呼べるのかは不明)。このUI部分をSwarmやIPFSなどの分散ストレージ上に配置することで、UI部分も分散というか非中央集権化(decentralized)されてDApp的なものになるらしいです。
Web3.pyクライアント
web3.jsのPython版のWeb3.pyからもアクセスしてみました。web3.pyのpipインストールが必要です。またWindowsの場合はVisualC++のライブラリのインストールも必要です。
参考 https://note.com/izumisano/n/ncb32df7ab951
作ったソースはこれです→ sample1.py
エンドポイントを指定してプロバイダを作成します。
web3 = Web3(Web3.HTTPProvider(rpc_addr))
デプロイしたコントラクトを取得している部分です。なおabiはjson形式なのでremixからコピーしてpythonのソース中にそのまま貼り付けることがでいます(こういうちょっとしたサンプルなら外部のファイルに保存して読み込むより簡単。最近知った)。
strage = web3.eth.contract(address=addr, abi=abi)
MyStrage#get()を呼び出すのは簡単でこれだけ
#参照系
ret = strage.functions.get().call()
print("ret=", ret)
状態が変化するsetの方はnonceを渡して、トランザクションを~と少し処理が長いです。なおPolygonは最近EIP-1559というガス代まわりのアップデートを行ったためかガス代は別途指定しなくても標準価格?が使われるような仕組みになったようです。
nonce = web3.eth.getTransactionCount(wallet)
func = strage.functions.set("hello")
transaction = func.buildTransaction()
transaction.update({ 'nonce' : nonce })
signed_tx = web3.eth.account.sign_transaction(transaction, key)
tx_hash = web3.eth.sendRawTransaction(signed_tx.rawTransaction)
result = web3.eth.wait_for_transaction_receipt(tx_hash)
print("resut=", result)
なお、この部分は
web3.middleware_onion.inject(geth_poa_middleware, layer=0)
以下のようなエラーメッセージへの対処です。(理解してない)
The field extraData is 97 bytes, but should be 32. It is quite likely that you are connected to a POA chain
その他
Ganacheというのも試してみました。インストール後、起動しただけでアカウント10コ、自動マイニングスタートで、開発環境でGethの代わりに便利に使えそうです。Web3 Providerで接続し(デフォルトでRCPはhttp://127.0.0.1:7545)、デプロイやコントラクト呼び出しのアカウントはRemixのAccountからサクっと切り替えられます。
ローカルの開発環境はGethの代わりにGanacheを使うのが便利そうです。
ただ、現在の最新安定板のGanacheだとWeb3.pyからアクセスしたとき更新処理でエラーがでてうまく動きませんでした(ガス代パラメータをちゃんと指定すれば動く?)
Python Web3 and Ganache error “Method eth_maxPriorityFeePerGas not supported
The current stable version of Ganache (6.x) does not support EIP-1559 related functionality, so the RPC call fails. Adding the gas price argument to the transaction will make web3py send a “legacy transaction” (i.e. pre EIP-1559), so it will not try to query the priority fee and simply hard-code the gas price.
From Ganache 7.x (in beta at the time of writing), EIP-1559 transactions will be supported, so the default behavior of web3py will work too.
以上、はじめてSolidityでスマートコントラクトを書いてデプロイ、クライアントからアクセスというのをやってみました。もう少し複雑なプログラムを書く場合は便利ツールを使った方がよさそうですが、初歩の初歩だと便利ツールの使い方を覚えるのが大変そうで気がすすまない。ということでなるべく知らないツールを使わない方法でやってみました。Remix簡単便利ですね。
あとぐぐって調べたスマートコントラクトの入門記事は3年前ぐらいの記事が多く、既に最新バージョンのSolidityとは文法が違うとか、web3.jsを使った記事が多かったですが今はメタマスク連携でうまく動かないとか、情報がすぐ陳腐化するというか本当変化が速いですね。あと3年前に流行ったことを周回遅れで入門している気もしますが、まぁ競争でもないですしのんびりいきたいと思います。
コメント