Web3.py でスマートコントラクトに値を渡してみる


はじめに

前回はweb3.pyを使って簡単なスマートコントラクトの実行を行い pythonの素晴らしさを、またしても再認識した。

Web3.py でスマートコントラクトを実行してみる

今回は続きとして、前回のコードを改修し、スマートコントラクトに好きな挨拶を渡して表示できるようにしてみる。

参考サイト

公式ドキュメント

https://web3py.readthedocs.io/en/stable/contracts.html#contracts
こつこつイーサリアム
https://k2k2-ethereum.com/programming/python/273/

pythonスクリプト

今回もわかりやすいように、スマートコントラクトを作成しブロックチェーンに展開するスクリプトと、値を渡して呼び出すスクリプトを分けて作ることにした。

スマートコントラクト作成スクリプト

contract_set.py

# ライブラリの読み込み
from web3 import Web3
from solcx import compile_source

# ローカルのイーサリアム環境(Basu)に接続
w3 = Web3(Web3.HTTPProvider('http://127.0.0.1:8545'))

# アカウントと秘密鍵の設定
account_0 = w3.toChecksumAddress('0xfe3b557e8fb62b89f4916b721be55ceb828dbd73')
private_key = '0x8f2a55949038a9610f50fb23b5883af3b4ecb3c3bb792cbcefbd1542c692be63'

# Solidity のソースをコンパイル
compiled_sol = compile_source(
    '''
    pragma solidity >0.5.0;

    contract Greeter {
	string public greeting;

	constructor() public {
	    greeting = 'Hogetaro';
	}

	function setGreeting(string memory _greeting) public {
	    greeting = _greeting;
	}

	function greet() view public returns (string memory) {
	    return greeting;
	}
    }
    ''',
    output_values=['abi', 'bin']
)

# コントラクトインターフェイスの取得
contract_id, contract_interface = compiled_sol.popitem()

# print(contract_interface)

# バイトコードとABI
bytecode = contract_interface['bin']
abi = contract_interface['abi']

# ABIの表示
print(abi)

# コントラクトの作成
contract = w3.eth.contract(abi=abi, bytecode=bytecode)

# トランザクションの生成
tx = contract.constructor().buildTransaction({
    'from': account_0,
    'nonce': w3.eth.getTransactionCount(account_0),
    'gas': 1728712,
    'gasPrice': w3.toWei('21', 'gwei')})

# トランザクションに署名
signed_tx = w3.eth.account.signTransaction(tx, private_key)

# トランザクションの送信
tx_hash = w3.eth.sendRawTransaction(signed_tx.rawTransaction)

# トランザクションレシート?
tx_receipt = w3.eth.waitForTransactionReceipt(tx_hash)

# コントラクトアドレスの表示
print(tx_receipt.contractAddress)

中身はほとんど前回と同じで、setGreeting という関数を追加しただけである。

これを実行すると、ABIとコントラクトアドレスが表示される。

$ python3 contract_set.py 
[{'inputs': [], 'stateMutability': 'nonpayable', 'type': 'constructor'}, {'inputs': [], 'name': 'greet', 'outputs': [{'internalType': 'string', 'name': '', 'type': 'string'}], 'stateMutability': 'view', 'type': 'function'}, {'inputs': [], 'name': 'greeting', 'outputs': [{'internalType': 'string', 'name': '', 'type': 'string'}], 'stateMutability': 'view', 'type': 'function'}, {'inputs': [{'internalType': 'string', 'name': '_greeting', 'type': 'string'}], 'name': 'setGreeting', 'outputs': [], 'stateMutability': 'nonpayable', 'type': 'function'}]
0xfeae27388A65eE984F452f86efFEd42AaBD438FD

スマートコントラクトへの値の設定および呼び出しスクリプト

set_call.py

さきほど表示されたABIとコントラクトアドレスを使って、値を渡したあとに呼び出すスクリプトを作成。

今回は新たなトランザクションを生成、実行する必要があるため前回より複雑になっている。

# ライブラリのインポート
from web3 import Web3

# 接続
w3 = Web3(Web3.HTTPProvider('http://127.0.0.1:8545'))

account_0 = w3.toChecksumAddress('0xfe3b557e8fb62b89f4916b721be55ceb828dbd73')
private_key = '0x8f2a55949038a9610f50fb23b5883af3b4ecb3c3bb792cbcefbd1542c692be63'

# ABI
abi = [{'inputs': [], 'stateMutability': 'nonpayable', 'type': 'constructor'}, {'inputs': [], 'name': 'greet', 'outputs': [{'internalType': 'string', 'name': '', 'type': 'string'}], 'stateMutability': 'view', 'type': 'function'}, {'inputs': [], 'name': 'greeting', 'outputs': [{'internalType': 'string', 'name': '', 'type': 'string'}], 'stateMutability': 'view', 'type': 'function'}, {'inputs': [{'internalType': 'string', 'name': '_greeting', 'type': 'string'}], 'name': 'setGreeting', 'outputs': [], 'stateMutability': 'nonpayable', 'type': 'function'}]

# コントラクトアドレス
contractAddress = '0xfeae27388A65eE984F452f86efFEd42AaBD438FD'

# コントラクトオブジェクト
contract = w3.eth.contract(address=contractAddress, abi=abi)

# 呼び出しと表示

tx = contract.functions.setGreeting('KONNICHIWA!!!!!').buildTransaction({'nonce': w3.eth.getTransactionCount(account_0)})

signed_tx = w3.eth.account.signTransaction(tx, private_key)

# トランザクションの送信
tx_hash = w3.eth.sendRawTransaction(signed_tx.rawTransaction)

tx_receipt = w3.eth.waitForTransactionReceipt(tx_hash)

print(contract.functions.greet().call())

これを実行すると、元のコントラクトの挨拶を上書きして、設定した挨拶が返される。

$ python3 set_call.py 
KONNICHIWA!!!!!

素晴らしい!さすが python!!

挨拶をハードコーディングしてしまってるのを引数で渡すようにすれば、もっと前回との違いがわかりやすい気がするが、とりあえずこれで満足しました。