はじめに
Ethereumを現在学習中のこつこつイーサリアムです。イーサリアムをPythonで学習しようと思い、「Ethereum python」で検索をかけたところ、Ethereum 公式のサイト「Python開発者のためのイーサリアム」の「Intro to Web3.py · Ethereum For Python Developers」が見つかりました。
実践してみたところとても勉強になったので、勉強記録も兼ねて&ところどころ追記しながら、記事化しておこうと思います。すべてを1本の記事にすると長くなるので、3本立てとしました。
本記事ではまず、「Web3.pyとは」「アカウント残高の取得」「スマートコントラクト(ERC-20 トークン)情報の取得」まで、実施してみます!
Web3.pyのインストール、事前準備
Web3.pyとは、イーサリアム財団によって維持・管理されている、イーサリアムブロックチェーンを読み込み・書き込みするためのPythonライブラリです。
やりとりはJSON RPCという形式(PythonやJavascriptでよく使われるデータ形式)で行われるため、直感的にコマンドを作成・理解しやすいですね。
公式ドキュメントは以下↓からどうぞ。
Web3.pyのインストール
pythonをインストールしていない場合は事前にpython(バージョン3以上)をインストールしておきます。なお、Web3.pyのインストールは以下のコマンドを打つだけです。
$ pip install web3
ただし、インストール時に「Microsoft C++ Build Tools」がインストールされていない旨表示される場合には、以下の記事を参考にしてインストールします。
その後にWeb3.pyをインストールすれば問題なく完了するはずです。
infra.io
イーサリアムメインネットにアクセスするためには、その構成ノードへアクセスする必要があります。ノードを作成する方法(Geth/Parity)もありますが、ブロックチェーンへの大量のファイルダウンロード等手間がかかるので、別の方法で実施します。
別の方法とは、イーサリアムノードへのAPIアクセスを無料で提供しているサービスを利用することです。具体的には、「Infra」というサービスがあるので、サインアップし、APIキーを発行すればアクセスできるようになります。
Infraでサインアップをして新しいプロジェクトを作成すると、以下のようなURLが発行されるので、コピーしておきましょう。
"https://mainnet.infra.io/v3/Your_Infra_API_Key"
アカウント残高の取得
準備ができたので、いよいよWeb3.pyを使っていきましょう。
# 1. Import module
import json
from web3 import Web3
# 2. Set web3 module
infura_url = "https://mainnet.infura.io/v3/Your-Infra-API-Key"
web3 = Web3(Web3.HTTPProvider(infura_url))
# 3. Print Connection check
print(web3.isConnected())
# 4. Get Block Number
print(web3.eth.blockNumber)
# 5. Print account balances
account = "0xd26114cd6EE289AccF82350c8d8487fedB8A0C07"
balance = web3.eth.getBalance(account)
print(web3.fromWei(balance, "ether"))
1.でモジュールの読み込みを行っています。2.でWeb3モジュールによるアクセス設定(先ほど取得したInfraのURLを記載ください)、3.でコネクションの確認、4.でブロック番号の確認、5.でアカウント残高(アドレスはサンプル用で何も入っていません)を取得しています。
上記のファイルを「app01.py」等とし、「python app01.py」とコマンドを打つと、内容が実行されるはずです。実行結果のサンプルは以下の通りです。
True
13171300
0
これで、pythonを使ってEthereumネットワークにアクセスする方法がわかりましたね!実際に自分のウォレットのアドレスの残高等も、上記のプログラムでアドレスを変更すれば取得できますので、お試しくださいませ。
スマートコントラクト(ERC-20 トークン) 情報の取得
次はスマートコントラクト情報の取得です。スマートコントラクトの情報については、「ABI (Application Binary Interface)」で定義された情報でやりとりされます。今回使用するABIの中身については、トークンの定義が記載されているもの、とでも解釈しておけばよいのかなと思います。では、テストスクリプトを見てみます。
# 1. Import Module
import json
from web3 import Web3
# 2. Fill in your infura API key here
infura_url = "https://mainnet.infura.io/v3/Your-Infra-API-Key"
web3 = Web3(Web3.HTTPProvider(infura_url))
# 3. Set ABI, Address
abi = [{"constant":True,"inputs":[],"name":"mintingFinished","outputs":[{"name":"","type":"bool"}],"payable":False,"type":"function"},{"constant":True,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":False,"type":"function"},{"constant":False,"inputs":[{"name":"_spender","type":"address"},{"name":"_value","type":"uint256"}],"name":"approve","outputs":[],"payable":False,"type":"function"},{"constant":True,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":False,"type":"function"},{"constant":False,"inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transferFrom","outputs":[],"payable":False,"type":"function"},{"constant":True,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint256"}],"payable":False,"type":"function"},{"constant":False,"inputs":[],"name":"unpause","outputs":[{"name":"","type":"bool"}],"payable":False,"type":"function"},{"constant":False,"inputs":[{"name":"_to","type":"address"},{"name":"_amount","type":"uint256"}],"name":"mint","outputs":[{"name":"","type":"bool"}],"payable":False,"type":"function"},{"constant":True,"inputs":[],"name":"paused","outputs":[{"name":"","type":"bool"}],"payable":False,"type":"function"},{"constant":True,"inputs":[{"name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"balance","type":"uint256"}],"payable":False,"type":"function"},{"constant":False,"inputs":[],"name":"finishMinting","outputs":[{"name":"","type":"bool"}],"payable":False,"type":"function"},{"constant":False,"inputs":[],"name":"pause","outputs":[{"name":"","type":"bool"}],"payable":False,"type":"function"},{"constant":True,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":False,"type":"function"},{"constant":True,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":False,"type":"function"},{"constant":False,"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transfer","outputs":[],"payable":False,"type":"function"},{"constant":False,"inputs":[{"name":"_to","type":"address"},{"name":"_amount","type":"uint256"},{"name":"_releaseTime","type":"uint256"}],"name":"mintTimelocked","outputs":[{"name":"","type":"address"}],"payable":False,"type":"function"},{"constant":True,"inputs":[{"name":"_owner","type":"address"},{"name":"_spender","type":"address"}],"name":"allowance","outputs":[{"name":"remaining","type":"uint256"}],"payable":False,"type":"function"},{"constant":False,"inputs":[{"name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":False,"type":"function"},{"anonymous":False,"inputs":[{"indexed":True,"name":"to","type":"address"},{"indexed":False,"name":"value","type":"uint256"}],"name":"Mint","type":"event"},{"anonymous":False,"inputs":[],"name":"MintFinished","type":"event"},{"anonymous":False,"inputs":[],"name":"Pause","type":"event"},{"anonymous":False,"inputs":[],"name":"Unpause","type":"event"},{"anonymous":False,"inputs":[{"indexed":True,"name":"owner","type":"address"},{"indexed":True,"name":"spender","type":"address"},{"indexed":False,"name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":False,"inputs":[{"indexed":True,"name":"from","type":"address"},{"indexed":True,"name":"to","type":"address"},{"indexed":False,"name":"value","type":"uint256"}],"name":"Transfer","type":"event"}]
address = "0xd26114cd6EE289AccF82350c8d8487fedB8A0C07"
# 4. Access contract
contract = web3.eth.contract(address=address, abi=abi)
# 5. Get contract information
# 5.1. Contract name, symbol
print(contract.functions.name().call())
print(contract.functions.symbol().call())
# 5.2. Total Supply
totalSupply = contract.functions.totalSupply().call()
print(web3.fromWei(totalSupply, 'ether'))
# 5.3. Balance
balance = contract.functions.balanceOf(address).call()
print(web3.fromWei(balance, 'ether'))
2.まではこれまでと同じですね。3.では、ABI(今回はERC-20トークン(OmiseGo)のスマートコントラクト)を定義しています。4.でコントラクトのモジュールにアクセスし、5.で情報を取得しています。
5.1.ではコントラクト名とシンボル、5.2.では総供給量、5.4.ではトークン残高を取得しています。実行結果のサンプルは下記のとおりです。
OMGToken
OMG
140245398.245132780789239631
26624.6403596473000522
このように、ABIを使用することで、任意のトークンやスマートコントラクトの情報にアクセスすることが可能となります。
おわりに
PythonのWeb3.pyを使用して、アカウント残高やスマートコントラクト情報を取得する方法をご紹介しました。
いままでEtherscanやウェブサイトを通してしか得られなかった情報が、このようにコマンドラインでデータを取得できるので、より実感が湧きやすく、データ利用の幅が広まったかなと思います。お役に立てば幸いです。
次回記事では、テスト環境を用いたイーサリアム送金についてご紹介します。