web3.pyとpy-solc-xでの独自SmartContractのDeploy
はじめに
@yutakikuchi_です
web3.pyを利用したpolygon networkに接続する例を下記のnoteに書きましたが、web3.pyに追加してsolcx、ETHのEthereumTesterProviderを利用したSmartContractのDeploy実装をメモしておきます。
py-solc-xとは
solc自体はSolidity compilerであり、py-solc-xはpythonのwrapperとなります。使い方は上のURL、もしくはweb3.pyのreadthedocsページを見てもらうのが良いかと思います。
py-solc-xによるSmartContract
繰り返しになりますが、EthereumTesterProviderを利用してのSmartContractを記載します。まずはpy-solc-xのpip installです。また、サンプルページに書かれているEthereumTesterProviderを利用のためのmoduleである、eth_tester、py-evmもあわせてinstallします。
尚、2022年02月現在のsolcの最新versionは0.8.11のようで、solcをnpmでinstallします。https://docs.soliditylang.org/en/v0.8.11/
$ python -V (git)-[main]
Python 3.7.4
$ pip install py-solc-x
...
Successfully installed py-solc-x-1.1.1 semantic-version-2.8.5
$ pip install eth_tester py-evm
$ npm install -g solc@0.8.11
Deployのサンプルスクリプト
まずはPythonファイルです。この例にどれほどの意味があるかは、僕自身も良くわからないところではありますが、solファイルの中のcontract StoreVarで定義されているsetVarに255という値をセットして、それをContractとしたtransactionをtoAddressに対して実行しているようです。また gas_estimate = store_var_contract.functions.setVar(255).estimateGas() でガス代を事前に見積をしているようで、gas_estimate < 100000の場合にtransactionを送って、その結果を出力しています。
import sys
import time
import pprint
from web3.providers.eth_tester import EthereumTesterProvider
from web3 import Web3
from eth_tester import PyEVMBackend
from solcx import compile_source
def compile_source_file(file_path):
with open(file_path, 'r') as f:
source = f.read()
compiled_sol = compile_source(source,
output_values=["abi", "bin"],
solc_version="0.8.11")
return compiled_sol
def deploy_contract(w3, contract_interface):
tx_hash = w3.eth.contract(
abi=contract_interface['abi'],
bytecode=contract_interface['bin']).constructor().transact()
address = w3.eth.get_transaction_receipt(tx_hash)['contractAddress']
return address
if __name__ == '__main__':
w3 = Web3(EthereumTesterProvider(PyEVMBackend()))
contract_source_path = 'contract.sol'
compiled_sol = compile_source_file('contract.sol')
contract_id, contract_interface = compiled_sol.popitem()
address = deploy_contract(w3, contract_interface)
print(f'Deployed {contract_id} to: {address}\n')
store_var_contract = w3.eth.contract(address=address, abi=contract_interface["abi"])
gas_estimate = store_var_contract.functions.setVar(255).estimateGas()
print(f'Gas estimate to transact with setVar: {gas_estimate}')
if gas_estimate < 100000:
print("Sending transaction to setVar(255)\n")
tx_hash = store_var_contract.functions.setVar(255).transact()
receipt = w3.eth.wait_for_transaction_receipt(tx_hash)
print("Transaction receipt mined:")
pprint.pprint(dict(receipt))
print("\nWas transaction successful?")
pprint.pprint(receipt["status"])
else:
print("Gas cost exceeds 100000")
次にSolidityファイルです。下記ページに記載されているようにSolidityをcontract.solファイルへ記載し、元のpythonからはfile readしてからcompileすると良いと思います。
https://web3py.readthedocs.io/en/stable/examples.html#working-with-contracts
https://web3py.readthedocs.io/en/latest/contracts.html
https://solcx.readthedocs.io/en/stable/using-the-compiler.html
contract StoreVar {
uint8 public _myVar;
event MyEvent(uint indexed _var);
function setVar(uint8 _var) public {
_myVar = _var;
emit MyEvent(_var);
}
function getVar() public view returns (uint8) {
return _myVar;
}
}
上記pythonファイルを実行すると、下記のような出力結果が得られます。new contractsによるDeployは下記を参考にしています。https://github.com/ethereum/web3.py/blob/master/docs/examples.rst#deploying-new-contracts
Deployed <stdin>:StoreVar to: 0xF2E246BB76DF876Cef8b38ae84130F4F55De395b
Gas estimate to transact with setVar: 50480
Sending transaction to setVar(255)
Transaction receipt mined:
{'blockHash': HexBytes('0xa0938f23145fa9c6a9f273d8160bd83c2ad1527520c57d9f65ddfdd6f1086769'),
'blockNumber': 2,
'contractAddress': None,
'cumulativeGasUsed': 44966,
'effectiveGasPrice': 1000000000,
'from': '0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf',
'gasUsed': 44966,
'logs': [AttributeDict({'type': 'mined', 'logIndex': 0, 'transactionIndex': 0, 'transactionHash': HexBytes('0x1bb6e9660c46d5b40247ca1ce7ea85bbba93c56dfd19380c0cc3433abc33af4d'), 'blockHash': HexBytes('0xa0938f23145fa9c6a9f273d8160bd83c2ad1527520c57d9f65ddfdd6f1086769'), 'blockNumber': 2, 'address': '0xF2E246BB76DF876Cef8b38ae84130F4F55De395b', 'data': '0x', 'topics': [HexBytes('0x6c2b4666ba8da5a95717621d879a77de725f3d816709b9cbe9f059b8f875e284'), HexBytes('0x00000000000000000000000000000000000000000000000000000000000000ff')]})],
'state_root': b'\x01',
'status': 1,
'to': '0xF2E246BB76DF876Cef8b38ae84130F4F55De395b',
'transactionHash': HexBytes('0x1bb6e9660c46d5b40247ca1ce7ea85bbba93c56dfd19380c0cc3433abc33af4d'),
'transactionIndex': 0,
'type': '0x2'}
Was transaction successful?
1